From fa4467f4abeb04ed5638378d5953de2cd922abe8 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Wed, 15 Dec 2021 20:46:41 -0300 Subject: [PATCH] Import bios_extract fork source --- .gitignore | 12 + README.md | 3 +- bios_extract/.gitignore | 8 + bios_extract/.gitreview | 5 + bios_extract/COPYING | 294 ++++ bios_extract/Makefile | 55 + bios_extract/README.md | 13 + bios_extract/change-mcp55-mac.pl | 44 + bios_extract/csmcoreparse.py | 34 + bios_extract/decap.sh | 40 + bios_extract/dell_inspiron_1100_unpacker.py | 121 ++ bios_extract/hp_6715b_nc6320_unpacker.py | 66 + bios_extract/insyde-tools/dumpsetup.py | 353 +++++ bios_extract/insyde-tools/fsdump.py | 177 +++ bios_extract/insyde-tools/lzma.py | 230 +++ bios_extract/insyde-tools/main.py | 473 +++++++ .../specs/insyde-module-header-format.txt | 75 + bios_extract/insyde-tools/util.py | 168 +++ bios_extract/microcode_extract.py | 126 ++ bios_extract/phoenix_extract.py | 857 ++++++++++++ bios_extract/src/ami.c | 621 ++++++++ bios_extract/src/ami_slab.c | 195 +++ bios_extract/src/award.c | 73 + bios_extract/src/bcpvpd.c | 97 ++ bios_extract/src/bios_extract.c | 226 +++ bios_extract/src/bios_extract.h | 60 + bios_extract/src/compat.c | 63 + bios_extract/src/compat.h | 48 + bios_extract/src/lh5_extract.c | 553 ++++++++ bios_extract/src/lh5_extract.h | 36 + bios_extract/src/lh5_test.c | 137 ++ bios_extract/src/lzari_extract.c | 263 ++++ bios_extract/src/lzhuf_extract.c | 394 ++++++ bios_extract/src/lzss_extract.c | 92 ++ bios_extract/src/lzss_extract.h | 24 + bios_extract/src/phoenix.c | 1244 +++++++++++++++++ bios_extract/src/systemsoft.c | 232 +++ bios_extract/util/gitconfig/commit-msg | 173 +++ bios_extract/xfv/Decompress.c | 1041 ++++++++++++++ bios_extract/xfv/README.txt | 33 + bios_extract/xfv/efidecomp.c | 107 ++ bios_extract/xfv/efihack.h | 24 + bios_extract/xfv/xfv.py | 362 +++++ 43 files changed, 9251 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 bios_extract/.gitignore create mode 100644 bios_extract/.gitreview create mode 100644 bios_extract/COPYING create mode 100644 bios_extract/Makefile create mode 100644 bios_extract/README.md create mode 100644 bios_extract/change-mcp55-mac.pl create mode 100644 bios_extract/csmcoreparse.py create mode 100644 bios_extract/decap.sh create mode 100644 bios_extract/dell_inspiron_1100_unpacker.py create mode 100644 bios_extract/hp_6715b_nc6320_unpacker.py create mode 100644 bios_extract/insyde-tools/dumpsetup.py create mode 100644 bios_extract/insyde-tools/fsdump.py create mode 100644 bios_extract/insyde-tools/lzma.py create mode 100644 bios_extract/insyde-tools/main.py create mode 100644 bios_extract/insyde-tools/specs/insyde-module-header-format.txt create mode 100644 bios_extract/insyde-tools/util.py create mode 100644 bios_extract/microcode_extract.py create mode 100644 bios_extract/phoenix_extract.py create mode 100644 bios_extract/src/ami.c create mode 100644 bios_extract/src/ami_slab.c create mode 100644 bios_extract/src/award.c create mode 100644 bios_extract/src/bcpvpd.c create mode 100644 bios_extract/src/bios_extract.c create mode 100644 bios_extract/src/bios_extract.h create mode 100644 bios_extract/src/compat.c create mode 100644 bios_extract/src/compat.h create mode 100644 bios_extract/src/lh5_extract.c create mode 100644 bios_extract/src/lh5_extract.h create mode 100644 bios_extract/src/lh5_test.c create mode 100644 bios_extract/src/lzari_extract.c create mode 100644 bios_extract/src/lzhuf_extract.c create mode 100644 bios_extract/src/lzss_extract.c create mode 100644 bios_extract/src/lzss_extract.h create mode 100644 bios_extract/src/phoenix.c create mode 100644 bios_extract/src/systemsoft.c create mode 100644 bios_extract/util/gitconfig/commit-msg create mode 100644 bios_extract/xfv/Decompress.c create mode 100644 bios_extract/xfv/README.txt create mode 100644 bios_extract/xfv/efidecomp.c create mode 100644 bios_extract/xfv/efihack.h create mode 100644 bios_extract/xfv/xfv.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a587ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Python code +__pycache__ +*.pyc + +# bios_extract +bios_extract/src/bios_extract +lh5_test +ami_slab +bcpvpd +efidecomp +*.o +*~ diff --git a/README.md b/README.md index ed7c490..096b2de 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ 86Box BIOS Tools ================ -A full toolkit for analyzing and extracting x86 BIOS ROM images (mostly) within the context of the 86Box project. +A toolkit for analyzing and extracting x86 BIOS ROM images (mostly) within the context of the 86Box project. ## System requirements @@ -16,6 +16,7 @@ A full toolkit for analyzing and extracting x86 BIOS ROM images (mostly) within ``` cd bios_extract make +cd .. ``` 3. Download the `uefiextract` tool from its [GitHub repository](https://github.com/LongSoft/UEFITool/releases) and place its executable on the repository's root directory. Prebuilt versions are only available for `x86_64`, but this tool is optional; UEFI extraction will not work without it. diff --git a/bios_extract/.gitignore b/bios_extract/.gitignore new file mode 100644 index 0000000..42ce32b --- /dev/null +++ b/bios_extract/.gitignore @@ -0,0 +1,8 @@ +bios_extract +lh5_test +ami_slab +bcpvpd +efidecomp +*.o +*~ +*.pyc diff --git a/bios_extract/.gitreview b/bios_extract/.gitreview new file mode 100644 index 0000000..185d111 --- /dev/null +++ b/bios_extract/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=review.coreboot.org +port=29418 +project=bios_extract +defaultbranch=master diff --git a/bios_extract/COPYING b/bios_extract/COPYING new file mode 100644 index 0000000..b5aad66 --- /dev/null +++ b/bios_extract/COPYING @@ -0,0 +1,294 @@ +Unless a COPYING file in a subdirectory or file-specific license headers +specify a different license, the following applies to all files in this +directory and all subdirectories. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License below for more details. + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/bios_extract/Makefile b/bios_extract/Makefile new file mode 100644 index 0000000..a8a370a --- /dev/null +++ b/bios_extract/Makefile @@ -0,0 +1,55 @@ +MAKE = make +CFLAGS ?= -g -fpack-struct -Wall -O0 +CC ?= gcc + +all: bios_extract bcpvpd ami_slab xfv + +SRCDIR = src + +BIOS_EXTRACT_OBJS = $(SRCDIR)/lh5_extract.o $(SRCDIR)/lzari_extract.o \ + $(SRCDIR)/lzhuf_extract.o $(SRCDIR)/lzss_extract.o \ + $(SRCDIR)/ami.o $(SRCDIR)/award.o \ + $(SRCDIR)/phoenix.o $(SRCDIR)/systemsoft.o \ + $(SRCDIR)/bios_extract.o $(SRCDIR)/compat.o +bios_extract: $(BIOS_EXTRACT_OBJS) + $(CC) $(CFLAGS) $(BIOS_EXTRACT_OBJS) -o bios_extract + +BCPVPD_OBJS = $(SRCDIR)/lzss_extract.o $(SRCDIR)/bcpvpd.o +bcpvpd: $(BCPVPD_OBJS) + $(CC) $(CFLAGS) $(BCPVPD_OBJS) -o bcpvpd + +AMISLAB_OBJS = $(SRCDIR)/ami_slab.o +ami_slab: $(AMISLAB_OBJS) + $(CC) $(CFLAGS) $(AMISLAB_OBJS) -o ami_slab + +XFV_OBJS = xfv/Decompress.o xfv/efidecomp.o +xfv: $(XFV_OBJS) + $(CC) -I xfv/ $(CFLAGS) -o xfv/efidecomp $(XFV_OBJS) + +# just here to easily verify the functionality of the lh5 routine +LH5_TEST_OBJS = $(SRCDIR)/lh5_extract.o $(SRCDIR)/lh5_test.o +lh5_test: $(LH5_TEST_OBJS) + $(CC) $(CFLAGS) $(LH5_TEST_OBJS) -o lh5_test + +gitconfig: + [ -d .git ] + mkdir -p .git/hooks + for hook in commit-msg pre-commit ; do \ + if [ util/gitconfig/$$hook -nt .git/hooks/$$hook -o \ + ! -x .git/hooks/$$hook ]; then \ + sed -e "s,%MAKE%,$(MAKE),g" util/gitconfig/$$hook > .git/hooks/$$hook; \ + chmod +x .git/hooks/$$hook; \ + fi; \ + done + git config remote.origin.push HEAD:refs/for/master + (git config --global --includes user.name >/dev/null && git config --global --includes user.email >/dev/null) || (printf 'Please configure your name and email in git:\n\n git config --global user.name "Your Name Comes Here"\n git config --global user.email your.email@example.com\n'; exit 1) + +clean: + rm -f $(SRCDIR)/*.o + rm -f bios_extract + rm -f bcpvpd + rm -f lh5_test + rm -f ami_slab + rm -f xfv/efidecomp xfv/*.o + +.PHONY: all bios_extract bcpvpd ami_slab efidecomp lh5_test clean gitconfig diff --git a/bios_extract/README.md b/bios_extract/README.md new file mode 100644 index 0000000..150d437 --- /dev/null +++ b/bios_extract/README.md @@ -0,0 +1,13 @@ +bios_extract +============ +Fork of the [coreboot bios_extract tool](https://github.com/coreboot/bios_extract) modified for our needs. + +## Modifications + +* Added some sanity checks +* Improved AMI, Award and Phoenix BIOS detection +* Improved AMIBIOS extraction +* Added AMIBIOS WinBIOS (12/15/93), 4 (07/25/94) and 5 (10/10/94) extraction +* Added LH5 extraction bruteforcing for Intel AMI Color fork +* Improved Phoenix extraction based on PHOEDECO +* Implemented SystemSoft extraction based on SYSODECO diff --git a/bios_extract/change-mcp55-mac.pl b/bios_extract/change-mcp55-mac.pl new file mode 100644 index 0000000..dfa151b --- /dev/null +++ b/bios_extract/change-mcp55-mac.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl +# +# Update the onboard NIC mac address in a coreboot rom image for mcp55-based boards +# +# This program is free software (GPLv2 or higher). +# +# 2008-01-30 Ward Vandewege (ward@gnu.org) + +my $mac = $ARGV[0]; +my $file = $ARGV[1]; + +if (($mac eq '') or ($file eq '')) { + print "\nSyntax: $0 \n"; + exit 1; +} + +if (! -f $file) { + print "\nSyntax: $0 \n"; + print "\nERROR: Could not find file '$file'.\n\n"; + exit 1; +} + +if (!($mac =~ /^[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}$/)) { + print "\nSyntax: $0 \n"; + print "\nERROR: The mac address you specified ($mac) is not a valid mac address.\n\n"; + exit 1; +} + +my @mac = split(/:/,$mac); + +my $newmac = ''; + +for (my $c = 5; $c >= 0; $c--) { + $newmac .= chr(hex($mac[$c])); +} + +open(ROMIMAGE,"+<",$file) or die "Can't open file $file for writing\n"; +seek(ROMIMAGE,-48,2); + +print ROMIMAGE $newmac; +close(ROMIMAGE); + +print "Mac address succesfully updated to $mac in $file\n"; +exit 0; diff --git a/bios_extract/csmcoreparse.py b/bios_extract/csmcoreparse.py new file mode 100644 index 0000000..47f37f2 --- /dev/null +++ b/bios_extract/csmcoreparse.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# +# parse CSMCORE.raw from AMI UEFI +# 3-clause BSD license +# roxfan@skynet.be + +import struct, sys +if len(sys.argv) < 2: + fn = "CSMCORE.raw" +else: + fn = sys.argv[1] +f = open(fn, "rb") +while True: + print "%08X"%f.tell(), + hdr = f.read(10) + if len(hdr) == 0: + break + typ, vid, did, size = struct.unpack(" %s" % fname + if size == 0xFFFFFFFF: + d = f.read() + else: + d = f.read(size) + open(fname, "wb").write(d) diff --git a/bios_extract/decap.sh b/bios_extract/decap.sh new file mode 100644 index 0000000..4040337 --- /dev/null +++ b/bios_extract/decap.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# Takes a path to a file as first and only argument, removes the first 2048 B +# from that file and stores the result with the same name plus a '.bin' suffix. +# Useable to remove the header from UEFI Capsule files to use the resulting +# binary with flashrom. + +main () { + if [ "$#" -lt 1 -o ! -r "$1" ]; then + echo "Removes the 2048 B header of UEFI Capsule files.\n"\ + "Usage: $0 " + return 1 + fi + + capsize=$(wc -c "$1" | cut -f 1 -d ' ') + binsize=$(($capsize-2048)) + ispowoftwo=$(($binsize & ($binsize-1))) + if [ $ispowoftwo -ne 0 -o $binsize -eq 0 ]; then + echo "The size of the resulting file would not be a power of 2 (but $binsize B)." + return 1 + fi + + dd bs=2048 skip=1 if="$1" of="$1.bin" +} + +main $* diff --git a/bios_extract/dell_inspiron_1100_unpacker.py b/bios_extract/dell_inspiron_1100_unpacker.py new file mode 100644 index 0000000..3d5adf5 --- /dev/null +++ b/bios_extract/dell_inspiron_1100_unpacker.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python2 +# Dell/Phoenix ROM BIOS PLUS unpacker +# 2012-09-12 version 0.1 +# 2012-10-10 version 0.2 added support for older BIOSes with 16-bit length (Dell Inspiron 1100) +# 3-clause BSD license +# roxfan@skynet.be + +import array +import struct +import sys + +def memcpy(arr1, off1, arr2, off2, count): + while count: + if off1 < len(arr1): + arr1[off1] = arr2[off2] + elif off1 == len(arr1): + arr1.append(arr2[off2]) + else: + raise Exception("Trying to write out of bounds") + off1 += 1 + off2 += 1 + count -=1 + +# looks like some lzss variation +def dell_unpack(indata): + srcoff = 0 + dstoff = 0 + src = array.array('B', indata) + dst = array.array('B') + inlen = len(indata) + while srcoff < inlen: + b = src[srcoff] + nibl, nibh = b & 0x0F, (b >> 4) & 0x0F + srcoff += 1 + if nibl: + if nibl == 0xF: + al = src[srcoff] + ah = src[srcoff+1] + srcoff += 2 + cx = nibh | (ah << 4) + count = (cx & 0x3F) + 2 + delta = ((ah >> 2) << 8) | al + else: + count = nibl + 1 + delta = (nibh << 8) | src[srcoff] + srcoff += 1 + memcpy(dst, dstoff, dst, dstoff - delta - 1, count) + dstoff += count + elif nibh == 0x0E: + count = src[srcoff] + 1 + srcoff += 1 + memcpy(dst, dstoff, dst, dstoff - 1, count) + dstoff += count + else: + if nibh == 0x0F: + count = src[srcoff] + 15 + srcoff += 1 + else: + count = nibh + 1 + memcpy(dst, dstoff, src, srcoff, count) + dstoff += count + srcoff += count + + return dst.tostring() + +mod_types = { + 0x01: "Main ROM", + 0x0C: "Microcode update", +} + +print "Dell/Phoenix ROM BIOS PLUS unpacker" +if len(sys.argv) < 2: + print "Usage: dell_unpack.py bios.bin [offset]" + sys.exit(1) +fname = sys.argv[1] +offs = 0 +f = open(fname, "rb").read() +if len(sys.argv) > 2: + offs = int(sys.argv[2], 16) +else: + offs = f.find("\xF0\x00Copyright 1985-\x02\x04\xF0\x0F8 Phoenix Technologies Ltd.") + if offs == -1: + print "Does not look like a Dell/Phoenix ROM BIOS PLUS" + sys.exit(2) + if f[offs-5] == '\x01': + hlen = 5 # 32-bit length + offs -= 5 + fmt = " 0: + fn = "EC.bin" + print "%08X EC code, %08X %s" % (0, offs, fn) + open(fn, "wb").write(f[:offs]) +while True: + type, leng = struct.unpack(fmt, f[offs:offs+hlen]) + print "%08X type %02X" % (offs, type), + offs += hlen + if type == 0xFF: + print "" + break + data = f[offs:offs+leng] + offs += leng + if type != 0xC: + odata = dell_unpack(data) + else: + odata = data + print " %08X -> %08X" % (leng, len(odata)), + fn = "mod_%02X.bin" % type + print " %s" % fn, + if type in mod_types: + print "(%s)" % mod_types[type] + else: + print "" + open(fn, "wb").write(odata) diff --git a/bios_extract/hp_6715b_nc6320_unpacker.py b/bios_extract/hp_6715b_nc6320_unpacker.py new file mode 100644 index 0000000..caf0b85 --- /dev/null +++ b/bios_extract/hp_6715b_nc6320_unpacker.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python2 +# HP Compaq 6715b ROM unpacker by roxfan +# compressed data begins like this: +# 0000010000: 01 00 14 01 70 00 00 00 | 60 00 00 00 00 00 02 00 O .Op . O +# 0000010010: 50 4F 53 54 FF B8 00 20 | 8E D8 66 B8 10 FF 20 00 POST.. ..f... + +import struct, array, sys + +def unpack1(cdata, ulen): + pos = 0 + odata = "" + while ulen: + a = ord(cdata[pos]) + # print "%x: %x" % (pos, a) + pos += 1 + if a == 0xFF: + odata += cdata[pos:pos+8] + ulen -= 8 + pos += 8 + else: + mask = a | 0x100 + while mask and ulen: + # print hex(mask), hex(pos) + b = mask & 1 + mask >>= 1 + if mask == 0: + break + if b: + odata += cdata[pos] + pos += 1 + ulen -= 1 + else: + delta = ord(cdata[pos]) + pos += 1 + delta |= ord(cdata[pos])<<8 + pos += 1 + count = (delta & 0xF) + 3 + delta >>= 4 + # print "d: %d, c: %d" % (delta, count) + opos = len(odata)-delta + while count: + odata += odata[opos] + opos += 1 + count -= 1 + ulen -= 1 + return odata + +f = open(sys.argv[1], "rb") +f.seek(0x10000) + +while True: + flags, ulen, clen, dest = struct.unpack(" 1: + break + hdrlen = (flags>>16) & 0xFF + unk = (flags>>24) & 0xFF + print "comp: %d, hdr len: 0x%X, unk: %d, ulen: 0x%X, clen: 0x%X, dest: 0x%X" % (comp, hdrlen, unk, ulen, clen, +dest) + extra = f.read(hdrlen-0x10).rstrip('\0') + print " %s" % extra + cdata = f.read(clen) + fname = "%04X_%s.bin" % (dest>>4, extra) + if comp == 1: + cdata = unpack1(cdata, ulen) + open(fname, "wb").write(cdata) diff --git a/bios_extract/insyde-tools/dumpsetup.py b/bios_extract/insyde-tools/dumpsetup.py new file mode 100644 index 0000000..98a9203 --- /dev/null +++ b/bios_extract/insyde-tools/dumpsetup.py @@ -0,0 +1,353 @@ +#!/usr/bin/python + +import sys +import struct +import codecs + +sys.stdout = codecs.getwriter('utf-8')(sys.stdout) + +STRING_TABLE = 0x6F80 # FaithX +STRING_TABLE = 0x10b60 # Original +#STRING_TABLE = 0x108e0 + 12 # No idea +STRING_TABLE = 0x10b30 # Mine? + +storage_map = {} + +FORMS = [ + ("Exit", 0xC600), + #('Exit', 0xc640), + #('Boot', 0xc6f0), + #('Power', 0xc810), + #('Security',0xd140), + #('Advanced',0xd390), + #('Main', 0x106b0), + #('OEM', 0x10a20) +] + + +def fguid(s): + a, b, c, d = struct.unpack("] == 0x%x" % (qid, width, val) + elif self.opcode == self.EFI_IFR_EQ_ID_ID_OP: + qid, width, qid2, width2, val = struct.unpack( + "] == [0x%x.%d]" % ( + qid, width, qid2, width2, val) + elif self.opcode == self.EFI_IFR_EQ_ID_LIST_OP: + qid, width, length = struct.unpack("] in (%s)" % ( + qid, width, ','.join(["0x%x" % i for i in l])) + elif self.opcode == self.EFI_IFR_AND_OP: + print ts + "AND" + elif self.opcode == self.EFI_IFR_OR_OP: + print ts + "OR" + elif self.opcode == self.EFI_IFR_NOT_OP: + print ts + "NOT" + elif self.opcode == self.EFI_IFR_ONE_OF_OP: + qid, width, pid, hid = struct.unpack("] '%s'" % (qid, width, s[pid]) + if s[hid] and s[hid] != ' ': + print ts + "\Help text: '%s'" % s[hid] + elif self.opcode == self.EFI_IFR_ONE_OF_OPTION_OP: + oid, value, flags, key = struct.unpack("] %d-%d Step %d Default %d Flags 0x%x" % (t, s[pid], qid, width, min, max, step, default, flags) + if s[hid] and s[hid] != ' ': + print ts + "\Help text: '%s'" % s[hid] + elif self.opcode == self.EFI_IFR_PASSWORD_OP: + qid, width, pid, hid, flags, key, mins, maxs, encoding = struct.unpack("] Flags 0x%x Key 0x%x Size %d-%d Encoding %d" % (s[pid], qid, width, flags, key, mins, maxs, encoding) + if s[hid] and s[hid] != ' ': + print ts + "\Help text: '%s'" % s[hid] + else: + print ts + "Opcode 0x%x (%d)" % ( + self.opcode, self.length), hexdump(self.payload) + + +class Form(HiiPack): + def __init__(self, data, offset, stable=None): + #print "Constructing form.." + HiiPack.__init__(self, data, offset) + data = self.data + self.opcodes = [] + while len(data): + op = FormOp(data, stable) + #print "Created ", len(self.opcodes), op.length + assert op.length + data = data[op.length:] + self.opcodes.append(op) + + def fetch_opcodes(self, opcode_wanted): + return filter(lambda x: x.opcode == opcode_wanted, self.opcodes) + + def __repr__(self): + formset = self.fetch_opcodes(FormOp.EFI_IFR_FORM_SET_OP) + assert len(formset) == 1 + return "" % (formset[0].get_info(), self.offset) + return "%s" % formset + + def locate_formset(self): + pass + + def showinfo(self, stringtable, ts=''): + ind = 0 + in_if = False + fstk = [] + for op in self.opcodes: + if op.opcode == op.EFI_IFR_FORM_OP: + fstk.append(ind) + if op.indent < 0: + ind += op.indent + ots = ts + ' ' * ind + if in_if and op.opcode in (op.EFI_IFR_SUPPRESS_IF_OP, op.EFI_IFR_GRAYOUT_IF_OP): + ots = ts + ' ' * (ind - 1) + '+' + try: + op.showinfo(stringtable, ots) + #except: + # print ts+"ERROR DECODING OPCODE 0x%x LEN 0x%x"%(op.opcode, op.length) + finally: + pass + if (not in_if or op.opcode not in (op.EFI_IFR_SUPPRESS_IF_OP, op.EFI_IFR_GRAYOUT_IF_OP)) and op.indent > 0: + ind += op.indent + if op.opcode in (op.EFI_IFR_SUPPRESS_IF_OP, op.EFI_IFR_GRAYOUT_IF_OP): + in_if = True + elif op.opcode == op.EFI_IFR_END_IF_OP: + in_if = False + if op.opcode == op.EFI_IFR_END_FORM_OP: + xind = fstk.pop() + if xind != ind: + print "WARNING: Indentation mismatch" + ind = xind + +#filename = "vt_bios.fd" +#filename = sys.argv[1] +#filename = "../test1/fv-00000010.bin" + +#filename = "../test1/fe3542fe-c1d3-4ef8-7c65-8048606ff670-SetupUtility.sec0.sub2.pe" + +#pe = open(filename, "rb").read() + + +def dump_setup(pe): + strings = StringTable(pe, STRING_TABLE) + strings.showinfo() + + for fn, off in FORMS[0:]: + print + print "Reading form '%s'" % fn, off + f = Form(pe, off) + #f.showinfo(strings, ' ') + + #print "Storage map:" + #for k in sorted(storage_map.keys()): + #print " 0x%x: %s"%(k,storage_map[k]) diff --git a/bios_extract/insyde-tools/fsdump.py b/bios_extract/insyde-tools/fsdump.py new file mode 100644 index 0000000..ca05cfb --- /dev/null +++ b/bios_extract/insyde-tools/fsdump.py @@ -0,0 +1,177 @@ +#! /usr/bin/env python + +from __future__ import with_statement + +from util import from_b64 +import sys +import struct + + +def fguid(s): + a, b, c, d, e = struct.unpack("" % self.type) + + small, large = self.TI[self.type] + if callable(large): + large = large(self) + return small, large + + def showinfo(self, ts=''): + if self.type == 0x02: + print ts + "GUID-defined section" + if self.sections: + print ts + " CRC32 subsection container:" + for i, s in enumerate(self.sections): + print ts + " Subsection %d: type 0x%02x, size 0x%x" % ( + i, s.type, s.size) + s.showinfo(ts + " ") + + else: + print ts + self.get_type_info()[1] + + def dump(self, base): + + if self.sections: + for i, s in enumerate(self.sections): + s.dump("%s.sub%d" % (base, i)) + return + + name = "%s.%s" % (base, self.get_type_info()[0]) + with open(name, "wb") as fd: + fd.write(self.data) + + print name + + def __repr__(self): + return "" % (self.name, self.get_type_info()[0], self.size) + + +class FFSFile(object): + def __repr__(self): + return "" % (self.size - 0x18, len(self.data), self.type, self.state, fguid(self.guid)) + + def __init__(self, data): + hdr = data[:0x18] + self.guid, self.checksum, self.type, self.attributes, self.size, self.state = struct.unpack("<16sHBB3sB", hdr) + self.size = struct.unpack(" + +# MIT License. + +#~ Permission is hereby granted, free of charge, to any person +#~ obtaining a copy of this software and associated documentation +#~ files (the "Software"), to deal in the Software without +#~ restriction, including without limitation the rights to use, +#~ copy, modify, merge, publish, distribute, sublicense, and/or sell +#~ copies of the Software, and to permit persons to whom the +#~ Software is furnished to do so, subject to the following +#~ conditions: + +#~ The above copyright notice and this permission notice shall be +#~ included in all copies or substantial portions of the Software. + +#~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +#~ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +#~ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +#~ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#~ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +#~ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +#~ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +#~ OTHER DEALINGS IN THE SOFTWARE. + +from ctypes import (CDLL, c_int8, c_uint32, c_uint64, c_void_p, c_char_p, + c_size_t, cast, pointer, POINTER, Structure, create_string_buffer) + +from ctypes.util import find_library + +from hashlib import md5 + + +def md5sum(data): + return md5(data).hexdigest() + + +class lzmadec_info_t(Structure): + _fields_ = [('uncompressed_size', c_uint64), + ('dictionary_size', c_uint32), + ('internal_data_size', c_uint32), + ('is_streamed', c_uint32), + ('pb', c_uint32), + ('lp', c_uint32), + ('lc', c_uint32), ] + + def __repr__(self): + bits = [] + for fieldname, fieldtype in self._fields_: + bits.append("%s=%s" % (fieldname, getattr(self, fieldname))) + return "" % ", ".join(bits) + +lzmadec_info_p = POINTER(lzmadec_info_t) + + +class lzmadec_stream_t(Structure): + _fields_ = [('next_in', c_char_p), + ('avail_in', c_size_t), + ('total_in', c_uint64), + ('next_out', c_char_p), + ('avail_out', c_size_t), + ('total_out', c_uint64), + ('state', c_void_p), + ('lzma_alloc', c_void_p), + ('lzma_free', c_void_p), + ('opaque', c_void_p)] +lzmadec_stream_p = POINTER(lzmadec_stream_t) + + +class ctypes_function(object): + def __init__(self, lib, restype, argtypes): + self.lib, self.restype, self.argtypes = lib, restype, argtypes + + def __call__(self, function): + func_name = function.__name__ + f = getattr(self.lib, func_name) + f.restype, f.argtypes = self.restype, self.argtypes + return f + +library_path = find_library("lzmadec") +assert library_path, ( + "Couldn't find `liblzmadec.so`. Please install lzma_utils.\n" + " it can be found at http://tukaani.org/lzma/download" +) +lzma = CDLL(library_path) + +# I tried the simpler lzmadec_buffer function but it didn't like that I was +# providing too much data and crashed, so I switched to the stream instead. + + +@ctypes_function(lzma, c_int8, [lzmadec_info_p, c_char_p, c_size_t]) +def lzmadec_buffer_info(): + pass + + +@ctypes_function(lzma, c_int8, [lzmadec_stream_p]) +def lzmadec_init(): + pass + + +@ctypes_function(lzma, c_int8, [lzmadec_stream_p, c_int8]) +def lzmadec_decode(): + pass + + +@ctypes_function(lzma, c_int8, [lzmadec_stream_p]) +def lzmadec_end(): + pass + +# +# USEFUL CODE STARTS HERE +# + +# Based on concepts from scanlzma.c +# scanlzma, scan for lzma compressed data in stdin and echo it to stdout. +# Copyright (C) 2006 Timo Lindfors + + +def find_lzma_headers(buffer): + MAGIC_CHAR = chr(0x5D) + + position = 0 + positions = [] + + while position < len(buffer) and MAGIC_CHAR in buffer[position:]: + + position = buffer.index(MAGIC_CHAR, position) + 1 + + if (ord(buffer[position + 3]) < 0x20 and + (buffer[position + 9:].startswith("\x00" * 3) or + buffer[position + 4:].startswith("\xFF" * 8))): + positions.append(position - 1) + + return positions + + +def lzma_decompressed_size(buffer): + "Given `buffer`, return the decompressed size" + lzmadec_info = lzmadec_info_t() + result = lzmadec_buffer_info(pointer(lzmadec_info), buffer, len(buffer)) + assert not result, "lzmadec_buffer_info failed" + #print lzmadec_info + assert lzmadec_info.dictionary_size > lzmadec_info.uncompressed_size, ( + "This probably doesn't make sense.." + ) + #print "Here..", lzmadec_info + return lzmadec_info.uncompressed_size + + +def lzma_decode(input_buffer): + """ + `input_buffer`: string. + Return Value: (decompressed string, amount of `input_buffer` used) + """ + + result_size = lzma_decompressed_size(input_buffer) + + assert result_size + + result_data = create_string_buffer(result_size) + + lzmadec_stream = lzmadec_stream_t() + + assert not lzmadec_init(lzmadec_stream) + + lzmadec_stream.next_in = input_buffer + lzmadec_stream.avail_in = len(input_buffer) + + lzmadec_stream.next_out = cast(result_data, c_char_p) + lzmadec_stream.avail_out = result_size + + result = lzmadec_decode(lzmadec_stream, 1) + + #s = lzmadec_stream + #print s.avail_in, s.total_in, s.avail_out, s.total_out + + assert not lzmadec_end(lzmadec_stream) + + result_data = result_data.raw + amount_read = lzmadec_stream.total_in + + assert result == 1 + + return result_data, amount_read + + +def get_lzma_chunks(input_buffer): + """ + Scans `input_buffer` for LZMA-like data. + Returns a list of (position, "the data found decompressed"). + """ + + ph = possible_headers = find_lzma_headers(input_buffer) + # Not a real header location, but allows one last iteration to end of file + # in the following loop + ph.append(len(input_buffer) - 1) + + result = [] + + for this_header, next_header in zip(ph, ph[1:]): + try: + #print this_header, next_header + data, length = lzma_decode(input_buffer[this_header:next_header]) + except AssertionError: + continue + + result.append((this_header, data)) + + return result + + +def test(): + bios_data = open("data/original_bios_backup.fd", "rb").read() + + results = get_lzma_chunks(bios_data) + print map(md5sum, zip(*results)[1]) + + #~ headers = find_lzma_headers(bios_data) + #~ decompr_data, amoun_read = lzma_decode(bios_data[headers[0]:]) + #~ print md5sum(decompr_data) + +if __name__ == "__main__": + test() diff --git a/bios_extract/insyde-tools/main.py b/bios_extract/insyde-tools/main.py new file mode 100644 index 0000000..f9caf53 --- /dev/null +++ b/bios_extract/insyde-tools/main.py @@ -0,0 +1,473 @@ +#! /usr/bin/env python + +# Copyright (c) 2009 d6z + +# MIT License. Based on code found found at +# http://marcansoft.com/blog/2009/06/enabling-intel-vt-on-the-aspire-8930g/ + +#~ Permission is hereby granted, free of charge, to any person +#~ obtaining a copy of this software and associated documentation +#~ files (the "Software"), to deal in the Software without +#~ restriction, including without limitation the rights to use, +#~ copy, modify, merge, publish, distribute, sublicense, and/or sell +#~ copies of the Software, and to permit persons to whom the +#~ Software is furnished to do so, subject to the following +#~ conditions: + +#~ The above copyright notice and this permission notice shall be +#~ included in all copies or substantial portions of the Software. + +#~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +#~ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +#~ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +#~ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#~ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +#~ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +#~ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +#~ OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import with_statement + +import fsdump + +from os import makedirs +from os.path import exists +from lzma import get_lzma_chunks +from struct import unpack_from +from pprint import pprint +from array import array +from dumpsetup import StringTable, Form, FormOp +from util import (md5sum, read_with_progress, find_all, find_all_backwards, + Struct, SI, from_b64, hexbytes, substitute, chexdump, FindBadPosition, + FindStopSearching + ) + +#~ SAVE_PATH = "data/original_bios-zchef.fd"; WHAT = "zchef" +#~ SAVE_PATH = "data/original_bios-dhlacik.fd"; WHAT = "dhlacik" +#~ SAVE_PATH = "data/original_bios-FaithX.fd"; WHAT = "FaithX" +#~ SAVE_PATH = "data/original_bios-mine.fd"; WHAT = "mine" +#~ SAVE_PATH = "data/KM2_110.fd"; WHAT = "v110" + +SAVE_PATH = "data/original_bios-mine.fd" +WHAT = "mine" + + +KB, MB, GB = 2 ** 10, 2 ** 20, 2 ** 30 + +SAVE_PATH = "data/original_bios_backup1.fd" +BIOS_START = 4 * GB - 2 * MB +BIOS_SIZE = 2 * MB + +# Only necessary to modify these if you are reading from /dev/mem +# If reading from BIOS dump, they are ignored. +# If someone knows how to detect the bios size automatically, that would +# be very useful +BIOS_SIZE = 2 * MB +BIOS_START = 4 * GB - BIOS_SIZE + + +class FirmwareVolumeHeader(Struct): + rsvd = SI("<16s") + guid = SI("16s") + size = SI("Q") + magic = SI("4s") + attributes = SI("I") + hdrlen = SI("H") + checksum = SI("H") + rsvd2 = SI("3s") + revision = SI("B") + cruft = SI("16s") + + def showinfo(self, depth=0): + print " " * depth, "Reserved boot zone:", hexbytes(self.rsvd) + print " " * depth, "GUID:", hexbytes(self.guid) + print " " * depth, "Size: 0x%x (data 0x%x)" % (self.size, + len(self.data)) + print " " * depth, "Attributes: 0x%08x" % self.attributes + print " " * depth, "Revision: %d" % self.revision + + +class VariableHeader(Struct): + magic = SI("<2s") + status = SI("H") + attributes = SI("I") + nsize = SI("I") + dsize = SI("I") + guid = SI("16s") + # Some bioses do not have a checksum. + # Comment me out if your bios does not work + cs = SI("H") + + +class FirmwareVolume(object): + def __init__(self, buffer, position, where=None): + buffer = buffer[position:] + + try: + fvh = FirmwareVolumeHeader(buffer) + assert fvh.magic == "_FVH", "Invalid FirmwareVolume, wrong magic" + assert fvh.hdrlen == FirmwareVolumeHeader.struct_size, ( + "Invalid FirmwareVolume, wrong header length " + "0x%04x, expected 0x%04x" % + (fvh.hdrlen, FirmwareVolumeHeader.struct_size) + ) + assert fvh.size <= len(buffer), ( + "FirmwareVolume too big? " + "size=0x%08x buflen=0x%08x" % (fvh.size, len(buffer)) + ) + + #blockdata = buffer[fvh.py_struct.size:fvh.hdrlen] + self.data = buffer[fvh.hdrlen:fvh.size] + self.position = position + self.where = where + self.good = True + except AssertionError, e: + #print ">>BAD FV at 0x%08x" % position, e + self.good = False + + def has_VSS(self): + return "$VSS" in self.data + + def __repr__(self): + hasvss = " [VSS]" if self.has_vss() else "" + args = (self.position, len(self.data), self.where, hasvss) + return "" % args + + +class Variable(object): + GLOBAL_VARIABLE = from_b64('Yd/ki8qT0hGqDQDgmAMrjA') + HEADER_MAGIC = "\xAA\x55" + ACTIVE = 0x7F + + def __init__(self, complete_data): + header_size = VariableHeader.struct_size + + header = complete_data[:header_size] + self.vh = vh = VariableHeader(header) + print "Blah:", hexbytes(vh.guid) + + assert vh.magic == self.HEADER_MAGIC, "bad magic 0x%x" % vh.magic + + total_length = vh.dsize + vh.nsize + assert len(complete_data) >= total_length, "input not long enough" + + data = complete_data[header_size:] + data = data[:total_length] + + nullterm = data.index("\x00\x00") + 1 + strend = nullterm if nullterm < vh.nsize else vh.nsize + + self.name = data[:strend].decode("utf-16le") + self.value = data[vh.nsize:total_length] + + # Set the checksum to 0, and the status to 0x7F + fdata = substitute(header + data, header_size - 2, "\x00\x00") + fdata = substitute(fdata, 2, "\x7F\x00") + + self.ccsum = self.checksum(fdata) + + #assert self.ccsum == vh.cs, "Checksum Error" + + def __repr__(self): + return "" % (self.vh.status, self.vh.dsize, self.name) + + def __len__(self): + return VariableHeader.struct_size + self.vh.nsize + self.vh.dsize + + def checksum(self, data): + if len(data) % 2: + data += chr(0) + shorts = array("H", []) + shorts.fromstring(data) + return -sum(shorts) & 0xFFFF + + def showinfo(self, ts=''): + print ts + "Variable %s" % repr(self.name) + print ts + " Attributes: 0x%08x" % self.vh.attributes + print ts + " Status: 0x%02x" % self.vh.status + if self.vh.guid == self.GLOBAL_VARIABLE: + print ts + (" VendorGUID: EFI_GLOBAL_VARIABLE (%s)" % + ' '.join('%02x' % ord(c) for c in self.vh.guid)) + else: + print ts + (" VendorGUID: %s" % + ' '.join('%02x' % ord(c) for c in self.vh.guid)) + #print ts+" Checksum: 0x%02x"%self.vh.cs + #print ts+" calc 0x%04x"%self.ccsum + print ts + " Value (0x%x bytes):" % (len(self.value)) + chexdump(self.value, ts + " ") + + +class VSSData(object): + def __init__(self, data): + (size,) = unpack_from("I", data[4:]) + assert size < len(data), ( + "Too big! size = %i len = %i" % (size, len(data)) + ) + + vssdata = data[0x10:size] + + self.vars = [] + self.size = size + + position = 0 + while (position < len(data) and + vssdata[position:].startswith(Variable.HEADER_MAGIC)): + print "Creating variable at", position + v = Variable(vssdata[position:]) + position += len(v) + self.vars.append(v) + + def __repr__(self): + return "" % (len(self.vars), self.size) + + def __iter__(self): + return iter(self.vars) + + +class BIOS(object): + def __init__(self, from_where=None): + "Create a BIOS object" + + bios_data = self.load_bios(from_where) + + print "Operating on BIOS %s size = 0x%x" % (SAVE_PATH, len(bios_data)) + + print "Loading compressed sections" + compressed_chunks = get_lzma_chunks(bios_data) + print " .. found %i compressed sections" % len(compressed_chunks) + + print "Locating Firmware Volumes" + volumes = self.locate_firmware_volumes(bios_data) + + for position, data in compressed_chunks: + #if False: + #with open("data/fv-compr-0x%08x" % position, "wb") as f: + # Dump the executable with the PE header in the right place + #f.write(data[data.index("MZ"):]) + + where = "[compr at 0x%x]" % position + volumes.extend(self.locate_firmware_volumes(data, where)) + + # Only good volumes + volumes = filter(lambda fv: fv.good, volumes) + vol_compr = filter( + lambda fv: fv.where and "compr" in fv.where, volumes) + + print (" .. found %i FirmwareVolumes (%i compressed)" % + (len(volumes), len(vol_compr))) + + setup_utility = self.locate_setup_utility(vol_compr) + + TYPE_PE = 0x10 + setup_utility_pe = self.get_section_type(setup_utility[1], TYPE_PE) + + dump_filename = "data/SetupUtility-%s.pe" % WHAT + if not exists(dump_filename): + + pe = setup_utility_pe + + with open(dump_filename, "wb") as fd: + fd.write(pe.data) + + print "Wrote SetupUtility to %s" % dump_filename + print " Size = 0x%x MD5: %s" % (len(pe.data), md5sum(pe.data)) + + self.locate_packs(setup_utility_pe.data) + + self.locate_vss(volumes) + + def locate_vss(self, volumes): + + for vss_volume in filter(FirmwareVolume.has_vss, volumes): + print "Have vss_volume:", vss_volume + try: + vssdata = VSSData(vss_volume.data) + except AssertionError, e: + #print " .. failed to load: '%s'" % e + continue + + print vss_volume, vssdata + + for var in vssdata: + if var.vh.status == Variable.ACTIVE: + print var + if var.name == "Setup": + var.showinfo() + + def locate_packs(self, setuputility_binary): + "Searches for Forms and the English StringTable using a set of heuristics" + + # 1st byte: upper bits from length: almost certainly zero + # 2-3: Short typecode, 3 == form + # 4-5: 0x0e is the formset opcode, and 0x24 is is length + # This magic string appears three bytes into the header + form_magic = "\x00\x03\x00\x0e\x24" + form_magic_offset = -3 + + # HiipackHeaderSize + HHS = 6 + english_attribute_magic = "\x00" * 4 + + def create_stringtable(magic_location): + def test_stringtable(poss_header_location): + # We started at attributes, start at lnoff + poss_header_location -= 12 + + dat = setuputility_binary[poss_header_location:] + lnoff, plnoff, count, attributes = unpack_from(", name='%s' help='%s'" % args + + if not found: + print "Sorry, I couldn't locate the VT flag? :(" + + def get_sections(self, container): + "Return a recursive list of sections" + result = [] + for section in container.sections: + result.append(section) + if section.sections: + result.append(self.get_sections(section)) + return result + + def get_section_type(self, sections, type): + "Return the first section that has type `type`" + for section in sections: + if section.type == type: + return section + + def locate_setup_utility(self, volumes): + "Locate the SetupUtility section within `volumes`" + + for vol in volumes: + if vol.position != 0x10: + continue + + for file in fsdump.FS(vol.data): + FILE_TYPE_CONTAINS_PE = 0x07 + if file.type != FILE_TYPE_CONTAINS_PE: + continue + + sections = self.get_sections(file) + + TYPE_NAME = 0x15 + name_section = self.get_section_type(sections[1], TYPE_NAME) + + if name_section.name == "SetupUtility": + return sections + + raise RuntimeError("Shouldn't get here, seems we couldn't " + "find the SetupUtility :/") + + def locate_firmware_volumes(self, data, where=None): + """Search through `data` looking for firmware volume headers + `where` optionally specifies where it came from (e.g. compressed section) + """ + FVH_OFFSET_WITHIN_HEADER = 0x28 + subtract_offset = lambda x: x - FVH_OFFSET_WITHIN_HEADER + items = find_all(data, "_FVH", subtract_offset) + + #print "Found the following:", items + + return [FirmwareVolume(data, position, where) for position in items] + + def retrieve_from_memory(self): + "Download the BIOS directly from memory. Must be root to do this." + try: + with open("/dev/mem", "rb") as memory: + memory.seek(BIOS_START) + + print "Reading BIOS data.." + bios_data = read_with_progress(memory, BIOS_SIZE, 2 ** 6 * KB) + + except IOError, err: + if err.errno == 13: + print "Read error reading '%s' Are you root?" % path + else: + print "Unexpected error" + raise + + return bios_data + + def load_bios(self, from_where=None): + "If the desired bios file doesn't exist, try to load it from memory" + + if from_where is None: + from_where = SAVE_PATH + + if not exists(from_where): + bios_data = self.retrieve_from_memory() + + print "Saving BIOS to '%s' md5:%s" % (SAVE_PATH, md5sum(bios_data)) + with open(SAVE_PATH, "wb") as f_bios: + f_bios.write(bios_data) + + else: + with open(from_where, "rb") as f_bios: + bios_data = f_bios.read() + + print "Opened BIOS '%s' with md5:%s" % ( + from_where, md5sum(bios_data)) + + return bios_data + + +def main(): + if not exists("./data/"): + makedirs("./data/") + + bios = BIOS() + + print "Done" + +if __name__ == "__main__": + main() diff --git a/bios_extract/insyde-tools/specs/insyde-module-header-format.txt b/bios_extract/insyde-tools/specs/insyde-module-header-format.txt new file mode 100644 index 0000000..9a71586 --- /dev/null +++ b/bios_extract/insyde-tools/specs/insyde-module-header-format.txt @@ -0,0 +1,75 @@ +Offset (h) Length (h) Item +---------- ---------- ---- +0 10 GUID - [EFI_GUID] +10 1 Header checksum (2's complement sum of first 17h bytes - assume extra checksum is 0) \ [EFI_FFS_INTEGRITY_CHECK] +11 1 Module checksum (2's complement sum of bytes from 18h to end of module (excluding any FFh padding)) / +12 1 Type of module - [EFI_FV_FILETYPE] +13 1 Module attributes - [EFI_FFS_FILE_ATTRIBUTES] +14 3 Size of header and module (excluding FFh padding at end, but including 00h padding) - [Size] +17 1 State - [EFI_FFS_FILE_STATE] - only ever seen F8h + +Common header + +18 3 Size of extended header and module (from 18h in header, excluding FFh padding, but including 00h padding) +1b 1 Type byte / Extended header byte + - if 1,10h-1Bh then module follows directly + - if 2 then extended header +Extended header + +1c 10 GUID (? always FC1BCDB0-7D31-49AA-936A-A4600D9DD083) +2c 2 Data offset (from common header - normally 1Ch) +2e 2 Attributes (1 = Processing required, 2 = Status valid) +30 4 CRC32 of module (including last 4 bytes of header, excluding FFh padding, including 00h padding) + +Next common header + +34 3 Module size (including these 4 bytes, excluding ALL padding) +37 1 Type byte + + All modules are padded 00h/FFh to QWORD boundry + +Type +EFI_FV_FILETYPE_ALL 0x00 +EFI_FV_FILETYPE_RAW 0x01 +EFI_FV_FILETYPE_FREEFORM 0x02 +EFI_FV_FILETYPE_SECURITY_CORE 0x03 +EFI_FV_FILETYPE_PEI_CORE 0x04 +EFI_FV_FILETYPE_DXE_CORE 0x05 +EFI_FV_FILETYPE_PEIM 0x06 +EFI_FV_FILETYPE_DRIVER 0x07 +EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER 0x08 +EFI_FV_FILETYPE_APPLICATION 0x09 + +Attributes +00000001 - Tail present +00000010 - Needed for crisis recovery +00000100 - Reserved (0) +00111000 - Alignment (000 - 111) +01000000 - If set, module checksum present + +State +00000001 - EFI_FILE_HEADER_CONSTRUCTION +00000010 - EFI_FILE_HEADER_VALID +00000100 - EFI_FILE_DATA_VALID +00001000 - EFI_FILE_MARKED_FOR_UPDATE +00010000 - EFI_FILE_DELETED +00100000 - EFI_FILE_HEADER_INVALID +11000000 - ? Always 11 + +Type of region +EFI_SECTION_COMPRESSION 0x01 (not compressed in most insyde) +EFI_SECTION_GUID_DEFINED 0x02 +EFI_SECTION_PE32 0x10 +EFI_SECTION_PIC 0x11 +EFI_SECTION_TE 0x12 +EFI_SECTION_DXE_DEPEX 0x13 +EFI_SECTION_VERSION 0x14 +EFI_SECTION_USER_INTERFACE 0x15 +EFI_SECTION_COMPATIBILITY16 0x16 +EFI_SECTION_FIRMWARE_VOLUME_IMAGE 0x17 +EFI_SECTION_FREEFORM_SUBTYPE_GUID 0x18 +EFI_SECTION_RAW 0x19 +EFI_SECTION_PEI_DEPEX 0x1B + +Source: http://forums.mydigitallife.info/threads/11693-Insyde-module-headers + diff --git a/bios_extract/insyde-tools/util.py b/bios_extract/insyde-tools/util.py new file mode 100644 index 0000000..087e562 --- /dev/null +++ b/bios_extract/insyde-tools/util.py @@ -0,0 +1,168 @@ +#! /usr/bin/env python + +from hashlib import md5 +from sys import stdout +from pprint import pprint + + +def md5sum(data): + return md5(data).hexdigest() + + +class FindBadPosition(Exception): + """Raised to disallow a position from appearing in the result list in `find_all`""" + pass + + +class FindStopSearching(Exception): + """Stop searching in find_all""" + def __init__(self, result): + self.result = result + + +def from_b64(what): + return str.decode(what + "=" * (3 - len(what) % 3), "base64") + + +def hexbytes(bytes): + return " ".join(["%02x" % ord(c) for c in bytes]) + + +def substitute(input, where, what): + return "".join([input[:where], what, input[where + len(what):]]) + + +def find_all(buffer, what, callback=lambda x: x, start=0, stop=None): + if stop is None: + stop = len(buffer) + + position = start + result = [] + + while position < stop and what in buffer[position:stop + 1]: + + position = buffer.index(what, position, stop) + + try: + result.append(callback(position)) + except FindStopSearching, exc: + return exc.result + except FindBadPosition: + pass + + position += 1 + + return result + + +def find_all_backwards(buffer, what, callback=lambda x: x, start=None, stop=0): + "Beware! This function is not fully tested. I should really write some unit tests for the edge cases" + + if start is None: + start = len(buffer) + + position = start + result = [] + + while position > stop and what in buffer[stop:position]: + + position = buffer.rindex(what, stop, position) + + try: + result.append(callback(position)) + except FindStopSearching, exc: + return exc.result + except FindBadPosition: + pass + + position += 1 + + return result + + +def read_with_progress(file_handle, total, chunk_size): + assert not total % chunk_size, "chunk_size must be a divisor of total!" + data = [] + length = (80 - 6) + for i in xrange(0, total, chunk_size): + data.append(file_handle.read(chunk_size)) + done = length * i // total + print "\r[ %s>%s ]" % ("-" * done, " " * (length - done)), + stdout.flush() + print "\r[ %s> ]" % ("-" * length) + return "".join(data) + +from struct import unpack, Struct as pyStruct + + +class StructMeta(type): + def __new__(meta, classname, bases, classDict): + + is_structitem = lambda x: isinstance(x[1], SI) + struct_items = filter(is_structitem, classDict.iteritems()) + struct_items.sort(key=lambda x: x[1].position) + + struct_format = "".join(map(lambda x: x[1].packstr, struct_items)) + + struct = pyStruct(struct_format) + + def __init__(self, data): + + data_tuple = struct.unpack_from(data) + for (name, structitem), item_data in zip(struct_items, data_tuple): + setattr(self, name, item_data) + + self.rest_of_data = data[struct.size:] + + #def repack(self): + + cls = type.__new__(meta, classname, bases, classDict) + cls.py_struct = struct + cls.struct_size = struct.size + cls.__init__ = __init__ + + return cls + + +class SI(object): + "StructItem" + counter = 0 + + def __init__(self, packstr): + self.position = SI.counter + SI.counter += 1 + self.packstr = packstr + + +class Struct(object): + __metaclass__ = StructMeta + + +def hexdump(s, sep=" "): + return sep.join(map(lambda x: "%02x" % ord(x), s)) + + +def ascii(s): + s2 = "" + for c in s: + if ord(c) < 0x20 or ord(c) > 0x7e: + s2 += "." + else: + s2 += c + return s2 + + +def pad(s, c, l): + if len(s) < l: + s += c * (l - len(s)) + return s + + +def chexdump(s, ts="", off=0): + for i in range(0, len(s), 16): + print ts + "%08x %s %s |%s|" % (i + off, pad(hexdump(s[i:i + 8], ' '), " ", 23), pad(hexdump(s[i + 8:i + 16], ' '), " ", 23), pad(ascii(s[i:i + 16]), " ", 16)) + +if __name__ == "__main__": + + s = Header("test") + print "I have something:", s diff --git a/bios_extract/microcode_extract.py b/bios_extract/microcode_extract.py new file mode 100644 index 0000000..360278c --- /dev/null +++ b/bios_extract/microcode_extract.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python2 +# extract microcode updates from binary BIOS files +# v 0.1 2012/07/23 +# v 0.2 2012/07/23 added VIA Nano support (relaxed some checks) +# Licensed as Public Domain + +import ctypes +import struct +import sys +import array + +uint8_t = ctypes.c_ubyte +char = ctypes.c_char +uint32_t = ctypes.c_uint +uint64_t = ctypes.c_uint64 +uint16_t = ctypes.c_ushort + +def get_struct(str_, off, struct): + s = struct() + slen = ctypes.sizeof(s) + bytes = str_[off:off+slen] + fit = min(len(bytes), slen) + ctypes.memmove(ctypes.addressof(s), bytes, fit) + return s + +def DwordAt(f, off): + return struct.unpack(" maxoff: + return + + # update size must be a multiple of DWORD + if (hdr.DataSize & 3) or (hdr.TotalSize & 3): + return + + # looks okay. let's check the checksum + + mdata = f[off:off+check_len] + # make an array of DWORDs + arr = array.array("I", mdata) + # sum them + ck = sum(arr) & 0xFFFFFFFF + if ck == 0: + print "%08X: found a valid-looking update" % off + print ["Date: %02X/%02X/%4X", "Date: %02d/%02d/%4d"][is_via] % ((hdr.Date >> 24)&0xFF, (hdr.Date >> 16)&0xFF, hdr.Date & 0xFFFF) + print "Processor signature: %08X" % hdr.ProcessorSignature + if not is_via: + print "Processor flags: %08X" % hdr.ProcessorFlags + print "Length: %08X" % check_len + fname = "mcode_upd_%08X.bin" % off + print "Extracting to %s" % fname + open(fname, "wb").write(mdata) + return True + # nope + return False + +def find_ucode(f, is_via): + maxoff = len(f) + # minimal microcode update length is 2048 bytes + off = (maxoff-2048+8)&(~15) + # look for BCD date ddmmyyyy + # yyyy off-2 + # dd off-1 + # mm off + print "Scanning...\n%08X" % off + while off > 11: + # looks like a date? + m = ord(f[off]) + if (1 <= m <= 0x12) and (1 <= ord(f[off-1]) <= 0x31): + if check_valid_mcode(f, off-11, maxoff, is_via): + maxoff = off-11 + print "Scanning...\n%08X" % off + off -= 1 + if (off & 0xFFFFF) == 0: + print "%08X" % off + print "\nDone" + +if len(sys.argv) < 2: + print "Usage: microcode_extract.py [-i|-v]" + print " -i: look for Intel microcode (default)" + print " -v: look for VIA Nano microcode" + sys.exit(1) +else: + fn = None + is_via = False + for arg in sys.argv[1:]: + if arg == "-v": + is_via = True + elif arg == "-i": + is_via = False + else: + fn = arg + + f = open(fn, "rb").read() + find_ucode(f, is_via) diff --git a/bios_extract/phoenix_extract.py b/bios_extract/phoenix_extract.py new file mode 100644 index 0000000..f1adbe1 --- /dev/null +++ b/bios_extract/phoenix_extract.py @@ -0,0 +1,857 @@ +#!/usr/bin/env python2 +# Phoenix FFV BIOS dumper/extractor by roxfan +# 2012-09-12 version 0.1 +# 3-clause BSD license + +import sys, struct, ctypes +import os.path + +uint8_t = ctypes.c_ubyte +char = ctypes.c_char +uint32_t = ctypes.c_uint +uint64_t = ctypes.c_uint64 +uint16_t = ctypes.c_ushort + +def read_struct(li, struct): + s = struct() + slen = ctypes.sizeof(s) + bytes = li.read(slen) + fit = min(len(bytes), slen) + ctypes.memmove(ctypes.addressof(s), bytes, fit) + return s + +def get_struct(str_, off, struct): + s = struct() + slen = ctypes.sizeof(s) + bytes = str_[off:off+slen] + fit = min(len(bytes), slen) + ctypes.memmove(ctypes.addressof(s), bytes, fit) + return s + +def strguid(raw): + return "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}" % struct.unpack(" len(data): + print "" + return data + if clen + 8 < len(data): + data = data[:clen + 8] + p = subprocess.Popen([lzint_path, "-", "-"], stdout=subprocess.PIPE, stdin=subprocess.PIPE) + outd, errd = p.communicate(input=data) + return outd + except: + print "" + return data + +def parseSectionedFile(infile, pos1, endpos): + sections = [] + i = 0 + while pos1 < endpos: + sh = get_struct(infile, pos1, FfsSectionHeader) + print "\nSection %d" % i + sh.pprint() + dlen = sh.size() - ctypes.sizeof(sh) + data = infile[pos1 + ctypes.sizeof(sh):pos1 + ctypes.sizeof(sh) + dlen] + if sh.Type == SECTION_PLACE16: + offset = 0xFFFFFFFF + segment = 0xFFFF + if len(data) == 4: + addr = struct.unpack("> 4 + offset = addr - (seg<<4) + print " Address: %08X (%04X:%04X)" % (addr, seg, offset) + elif len(data) == 6: + offset, segment = struct.unpack("> 4 + offset = addr - (seg<<4) + print " PLACE16: %08X (%04X:%04X)" % (addr, seg, offset) + elif len(data) == 6: + offset, segment = struct.unpack("|') + print " ==> %s" % fname2 + open(fname2,"wb").write(ss[i].Data) + + if ff.Type == FILETYPE_BIN: + if ff.name2str() == "volumedir.bin2" and volno == None: + vols = parseVolumeDir2(infile, pos1, pos, False) + for i in range(len(vols)): + v = vols[i] + s = (v.VolStart + bioslen) & 0xFFFFFFFF # v.VolStart - 0xFF800000 + print "\n***\nVolume %d: address %08X, file offset %08X, size %08X\n***\n" % (i, v.VolStart, s, v.VolSize) + parse_range(infile, s, s + v.VolSize, bioslen, i) + vollist.extend(vols) + + fname2 = "%08X_%s.bin" % (pos0, fname) + if volno != None: + fname2 = "V%d_" % volno + fname2 + + fname2 = replace_bad(fname2, '\/:*?"<>|') + + print " ==> %s" % fname2 + dlen = ff.size() - hdrlen + data = infile[pos1:pos1 + dlen] + open(fname2,"wb").write(data) + return pos + +class FfsVolume: + def __init__(self, infile, pos, size, guid, ord): + self.filepos = pos + self.infile = infile + self.size = size + self.guid = guid + self.volInfo = None + self.ord = ord + self.files = [] + + def parse_volinfo(self, data): + self.volInfo = get_struct(data, 0, VolumeInfoBinHeader) + self.volInfo.parse_extras(data, 0) + + def parse_files(self): + pos = self.filepos + maxpos = pos + self.size + while True: + while pos < maxpos and self.infile[pos] in ['\xFF', '\x00']: + pos += 1 + if pos >= maxpos: + break + if self.infile[pos] != '\xF8': + return + ff = FfsFile(self.infile, pos) + pos = ff.get_endpos() + self.files.append(ff) + if ff.header.Type == FILETYPE_BIN and ff.header.name2str() == "volumeinfo.bin": + self.parse_volinfo(ff.raw_data()) + # skip padding after file + while pos < maxpos and infile[pos] in ['\xFF', '\x00']: + pos += 1 + + def pprint(self, bioslen, verbose): + if self.guid[0] == 0xBA: + self.parse_files() + addr = (self.filepos - bioslen)&0xFFFFFFFF + namestr = strguid(self.guid) + gt = guid2type(self.guid) + if gt == None: + gt = "VOL" + s = "%08X-%08X %08X %-40s %-8s %d(0x%x)" % (addr, addr + self.size-1, self.filepos, namestr, "<%s %d>" % (gt, self.ord), self.size, self.size) + # print "\n***\nVolume address %08X, file offset %08X, size %08X\n***\n" % (addr, self.filepos, self.size) + print s + if self.volInfo: + self.volInfo.pprint(verbose) + for f in self.files: + f.pprint(bioslen, verbose) + +def parse_range(infile, pos, maxpos, bioslen, volno = None): + start = pos + while pos < maxpos: + nextpos = parseFfsFile(infile, pos, bioslen, volno) + if nextpos == None: + break + print "\nfile offset: %08X" % pos + pos = nextpos + while pos < maxpos and infile[pos] in ['\xFF', '\x00']: + pos += 1 + if volno != None and start == pos and pos < maxpos: + fname = "V%d_%08X.bin" % (volno, pos) + print " ==> %s" % fname + open(fname,"wb").write(infile[pos:maxpos]) + +class PhoenixModuleHeader(ctypes.LittleEndianStructure): + _pack_ = 1 + _fields_ = [ + ("Signature", uint32_t), # + ("Signature2", uint8_t*3), # + ("Id", uint8_t), # + ("Type", uint8_t), # + ("HeadLen", uint8_t), # + ("Compression", uint8_t), # + ("Address", uint32_t), + ("ExpLen", uint32_t), + ("FragLen", uint32_t), + ("NextFrag", uint32_t), + ] + + def pprint(self): + print "Signature: 0x%08X" % self.Signature + print "Id: %d" % self.Id + print "Type: %02X" % self.Type + print "HeadLen: %02X" % self.HeadLen + print "Compression: %d" % self.Compression + addr = self.Address + seg = addr >> 4 + offset = addr - (seg<<4) + print "Address: %08X (%04X:%04X)" % (addr, seg, offset) + print "ExpLen: %X" % (self.ExpLen) + print "FragLen: %X" % (self.FragLen) + print "NextFrag: %X" % (self.NextFrag) + +""" + struct PhoenixModule { + uint32_t Signature; + uint8_t Signature2[3]; + uint8_t Id; + uint8_t Type; + uint8_t HeadLen; + uint8_t Compression; + uint16_t Offset; + uint16_t Segment; + uint32_t ExpLen; + uint32_t FragLength; + uint32_t NextFrag; + } *Module; +""" + +def parse_trailer(infile, pos, maxpos): + start = pos + i = 0 + while pos < maxpos: + if infile[pos:pos+4] == "BC\xD6\xF1": + modhdr = get_struct(infile, pos, PhoenixModuleHeader) + modhdr.pprint() + pos += modhdr.HeadLen + addr = modhdr.Address + seg = addr >> 4 + offset = addr - (seg<<4) + fname = "%08X_%04X_%04X.bin" % (pos, seg, offset) + print " ==> %s" % fname + open(fname,"wb").write(infile[pos:pos+modhdr.ExpLen]) + pos += modhdr.ExpLen + elif infile[pos:pos+8] == "FLASHDXE": + pos += 8 + while pos < maxpos: + nextpos = parseFfsFile(infile, pos, maxpos) + if nextpos == None: + break + print "\nfile offset: %08X" % pos + pos = nextpos + else: + print "\nunknown header at %08X" % pos + break + + +if len(sys.argv) < 1: + print "Usage: phoenix_scan.py BIOS.BIN [-d] [-t] [-v]" + print "-d: extract modules into files" + print "-t: extract the trailer parts" + print "-v: verbose info about flash layout" + sys.exit(1) + +inf = open(sys.argv[1],"rb") +infile = inf.read() +pos = infile.find("volumedi\xFFr.bin2") +if pos != -1: + pos -= 8 + print "Found Volume Directory v2 at %08X\n" % (pos) +else: + print "Volume dir not found; FFS dump won't be available" + #sys.exit(1) + +dump_all = False +dump_trailer = False +print_verbose = False +for a in sys.argv[2:]: + if a == '-d': + dump_all = True + elif a == '-t': + dump_trailer = True + elif a == '-v': + print_verbose = True + +alllen = len(infile) +bioslen = alllen & 0xFFFF0000 +if dump_all and pos != -1: + parse_range(infile, pos, alllen, bioslen) +if dump_trailer: + parse_trailer(infile, bioslen, alllen) + +if pos != -1: + voldir = FfsFile(infile, pos) + vols = parseVolumeDir2(voldir.raw_data(), 0, 0, print_verbose) + ffvols = [] + for i in range(len(vols)): + v = vols[i] + pos = (v.VolStart + bioslen) & 0xFFFFFFFF + ffv = FfsVolume(infile, pos, v.VolSize, v.Guid, i) + ffvols.append(ffv) + + print "FFS contents:" + for fv in ffvols: + print "" + fv.pprint(bioslen, print_verbose) diff --git a/bios_extract/src/ami.c b/bios_extract/src/ami.c new file mode 100644 index 0000000..80714d5 --- /dev/null +++ b/bios_extract/src/ami.c @@ -0,0 +1,621 @@ +/* + * Decompression utility for AMI BIOSes. + * + * Copyright 2009 Luc Verhaegen + * Copyright 2000-2006 Anthony Borisow + * Copyright 2021 RichardG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE 1 /* for memmem */ + +#include +#include +#include +#include +#include +#include +#include + +#include "bios_extract.h" +#include "compat.h" +#include "lh5_extract.h" + +struct AMI95ModuleName { + uint8_t Id; + char *Name; +}; + +static struct AMI95ModuleName AMI95ModuleNames[] = { + {0x00, "POST"}, + {0x01, "Setup Server"}, + {0x02, "RunTime"}, + {0x03, "DIM"}, + {0x04, "Setup Client"}, + {0x05, "Remote Server"}, + {0x06, "DMI Data"}, + {0x07, "Green PC"}, + {0x08, "Interface"}, + {0x09, "MP"}, + {0x0A, "Notebook"}, + {0x0B, "Int-10"}, + {0x0C, "ROM-ID"}, + {0x0D, "Int-13"}, + {0x0E, "OEM Logo"}, + {0x0F, "ACPI Table"}, + {0x10, "ACPI AML"}, + {0x11, "P6 Microcode"}, + {0x12, "Configuration"}, + {0x13, "DMI Code"}, + {0x14, "System Health"}, + {0x15, "Memory Sizing"}, + {0x16, "Memory Test"}, + {0x17, "Debug"}, + {0x18, "ADM (Display MGR)"}, + {0x19, "ADM Font"}, + {0x1A, "Small Logo"}, + {0x1B, "SLAB"}, + {0x1C, "BCP Info"}, + {0x1D, "Dual Logo"}, + {0x1E, "Intel OSB"}, + {0x20, "PCI AddOn ROM"}, + {0x21, "Multilanguage"}, + {0x22, "UserDefined"}, + {0x23, "ASCII Font"}, + {0x24, "BIG5 Font"}, + {0x25, "OEM Logo"}, + {0x26, "Debugger"}, + {0x27, "Debugger Port"}, + {0x28, "BMC Output"}, + {0x29, "MBI File"}, + {0x2A, "User ROM"}, + {0x2B, "PXE Code"}, + {0x2C, "AMI Font"}, + {0x2E, "User ROM"}, + {0x2D, "Battery Refresh"}, + {0x2F, "Serial Redirection"}, + {0x30, "Font Database"}, + {0x31, "OEM Logo Data"}, + {0x32, "Graphic Logo Code"}, + {0x33, "Graphic Logo Data"}, + {0x34, "Action Logo Code"}, + {0x35, "Action Logo Data"}, + {0x36, "Virus"}, + {0x37, "Online Menu"}, + {0x38, "Lang1 as ROM"}, + {0x39, "Lang2 as ROM"}, + {0x3A, "Lang3 as ROM"}, + {0x40, "AMD CIM-X NB binary"}, + {0x60, "AMD CIM-X SB binary"}, + {0x70, "OSD Bitmaps"}, + {0x80, "Image Info"}, + {0xab, "CompuTrace backdoor"}, + {0xf0, "Asrock Backup Util or Windows SLIC"}, + {0xf9, "Asrock AMD AHCI DLL"}, + {0xfa, "Asrock LOGO GIF"}, + {0xfb, "Asrock LOGO JPG"}, + {0xfc, "Asrock LOGO JPG"}, + {0xfd, "Asrock LOGO PCX - Instant boot"}, + {0, NULL} +}; + +static char *AMI95ModuleNameGet(uint8_t ID) +{ + int i; + + for (i = 0; AMI95ModuleNames[i].Name; i++) + if (AMI95ModuleNames[i].Id == ID) + return AMI95ModuleNames[i].Name; + return NULL; +} + +/* + * + */ +Bool +AMI940725Extract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t AMIBOffset, uint32_t ABCOffset) +{ + Bool Compressed; + uint32_t Offset = ABCOffset + 0x10; + char Date[9]; + unsigned char *ABWOffset; + char Version[5]; + int i; + + struct b94 { + const uint16_t PackLenLo; + const uint16_t PackLenHi; + const uint16_t RealLenLo; + const uint16_t RealLenHi; + } *b94; + + /* Get Date */ + memcpy(Date, BIOSImage + BIOSLength - 11, 8); + Date[8] = 0; + + ABWOffset = memmem(BIOSImage, BIOSLength, "AMIBIOS W ", 10); + if (ABWOffset) { + Version[0] = *(ABWOffset + 10); + Version[1] = *(ABWOffset + 11); + Version[2] = *(ABWOffset + 13); + Version[3] = *(ABWOffset + 14); + Version[4] = 0; + } else { + Version[0] = 0; + } + + + printf("AMI94 Version\t: %s (%s)\n", Version, Date); + + /* First, the boot rom */ + uint32_t BootOffset; + int fd; + + BootOffset = AMIBOffset & 0xFFFF0000; + + printf("0x%05X (%6d bytes) -> amiboot.rom\n", BootOffset, + BIOSLength - BootOffset); + + fd = open("amiboot.rom", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", + "amiboot.rom", strerror(errno)); + return FALSE; + } + + write(fd, BIOSImage + BootOffset, BIOSLength - BootOffset); + close(fd); + + for (i = 0; i < 0x80; i++) { + char filename[64]; + unsigned char *Buffer; + int BufferSize, ROMSize; + + b94 = (struct b94 *)(BIOSImage + Offset); + + if ((le16toh(b94->PackLenLo) == 0x0000) + || (le16toh(b94->RealLenLo) == 0x0000)) + break; + + sprintf(filename, "amibody_%02x.rom", i); + + Compressed = TRUE; + +NotCompressed: + ROMSize = le32toh(b94->PackLenLo); + if (Compressed) + BufferSize = le32toh(b94->RealLenLo); + else + BufferSize = ROMSize; + + printf("0x%05X (%6d bytes)", Offset + 8, + ROMSize); + + printf(" -> %-20s", filename); + + printf(" (%6d bytes)", BufferSize); + + printf("\n"); + + Buffer = MMapOutputFile(filename, BufferSize); + if (!Buffer) + return FALSE; + + if (Compressed) { + if (LH5Decode(BIOSImage + Offset + 8, + ROMSize, Buffer, BufferSize) == -1) { + Compressed = FALSE; + munmap(Buffer, BufferSize); + unlink(filename); + goto NotCompressed; + } + } else + memcpy(Buffer, BIOSImage + Offset + 8, + BufferSize); + + munmap(Buffer, BufferSize); + + Offset += ROMSize; + } + + return TRUE; +} + +/* + * + */ +Bool +AMI941010Extract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t AMIBOffset, uint32_t ABCOffset) +{ + Bool Compressed; + char Date[9]; + unsigned char *ABWOffset; + char Version[5]; + int i; + + struct part { + const uint16_t RealCS; + const uint8_t PartID; + const uint8_t IsComprs; + } *part; + + struct headerinfo { + const uint16_t ModuleCount; + } *headerinfo; + + struct b94 { + const uint16_t PackLenLo; + const uint16_t PackLenHi; + const uint16_t RealLenLo; + const uint16_t RealLenHi; + } *b94; + + /* Get Date */ + memcpy(Date, BIOSImage + BIOSLength - 11, 8); + Date[8] = 0; + + if (AMIBOffset) + ABWOffset = BIOSImage + AMIBOffset; + else + ABWOffset = memmem(BIOSImage + AMIBOffset, BIOSLength - AMIBOffset, "AMIBIOS W ", 10); + if (ABWOffset) { + Version[0] = *(ABWOffset + 10); + Version[1] = *(ABWOffset + 11); + Version[2] = *(ABWOffset + 13); + Version[3] = *(ABWOffset + 14); + Version[4] = 0; + } else { + Version[0] = 0; + } + + if (BIOSImage[ABCOffset] == 'O') /* NexGen */ + ABCOffset -= 5; + + printf("AMI94 Version\t: %s (%s)\n", Version, Date); + + /* First, the boot rom */ + uint32_t BootOffset; + int fd; + + BootOffset = AMIBOffset & 0xFFFF0000; + + printf("0x%05X (%6d bytes) -> amiboot.rom\n", BootOffset, + BIOSLength - BootOffset); + + fd = open("amiboot.rom", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", + "amiboot.rom", strerror(errno)); + return FALSE; + } + + write(fd, BIOSImage + BootOffset, BIOSLength - BootOffset); + close(fd); + + /* now dump the individual modules */ + headerinfo = (struct headerinfo *)(BIOSImage + ABCOffset + 0x10); + for (i = 0; i < headerinfo->ModuleCount; i++) { + char filename[64], *ModuleName; + unsigned char *Buffer; + int BufferSize, ROMSize; + + part = (struct part *)(BIOSImage + ABCOffset + 0x14 + (i * 4)); + b94 = (struct b94 *)(BIOSImage + ABCOffset + part->RealCS); + + if (part->IsComprs & 0x80) + Compressed = FALSE; + else + Compressed = TRUE; + + sprintf(filename, "amibody_%02x.rom", i); + +NotCompressed: + if (Compressed) { + ROMSize = le16toh(b94->PackLenLo); + BufferSize = le16toh(b94->RealLenLo); + } else { + ROMSize = BufferSize = 0x10000 - part->RealCS; + } + + printf("0x%05X (%6d bytes)", ABCOffset + part->RealCS + 8, + ROMSize); + + printf(" -> %-20s", filename); + + if (Compressed) + printf(" (%6d bytes)", BufferSize); + else + printf(" "); + + ModuleName = AMI95ModuleNameGet(part->PartID); + if (ModuleName) + printf(" \"%s\"\n", ModuleName); + else + printf("\n"); + + Buffer = MMapOutputFile(filename, BufferSize); + if (!Buffer) + return FALSE; + + if (Compressed) { + if (LH5Decode(BIOSImage + ABCOffset + part->RealCS + 8, + ROMSize, Buffer, BufferSize) == -1) { + Compressed = FALSE; + munmap(Buffer, BufferSize); + unlink(filename); + goto NotCompressed; + } + } else + memcpy(Buffer, BIOSImage + ABCOffset + part->RealCS, + BufferSize); + + munmap(Buffer, BufferSize); + } + + return TRUE; +} + +/* + * + */ +Bool +AMI95Extract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t AMIBOffset, uint32_t ABCOffset) +{ + Bool Compressed, ZeroVersion; + uint32_t Offset, PackedOffset, NewOffset; + char Date[9], OffsetMode; + int i; + + struct abc { + const char AMIBIOSC[8]; + const char Version[4]; + const uint16_t CRCLen; + const uint32_t CRC32; + const uint16_t BeginLo; + const uint16_t BeginHi; + } *abc; + + struct bigpart { + const uint32_t CSize; + const uint32_t Unknown; + } *bigpart; + + struct part { + /* When Previous Part Address is 0xFFFFFFFF, then this is the last part. */ + uint16_t PrePartLo; /* Previous part low word */ + uint16_t PrePartHi; /* Previous part high word */ + uint16_t CSize; /* Header length */ + uint8_t PartID; /* ID for this header */ + uint8_t IsComprs; /* 0x80 -> compressed */ + uint32_t RealCS; /* Old BIOSes: + Real Address in RAM where to expand to + Now: + Type 0x20 PCI ID of device for this ROM + Type 0x21 Language ID (ascii) */ + uint32_t ROMSize; /* Compressed Length */ + uint32_t ExpSize; /* Expanded Length */ + } *part; + + if (!ABCOffset) { + if ((BIOSImage[8] == '1') && (BIOSImage[9] == '0') && + (BIOSImage[11] == '1') && (BIOSImage[12] == '0')) + return AMI941010Extract(BIOSImage, BIOSLength, BIOSOffset, 0, 0); + else + return AMI940725Extract(BIOSImage, BIOSLength, BIOSOffset, 0, 0); + } + + if (ABCOffset + sizeof (struct abc) < BIOSLength) { + abc = (struct abc *)(BIOSImage + ABCOffset); + ZeroVersion = (memcmp(abc->Version, "0000", 4) == 0); + if ((memcmp(abc->Version, "AMIN", 4) == 0) || ZeroVersion) { + /* Skip to next one if immediately followed by "AMINCBLK" + * header or "0000" in place of a version number. */ + abc = (struct abc *)memmem (BIOSImage + ABCOffset + 1, + BIOSLength - ABCOffset - 1 - sizeof (struct abc), + "AMIBIOSC", 8); + /* go back to the original if only a 0000 is present */ + if (!abc && ZeroVersion) + abc = (struct abc *)(BIOSImage + ABCOffset); + } + } else + abc = NULL; + + if (!abc) { + fprintf(stderr, + "Error: short read after AMIBIOSC signature.\n"); + return FALSE; + } + + /* Get Date */ + memcpy(Date, BIOSImage + BIOSLength - 11, 8); + Date[8] = 0; + + printf("AMI95 Version\t: %.4s (%s)\n", abc->Version, Date); + + /* First, the boot rom */ + uint32_t BootOffset; + int fd; + + BootOffset = AMIBOffset & 0xFFFF0000; + + printf("0x%05X (%6d bytes) -> amiboot.rom\n", BootOffset, + BIOSLength - BootOffset); + + fd = open("amiboot.rom", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", + "amiboot.rom", strerror(errno)); + return FALSE; + } + + write(fd, BIOSImage + BootOffset, BIOSLength - BootOffset); + close(fd); + + /* now dump the individual modules */ + if (BIOSLength > 0x100000) { + OffsetMode = 'A'; + Offset = (le16toh(abc->BeginHi) << 16) + le16toh(abc->BeginLo); + if ((Offset - BIOSOffset) >= BIOSLength) { +OffsetModeB: + OffsetMode = 'B'; + /* amideco considers 0x100000, but this only works up to 4 MB + 8 MB is undecipherable so far */ + PackedOffset = 0x100000; + Offset = (le16toh(abc->BeginHi) << 4) + le16toh(abc->BeginLo); + Offset = BIOSLength - (PackedOffset - (Offset + sizeof(struct abc))) - sizeof(struct abc); + } + } else { + OffsetMode = 'C'; + Offset = (le16toh(abc->BeginHi) << 4) + le16toh(abc->BeginLo); + } + + for (i = 0; i < 0x80; i++) { + char filename[64], *ModuleName; + unsigned char *Buffer; + int BufferSize, ROMSize; + + if ((Offset - BIOSOffset) >= BIOSLength) { + fprintf(stderr, "Error: part overruns buffer at %05X\n", + Offset - BIOSOffset); + return FALSE; + } + part = (struct part *)(BIOSImage + (Offset - BIOSOffset)); + + if (part->IsComprs & 0x80) + Compressed = FALSE; + else + Compressed = TRUE; + + /* even they claim they are compressed they arent */ + if ((part->PartID == 0x40) || (part->PartID == 0x60)) + Compressed = FALSE; + + if (part->PartID == 0x20) { + uint16_t vid = le32toh(part->RealCS) & 0xFFFF; + uint16_t pid = le32toh(part->RealCS) >> 16; + sprintf(filename, "amipci_%04X_%04X.rom", vid, pid); + } else if (part->PartID == 0x21) { + sprintf(filename, "amilang_%c%c.rom", + (le32toh(part->RealCS) >> 8) & 0xFF, + le32toh(part->RealCS) & 0xFF); + } else + sprintf(filename, "amibody_%02x.rom", part->PartID); + +NotCompressed: + if (Compressed) { + ROMSize = le32toh(part->ROMSize); + BufferSize = le32toh(part->ExpSize); + } else { + BufferSize = le16toh(part->CSize); + if (!BufferSize || (BufferSize == 0xFFFF)) { + bigpart = + (struct bigpart *)(BIOSImage + + (Offset - BIOSOffset) - + sizeof(struct bigpart)); + BufferSize = le32toh(bigpart->CSize); + } + ROMSize = BufferSize; + } + + /* misunderstood an offset mode B image */ + if ((i == 0) && (OffsetMode == 'A') && (ROMSize == 0xFFFFFFFF)) + goto OffsetModeB; + + if (Compressed) + printf("0x%05X (%6d bytes)", Offset - BIOSOffset + 0x14, + ROMSize); + else + printf("0x%05X (%6d bytes)", Offset - BIOSOffset + 0x0C, + ROMSize); + + printf(" -> %-20s", filename); + + if (Compressed) + printf(" (%6d bytes)", BufferSize); + else + printf(" "); + + ModuleName = AMI95ModuleNameGet(part->PartID); + if (ModuleName) + printf(" \"%s\"\n", ModuleName); + else + printf("\n"); + + Buffer = MMapOutputFile(filename, BufferSize); + if (!Buffer) { + if (Compressed) { + Compressed = FALSE; + goto NotCompressed; + } else { + return FALSE; + } + } + + NewOffset = Offset - BIOSOffset; + if (Compressed) + NewOffset += 0x14; + else + NewOffset += 0x0C; + + if ((NewOffset + ROMSize) >= BIOSLength) + ROMSize = BIOSLength - NewOffset; + + if (Compressed) { + if (LH5Decode(BIOSImage + NewOffset, + ROMSize, Buffer, BufferSize) == -1) { + Compressed = FALSE; + munmap(Buffer, BufferSize); + unlink(filename); + goto NotCompressed; + } + } else + memcpy(Buffer, BIOSImage + NewOffset, + ROMSize); + + munmap(Buffer, BufferSize); + + if ((le16toh(part->PrePartHi) == 0xFFFF) + || (le16toh(part->PrePartLo) == 0xFFFF)) + break; + + switch (OffsetMode) { + case 'B': + Offset = + (le16toh(part->PrePartHi) << 4) + + le16toh(part->PrePartLo); + Offset = BIOSLength - (PackedOffset - (Offset + sizeof(struct abc))) - sizeof(struct abc); + if ((Offset - BIOSOffset) < BIOSLength) + break; + + case 'A': + Offset = + (le16toh(part->PrePartHi) << 16) + + le16toh(part->PrePartLo); + break; + + case 'C': + Offset = + (le16toh(part->PrePartHi) << 4) + + le16toh(part->PrePartLo); + break; + } + } + + return TRUE; +} diff --git a/bios_extract/src/ami_slab.c b/bios_extract/src/ami_slab.c new file mode 100644 index 0000000..40a1969 --- /dev/null +++ b/bios_extract/src/ami_slab.c @@ -0,0 +1,195 @@ +/* + * Copyright 2010 Michael Karcher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" + +#if !defined(le32toh) || !defined(le16toh) +#if BYTE_ORDER == LITTLE_ENDIAN +#define le32toh(x) (x) +#define le16toh(x) (x) +#else +#include +#define le32toh(x) bswap_32(x) +#define le16toh(x) bswap_16(x) +#endif +#endif + +struct slabentry { + uint32_t destaddr; + uint32_t length_flag; +}; + +struct slabheader { + uint16_t entries; + uint16_t headersize; + struct slabentry blocks[0]; +}; + +struct nameentry { + uint8_t segtype; + uint16_t dtor_offset; + char name[0]; +}; + +int slabextract(const unsigned char *buffer, int bufferlen) +{ + const struct slabheader *h = (const void *)buffer; + const unsigned char *listpointer; + const unsigned char *datapointer; + int i, count, headersize; + + headersize = le16toh(h->headersize); + count = le16toh(h->entries); + if ((headersize < ((count * 8) + 4)) || (bufferlen < headersize)) { + fprintf(stderr, + "Invalid file header - probably not a SLAB file\n"); + return 1; + } + printf("%d entries\n", count); + + /* FIXME: Is the 37 really constant? */ + if (((8 * count) + 37) < headersize) { + listpointer = buffer + 8 * count + 37; + printf("Name Tp "); + } else { + listpointer = NULL; /* No names present */ + printf("Name "); + } + + datapointer = buffer + le32toh(h->headersize); + + printf("LoadAddr size initialized\n"); + + for (i = 0; i < count; i++) { + const struct slabentry *block; + char filename[25]; + uint32_t len; + int has_data; + + if (listpointer) { + const struct nameentry *entry = + (const void *)listpointer; + block = + (const void *)(buffer + + le16toh(entry->dtor_offset)); + sprintf(filename, "%.20s.bin", entry->name); + listpointer += strlen(entry->name) + 4; + printf("%-15s %02x ", entry->name, entry->segtype); + } else { + block = (const void *)(buffer + 4 + 8 * i); + sprintf(filename, "block%02d.bin", i); + printf("block%02d ", i); + } + + len = le32toh(block->length_flag); + if (len & 0x80000000) + has_data = 1; + else + has_data = 0; + len &= 0x7fffffff; + + printf("%08x %8d\t %s\n", le32toh(block->destaddr), len, + has_data ? "yes" : "no"); + + if (has_data) { + int outfd; + + if (datapointer + len > buffer + bufferlen) { + fprintf(stderr, + "Not enough data. File truncated?\n"); + return 1; + } + outfd = + open(filename, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (outfd != -1) { + int ret = write(outfd, datapointer, len); + if (ret == -1) + fprintf(stderr, "Can't write %s: %s\n", + filename, strerror(errno)); + else if (ret < len) + fprintf(stderr, + "Can't write %s completely: Disk full?\n", + filename); + close(outfd); + } else + fprintf(stderr, + "Can't create output file %s: %s\n", + filename, strerror(errno)); + datapointer += len; + } + } + + if (datapointer != buffer + bufferlen) + fprintf(stderr, "Warning: Unexpected %d trailing bytes", + (int)(buffer + bufferlen - datapointer)); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int infd; + unsigned char *InputBuffer; + int InputBufferSize; + + if (argc != 2) { + printf("usage: %s \n", argv[0]); + return 1; + } + + infd = open(argv[1], O_RDONLY); + if (infd < 0) { + fprintf(stderr, "Error: Failed to open %s: %s\n", argv[1], + strerror(errno)); + return 1; + } + + InputBufferSize = lseek(infd, 0, SEEK_END); + if (InputBufferSize < 0) { + fprintf(stderr, "Error: Failed to lseek \"%s\": %s\n", argv[1], + strerror(errno)); + return 1; + } + + InputBuffer = + mmap(NULL, InputBufferSize, PROT_READ, MAP_PRIVATE, infd, 0); + if (InputBuffer < 0) { + fprintf(stderr, "Error: Failed to mmap %s: %s\n", argv[1], + strerror(errno)); + return 1; + } + + /* fixed header size - everything else is checked dynamically in slabextract */ + if (InputBufferSize < 4) { + fprintf(stderr, + "Error: \"%s\" is too small to be a SLAB file.\n", + argv[1]); + return 1; + } + + return slabextract(InputBuffer, InputBufferSize); +} diff --git a/bios_extract/src/award.c b/bios_extract/src/award.c new file mode 100644 index 0000000..2c32aed --- /dev/null +++ b/bios_extract/src/award.c @@ -0,0 +1,73 @@ +/* + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE 1 /* for memmem */ + +#include +#include +#include +#include + +#include "compat.h" +#include "bios_extract.h" +#include "lh5_extract.h" + +/* + * + */ +Bool +AwardExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t BCPSegmentOffset) +{ + unsigned char *p, *Buffer; + int HeaderSize; + unsigned int BufferSize, PackedSize; + char *filename; + unsigned short crc; + + printf("Found Award BIOS.\n"); + + p = BIOSImage; + while (p) { + p = memmem(p, BIOSLength - (p - BIOSImage), "-lh5-", 5); + if (!p) + break; + p -= 2; + HeaderSize = LH5HeaderParse(p, BIOSLength - (p - BIOSImage), + &BufferSize, &PackedSize, &filename, + &crc); + if (!HeaderSize) + return FALSE; + + printf("0x%05X (%6d bytes) -> %s \t(%6d bytes)\n", + (unsigned int)(p - BIOSImage), HeaderSize + PackedSize, + filename, BufferSize); + + Buffer = MMapOutputFile(filename, BufferSize); + if (!Buffer) + return FALSE; + + LH5Decode(p + HeaderSize, PackedSize, Buffer, BufferSize); + + munmap(Buffer, BufferSize); + + p += HeaderSize + PackedSize; + } + + return TRUE; +} diff --git a/bios_extract/src/bcpvpd.c b/bios_extract/src/bcpvpd.c new file mode 100644 index 0000000..b44c669 --- /dev/null +++ b/bios_extract/src/bcpvpd.c @@ -0,0 +1,97 @@ +/* + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This code is heavily based on Veit Kannegiesers + * e_bcpvpd.pas program. This software comes with no license, but is freely + * downloadable at http://kannegieser.net/veit/quelle/phoedeco_src.arj + */ +/* + * It should be very straightfoward to add support for the $COMPIBM compressed + * bios images as well. According to Veits code, the id-string is "$COMPIBM", + * and the data starts straight after the (not null-terminated) id-string. + */ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "lzss_extract.h" + +int main(int argc, char *argv[]) +{ + int infd, outfd; + unsigned char *InputBuffer; + int InputBufferSize; + + if (argc != 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + infd = open(argv[1], O_RDONLY); + if (infd < 0) { + fprintf(stderr, "Error: Failed to open %s: %s\n", argv[1], + strerror(errno)); + return 1; + } + + InputBufferSize = lseek(infd, 0, SEEK_END); + if (InputBufferSize < 0) { + fprintf(stderr, "Error: Failed to lseek \"%s\": %s\n", argv[1], + strerror(errno)); + return 1; + } + + InputBuffer = + mmap(NULL, InputBufferSize, PROT_READ, MAP_PRIVATE, infd, 0); + if (InputBuffer < 0) { + fprintf(stderr, "Error: Failed to mmap %s: %s\n", argv[1], + strerror(errno)); + return 1; + } + + if (InputBufferSize < 0x52) { + fprintf(stderr, + "Error: \"%s\" is too small tp be a BCPVPD file.\n", + argv[1]); + return 1; + } + + if (strncmp((char *)InputBuffer, "BCPVPD", 7)) { + fprintf(stderr, + "Error: unable to find BCPVPD header in \"%s\".\n", + argv[1]); + return 1; + } + + outfd = open(argv[2], O_RDWR | O_TRUNC | O_CREAT, S_IRWXU); + if (outfd == -1) { + fprintf(stderr, "Error: Failed to open \"%s\": %s\n", argv[2], + strerror(errno)); + return 1; + } + + return LZSSExtract(InputBuffer + 0x52, InputBufferSize - 0x52, outfd); +} diff --git a/bios_extract/src/bios_extract.c b/bios_extract/src/bios_extract.c new file mode 100644 index 0000000..9c2dd1c --- /dev/null +++ b/bios_extract/src/bios_extract.c @@ -0,0 +1,226 @@ +/* + * Copyright 2009 Luc Verhaegen + * Copyright 2021 RichardG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE /* memmem is useful */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include "bios_extract.h" +#include "lh5_extract.h" + +static void HelpPrint(char *name) +{ + printf("\n"); + printf("Program to extract compressed modules from BIOS images.\n"); + printf("Supports AMI, Award, Phoenix and SystemSoft BIOSes.\n"); + printf("\n"); + printf("Usage:\n\t%s \n", name); +} + +unsigned char *MMapOutputFile(char *filename, int size) +{ + unsigned char *Buffer; + char *tmp; + int fd; + + if ((size < 0) || (size > 16777216)) { + fprintf(stderr, "Error: %s too big (%d bytes)\n", filename, + size); + return NULL; + } + + /* all slash signs '/' in filenames will be replaced by a backslash sign '\' */ + tmp = filename; + while ((tmp = strchr(tmp, '/')) != NULL) + tmp[0] = '\\'; + + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, + strerror(errno)); + return NULL; + } + + /* grow file */ + if (lseek(fd, size - 1, SEEK_SET) == -1) { + fprintf(stderr, "Error: Failed to grow \"%s\": %s\n", filename, + strerror(errno)); + close(fd); + return NULL; + } + + if (write(fd, "", 1) != 1) { + fprintf(stderr, "Error: Failed to write to \"%s\": %s\n", + filename, strerror(errno)); + close(fd); + return NULL; + } + + Buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (Buffer == ((void *)-1)) { + fprintf(stderr, "Error: Failed to mmap %s: %s\n", filename, + strerror(errno)); + close(fd); + return NULL; + } + + close(fd); + + return Buffer; +} + +/* TODO: Make bios identification more flexible */ + +static struct { + char *String1; + char *String2; + Bool(*Handler) (unsigned char *Image, int ImageLength, int ImageOffset, + uint32_t Offset1, uint32_t Offset2); +} BIOSIdentification[] = { + { + "AMIBIOS (C)1993 American Megatrends Inc.,", "AMIBIOSC", AMI940725Extract}, { + "AMIBIOS W 04 ", "AMIBIOSC", AMI940725Extract}, { + "AMIBIOS W 05 ", "AMIBIOSC", AMI941010Extract}, { + "AMIBIOS W 05 ", "OSC10/10/94", AMI941010Extract}, { /* NexGen */ + "AMIBOOT ROM", "AMIBIOSC0", AMI95Extract}, { + "SUPER ROM", "AMIBIOSC0", AMI95Extract}, { /* Supermicro */ + "$ASUSAMI$", "AMIBIOSC0", AMI95Extract}, { + "AMIEBBLK", "AMIBIOSC0", AMI95Extract}, { + "AMIBIOSC06", NULL, AMI95Extract}, { + "AMIBIOSC07", NULL, AMI95Extract}, { + "AMIBIOSC08", NULL, AMI95Extract}, { + "AMIBIOSC09", NULL, AMI95Extract}, { /* Hyper-V legacy BIOS */ + "= Award Decompression Bios =", NULL, AwardExtract}, { + "awardext.rom", NULL, AwardExtract}, { + "Phoenix Technologies", "BCPSEGMENT", PhoenixExtract}, { + "\xEE\x88SYSBIOS", "\xEE\x88", SystemSoftExtract}, { + "\xFF\x88SYSBIOS", "\xFF\x88", SystemSoftExtract}, { /* Insyde */ +NULL, NULL, NULL},}; + +int main(int argc, char *argv[]) +{ + int FileLength = 0; + uint32_t BIOSOffset = 0; + unsigned char *BIOSImage = NULL, + IntelAMI[256], /* just 13 bytes needed, but LH5Decode overflows the buffer */ + *Buffer = NULL; + int fd; + uint32_t Offset1 = 0, Offset2 = 0; + int i, len; + unsigned char *tmp; + + if ((argc != 2) || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + HelpPrint(argv[0]); + return 1; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error: Failed to open %s: %s\n", argv[1], + strerror(errno)); + return 1; + } + + FileLength = lseek(fd, 0, SEEK_END); + if (FileLength < 0) { + fprintf(stderr, "Error: Failed to lseek \"%s\": %s\n", argv[1], + strerror(errno)); + return 1; + } + + BIOSOffset = (0x100000 - FileLength) & 0xFFFFF; + + BIOSImage = mmap(NULL, FileLength, PROT_READ, MAP_PRIVATE, fd, 0); + if (BIOSImage < 0) { + fprintf(stderr, "Error: Failed to mmap %s: %s\n", argv[1], + strerror(errno)); + return 1; + } + + printf("Using file \"%s\" (%ukB)\n", argv[1], FileLength >> 10); + + for (i = 0; BIOSIdentification[i].Handler; i++) { + if ((i == 0) || strcmp(BIOSIdentification[i].String1, BIOSIdentification[i - 1].String1)) { + len = strlen(BIOSIdentification[i].String1); + tmp = + memmem(BIOSImage, FileLength - len, + BIOSIdentification[i].String1, len); + if (!tmp) { + Offset1 = -1; + continue; + } + Offset1 = tmp - BIOSImage; + } else if (Offset1 == -1) { + continue; + } + + if (BIOSIdentification[i].String2) { + len = strlen(BIOSIdentification[i].String2); + tmp = + memmem(BIOSImage, FileLength - len, + BIOSIdentification[i].String2, len); + if (!tmp) + continue; + Offset2 = tmp - BIOSImage; + } else { + Offset2 = Offset1; + } + + if (BIOSIdentification[i].Handler + (BIOSImage, FileLength, BIOSOffset, Offset1, Offset2)) + return 0; + else + return 1; + } + + /* Bruteforce Intel AMI Color fork LH5. */ + for (i = 0; i < (FileLength - 10); i += 0x10000) { + BIOSOffset = i; +CopyrightOffset:if ((LH5Decode(BIOSImage + BIOSOffset, FileLength - BIOSOffset, IntelAMI, 13) > -1) && + !memcmp(IntelAMI, "AMIBIOS(C)AMI", 13)) { + printf("Found Intel AMIBIOS.\nOffset: %X\n", BIOSOffset); + + Buffer = MMapOutputFile("intelbody.bin", 65536); + if (!Buffer) + return 1; + + i = 65536; + while ((LH5Decode(BIOSImage + BIOSOffset, FileLength - BIOSOffset, Buffer, i) == -1) && + (i > 16)) + i--; + + munmap(Buffer, 65536); + + return 0; + } else if (!(BIOSOffset & 0xff)) { + BIOSOffset += 0x44; + goto CopyrightOffset; + } + } + + fprintf(stderr, "Error: Unable to detect BIOS Image type.\n"); + return 1; +} diff --git a/bios_extract/src/bios_extract.h b/bios_extract/src/bios_extract.h new file mode 100644 index 0000000..1ebc0e6 --- /dev/null +++ b/bios_extract/src/bios_extract.h @@ -0,0 +1,60 @@ +/* + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef BIOS_EXTRACT_H +#define BIOS_EXTRACT_H + +#define Bool int +#define FALSE 0 +#define TRUE 1 + +#if !defined(le32toh) || !defined(le16toh) +#if BYTE_ORDER == LITTLE_ENDIAN +#define le32toh(x) (x) +#define le16toh(x) (x) +#else +#include +#define le32toh(x) bswap_32(x) +#define le16toh(x) bswap_16(x) +#endif +#endif + +/* bios_extract.c */ +unsigned char *MMapOutputFile(char *filename, int size); + +/* ami.c */ +Bool AMI940725Extract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t Offset2); +Bool AMI941010Extract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t Offset2); +Bool AMI95Extract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t Offset2); + +/* phoenix.c */ +Bool PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t Offset2); + +/* award.c */ +Bool AwardExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t Offset2); + +/* systemsoft.c */ +Bool SystemSoftExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t Offset2); + +#endif /* BIOS_EXTRACT_H */ diff --git a/bios_extract/src/compat.c b/bios_extract/src/compat.c new file mode 100644 index 0000000..f577254 --- /dev/null +++ b/bios_extract/src/compat.c @@ -0,0 +1,63 @@ +/* + * Decompression utility for AMI BIOSes. + * + * Copyright (C) 2009-2010 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#ifdef __APPLE__ +void *memmem(const void *haystack, size_t haystacklen, const void *needle, + size_t needlelen) +{ + char *searchpointer = (char *)haystack; + char *patternpointer = (char *)needle; + char *endofsearch = searchpointer + haystacklen - needlelen; + + if (!(haystack && needle && haystacklen && needlelen)) + return NULL; + + while (searchpointer <= endofsearch) { + if (*searchpointer == *patternpointer) + if (memcmp(searchpointer, patternpointer, needlelen) == + 0) + return searchpointer; + searchpointer++; + } + + return NULL; +} + +size_t strnlen(const char *s, size_t maxlen) +{ + const char *end = memchr(s, '\0', maxlen); + return end ? (size_t) (end - s) : maxlen; +} + +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen(s, n); + char *new = malloc(len + 1); + + if (new == NULL) + return NULL; + + new[len] = '\0'; + return memcpy(new, s, len); +} +#endif diff --git a/bios_extract/src/compat.h b/bios_extract/src/compat.h new file mode 100644 index 0000000..307c89a --- /dev/null +++ b/bios_extract/src/compat.h @@ -0,0 +1,48 @@ +/* + * Decompression utility for AMI BIOSes. + * + * Copyright (C) 2009-2010 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef __APPLE__ +void *memmem(const void *haystack, size_t haystacklen, const void *needle, + size_t needlelen); +size_t strnlen(const char *s, size_t maxlen); +char *strndup(const char *s, size_t n); +#endif + +// "endian.h" does not exist on (at least) these platforms: +// NetBSD, OSF/Tru64, HP-UX 10, Solaris, A/UX, Ultrix and +// AIX. It exists on FreeBSD, Linux and Irix. +#ifdef __linux__ +#include +#include +#elif __FreeBSD__ +#include +#include +#endif + +#if !defined(le32toh) || !defined(le16toh) +#if BYTE_ORDER == LITTLE_ENDIAN +#define le32toh(x) (x) +#define le16toh(x) (x) +#else +#include +#define le32toh(x) bswap_32(x) +#define le16toh(x) bswap_16(x) +#endif +#endif diff --git a/bios_extract/src/lh5_extract.c b/bios_extract/src/lh5_extract.c new file mode 100644 index 0000000..2f6fa85 --- /dev/null +++ b/bios_extract/src/lh5_extract.c @@ -0,0 +1,553 @@ +/* + * This file is a severely cut down and cleaned up version of lha. + * + * All changes compared to lha-svn894 are: + * + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * LHA has a terrible history... It dates back to 1988, has had many different + * authors and has been mostly Public Domain Software. + * + * Since 1999, Koji Arai has been doing most of + * the work at http://sourceforge.jp/projects/lha/. + */ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include "compat.h" +#include "lh5_extract.h" + +/* + * LHA header parsing. + */ +static int calc_sum(unsigned char *p, int len) +{ + int sum = 0; + + while (len--) + sum += *p++; + + return sum & 0xff; +} + +/* + * level 1 header + * + * + * offset size field name + * ----------------------------------- + * 0 1 header size [*1] + * 1 1 header sum + * ------------------------------------- + * 2 5 method ID ^ + * 7 4 skip size [*2] | + * 11 4 original size | + * 15 2 time | + * 17 2 date | + * 19 1 attribute (0x20 fixed) | [*1] header size (X+Y+25) + * 20 1 level (0x01 fixed) | + * 21 1 name length | + * 22 X filename | + * X+ 22 2 file crc (CRC-16) | + * X+ 24 1 OS ID | + * X +25 Y ??? | + * X+Y+25 2 next-header size v + * ------------------------------------------------- + * X+Y+27 Z ext-header ^ + * : | + * ----------------------------------- | [*2] skip size + * X+Y+Z+27 data | + * : v + * ------------------------------------------------- + * + */ +unsigned int +LH5HeaderParse(unsigned char *Buffer, int BufferSize, + unsigned int *original_size, unsigned int *packed_size, + char **name, unsigned short *crc) +{ + unsigned int offset; + unsigned char header_size, checksum, name_length; + + if (BufferSize < 27) { + fprintf(stderr, + "Error: Packed Buffer is too small to contain an lha header.\n"); + return 0; + } + + /* check attribute */ + if (Buffer[19] != 0x20) { + fprintf(stderr, "Error: Invalid lha header attribute byte.\n"); + return 0; + } + + /* check method */ + if (memcmp(Buffer + 2, "-lh5-", 5) != 0) { + fprintf(stderr, "Error: Compression method is not LZHUFF5.\n"); + return 0; + } + + /* check header level */ + if (Buffer[20] != 1) { + fprintf(stderr, "Error: Header level %d is not supported\n", + Buffer[20]); + return 0; + } + + /* read in the full header */ + header_size = Buffer[0]; + if (BufferSize < (header_size + 2)) { + fprintf(stderr, + "Error: Packed Buffer is too small to contain the full header.\n"); + return 0; + } + + /* verify checksum */ + checksum = Buffer[1]; + if (calc_sum(Buffer + 2, header_size) != checksum) { + fprintf(stderr, "Error: Invalid lha header checksum.\n"); + return 0; + } + + *packed_size = le32toh(*(unsigned int *)(Buffer + 7)); + *original_size = le32toh(*(unsigned int *)(Buffer + 11)); + + name_length = Buffer[21]; + *name = strndup((char *)Buffer + 22, name_length); + + *crc = le16toh(*(unsigned short *)(Buffer + 22 + name_length)); + + offset = header_size + 2; + /* Skip extended headers */ + while (1) { + unsigned short extend_size = + le16toh(*(unsigned short *)(Buffer + offset - 2)); + + if (!extend_size) + break; + + *packed_size -= extend_size; + offset += extend_size; + + if (BufferSize < offset) { + fprintf(stderr, + "Error: Buffer to small to contain extended header.\n"); + return 0; + } + } + + return offset; +} + +/* + * CRC Calculation. + */ +#define CRCPOLY 0xA001 /* CRC-16 (x^16+x^15+x^2+1) */ + +unsigned short CRC16Calculate(unsigned char *Buffer, int BufferSize) +{ + unsigned short CRCTable[0x100]; + unsigned short crc; + int i; + + /* First, initialise our CRCTable */ + for (i = 0; i < 0x100; i++) { + unsigned short r = i; + unsigned int j; + + for (j = 0; j < 8; j++) { + if (r & 1) + r = (r >> 1) ^ CRCPOLY; + else + r >>= 1; + } + CRCTable[i] = r; + } + + /* now go over the entire Buffer */ + crc = 0; + for (i = 0; i < BufferSize; i++) + crc = CRCTable[(crc ^ Buffer[i]) & 0xFF] ^ (crc >> 8); + + return crc; +} + +/* + * Bit handling code. + */ +static unsigned char *CompressedBuffer; +static int CompressedSize; +static int CompressedOffset; + +static unsigned short bitbuf; +static unsigned char subbitbuf, bitcount; + +static void BitBufInit(unsigned char *Buffer, int BufferSize) +{ + CompressedBuffer = Buffer; + CompressedOffset = 0; + CompressedSize = BufferSize; + + bitbuf = 0; + subbitbuf = 0; + bitcount = 0; +} + +static void fillbuf(unsigned char n) +{ /* Shift bitbuf n bits left, read n bits */ + while (n > bitcount) { + n -= bitcount; + bitbuf = (bitbuf << bitcount) + (subbitbuf >> (8 - bitcount)); + + if (CompressedOffset < CompressedSize) { + subbitbuf = CompressedBuffer[CompressedOffset]; + CompressedOffset++; + } else + subbitbuf = 0; + + bitcount = 8; + } + bitcount -= n; + bitbuf = (bitbuf << n) + (subbitbuf >> (8 - n)); + subbitbuf <<= n; +} + +static unsigned short getbits(unsigned char n) +{ + unsigned short x; + + x = bitbuf >> (16 - n); + fillbuf(n); + + return x; +} + +static unsigned short peekbits(unsigned char n) +{ + unsigned short x; + + x = bitbuf >> (16 - n); + + return x; +} + +/* + * + * LHA extraction. + * + */ +#define MIN(a,b) ((a) <= (b) ? (a) : (b)) + +#define LZHUFF5_DICBIT 13 /* 2^13 = 8KB sliding dictionary */ +#define MAXMATCH 256 /* formerly F (not more than 255 + 1) */ +#define THRESHOLD 3 /* choose optimal value */ +#define NP (LZHUFF5_DICBIT + 1) +#define NT (16 + 3) /* USHORT + THRESHOLD */ +#define NC (255 + MAXMATCH + 2 - THRESHOLD) + +#define PBIT 4 /* smallest integer such that (1 << PBIT) > * NP */ +#define TBIT 5 /* smallest integer such that (1 << TBIT) > * NT */ +#define CBIT 9 /* smallest integer such that (1 << CBIT) > * NC */ + +/* #if NT > NP #define NPT NT #else #define NPT NP #endif */ +#define NPT 0x80 + +static unsigned short left[2 * NC - 1], right[2 * NC - 1]; + +static unsigned short c_table[4096]; /* decode */ +static unsigned short pt_table[256]; /* decode */ + +static unsigned char c_len[NC]; +static unsigned char pt_len[NPT]; + +static int +make_table(short nchar, unsigned char bitlen[], short tablebits, + unsigned short table[]) +{ + unsigned short count[17]; /* count of bitlen */ + unsigned short weight[17]; /* 0x10000ul >> bitlen */ + unsigned short start[17]; /* first code of bitlen */ + unsigned short total; + unsigned int i, l; + int j, k, m, n, avail; + unsigned short *p; + + avail = nchar; + + /* initialize */ + for (i = 1; i <= 16; i++) { + count[i] = 0; + weight[i] = 1 << (16 - i); + } + + /* count */ + for (i = 0; i < nchar; i++) { + if (bitlen[i] > 16) { + /* CVE-2006-4335 */ + fprintf(stderr, "Error: Bad table (case a)\n"); + return -1; + } else + count[bitlen[i]]++; + } + + /* calculate first code */ + total = 0; + for (i = 1; i <= 16; i++) { + start[i] = total; + total += weight[i] * count[i]; + } + if (((total & 0xffff) != 0) || (tablebits > 16)) { /* 16 for weight below */ + fprintf(stderr, "Error: make_table(): Bad table (case b)\n"); + return -1; + } + + /* shift data for make table. */ + m = 16 - tablebits; + for (i = 1; i <= tablebits; i++) { + start[i] >>= m; + weight[i] >>= m; + } + + /* initialize */ + j = start[tablebits + 1] >> m; + k = MIN(1 << tablebits, 4096); + if (j != 0) + for (i = j; i < k; i++) + table[i] = 0; + + /* create table and tree */ + for (j = 0; j < nchar; j++) { + k = bitlen[j]; + if (k == 0) + continue; + l = start[k] + weight[k]; + if (k <= tablebits) { + /* code in table */ + l = MIN(l, 4096); + for (i = start[k]; i < l; i++) + table[i] = j; + } else { + /* code not in table */ + i = start[k]; + if ((i >> m) > 4096) { + /* CVE-2006-4337 */ + fprintf(stderr, "Error: Bad table (case c)"); + exit(1); + } + p = &table[i >> m]; + i <<= tablebits; + n = k - tablebits; + /* make tree (n length) */ + while (--n >= 0) { + if (*p == 0) { + right[avail] = left[avail] = 0; + *p = avail++; + } + if (i & 0x8000) + p = &right[*p]; + else + p = &left[*p]; + i <<= 1; + } + *p = j; + } + start[k] = l; + } + return 0; +} + +static int read_pt_len(short nn, short nbit, short i_special) +{ + int i, c, n; + + n = getbits(nbit); + if (n == 0) { + c = getbits(nbit); + for (i = 0; i < nn; i++) + pt_len[i] = 0; + for (i = 0; i < 256; i++) + pt_table[i] = c; + } else { + i = 0; + while (i < MIN(n, NPT)) { + c = peekbits(3); + if (c != 7) + fillbuf(3); + else { + unsigned short mask = 1 << (16 - 4); + while (mask & bitbuf) { + mask >>= 1; + c++; + } + fillbuf(c - 3); + } + + pt_len[i++] = c; + if (i == i_special) { + c = getbits(2); + while (--c >= 0 && i < NPT) + pt_len[i++] = 0; + } + } + while (i < nn) + pt_len[i++] = 0; + + if (make_table(nn, pt_len, 8, pt_table) == -1) + return -1; + } + return 0; +} + +static int read_c_len(void) +{ + short i, c, n; + + n = getbits(CBIT); + if (n == 0) { + c = getbits(CBIT); + for (i = 0; i < NC; i++) + c_len[i] = 0; + for (i = 0; i < 4096; i++) + c_table[i] = c; + } else { + i = 0; + while (i < MIN(n, NC)) { + c = pt_table[peekbits(8)]; + if (c >= NT) { + unsigned short mask = 1 << (16 - 9); + do { + if (bitbuf & mask) + c = right[c]; + else + c = left[c]; + mask >>= 1; + } while (c >= NT && (mask || c != left[c])); /* CVE-2006-4338 */ + } + fillbuf(pt_len[c]); + if (c <= 2) { + if (c == 0) + c = 1; + else if (c == 1) + c = getbits(4) + 3; + else + c = getbits(CBIT) + 20; + while (--c >= 0) + c_len[i++] = 0; + } else + c_len[i++] = c - 2; + } + while (i < NC) + c_len[i++] = 0; + + if (make_table(NC, c_len, 12, c_table) == -1) + return -1; + } + return 0; +} + +static unsigned short decode_c_st1(void) +{ + unsigned short j, mask; + + j = c_table[peekbits(12)]; + if (j < NC) + fillbuf(c_len[j]); + else { + fillbuf(12); + mask = 1 << (16 - 1); + do { + if (bitbuf & mask) + j = right[j]; + else + j = left[j]; + mask >>= 1; + } while (j >= NC && (mask || j != left[j])); /* CVE-2006-4338 */ + fillbuf(c_len[j] - 12); + } + return j; +} + +static unsigned short decode_p_st1(void) +{ + unsigned short j, mask; + + j = pt_table[peekbits(8)]; + if (j < NP) + fillbuf(pt_len[j]); + else { + fillbuf(8); + mask = 1 << (16 - 1); + do { + if (bitbuf & mask) + j = right[j]; + else + j = left[j]; + mask >>= 1; + } while (j >= NP && (mask || j != left[j])); /* CVE-2006-4338 */ + fillbuf(pt_len[j] - 8); + } + if (j != 0) + j = (1 << (j - 1)) + getbits(j - 1); + return j; +} + +int +LH5Decode(unsigned char *PackedBuffer, int PackedBufferSize, + unsigned char *OutputBuffer, int OutputBufferSize) +{ + unsigned short blocksize = 0; + unsigned int i, c; + int n = 0; + + BitBufInit(PackedBuffer, PackedBufferSize); + fillbuf(2 * 8); + + while (n < OutputBufferSize) { + if (blocksize == 0) { + blocksize = getbits(16); + + if (read_pt_len(NT, TBIT, 3) == -1) + return -1; + if (read_c_len() == -1) + return -1; + if (read_pt_len(NP, PBIT, -1) == -1) + return -1; + } + blocksize--; + c = decode_c_st1(); + + if (c < 256) + OutputBuffer[n++] = c; + else { + int length = c - 256 + THRESHOLD; + int offset = 1 + decode_p_st1(); + + if (offset > n) + return -1; + + for (i = 0; i < length; i++) { + OutputBuffer[n] = OutputBuffer[n - offset]; + n++; + } + } + } + return 0; +} diff --git a/bios_extract/src/lh5_extract.h b/bios_extract/src/lh5_extract.h new file mode 100644 index 0000000..648cf58 --- /dev/null +++ b/bios_extract/src/lh5_extract.h @@ -0,0 +1,36 @@ +/* + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef LH5_EXTRACT_H +#define LH5_EXTRACT_H + +unsigned int LH5HeaderParse(unsigned char *Buffer, int BufferSize, + unsigned int *original_size, + unsigned int *packed_size, + char **name, unsigned short *crc); + +unsigned short CRC16Calculate(unsigned char *Buffer, int BufferSize); + +int LH5Decode(unsigned char *PackedBuffer, int PackedBufferSize, + unsigned char *OutputBuffer, int OutputBufferSize); + +int unlzari(unsigned char *in, int insz, unsigned char *out, int outsz, char common); + +int unlzh(unsigned char *in, int insz, unsigned char *out, int outsz); + +#endif /* LH5_EXTRACT_H */ diff --git a/bios_extract/src/lh5_test.c b/bios_extract/src/lh5_test.c new file mode 100644 index 0000000..2f5072f --- /dev/null +++ b/bios_extract/src/lh5_test.c @@ -0,0 +1,137 @@ +/* + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Test/Example code for LH5 extraction. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lh5_extract.h" + +int main(int argc, char *argv[]) +{ + char *filename; + unsigned short header_crc; + unsigned int header_size, original_size, packed_size; + int infd, outfd; + int LHABufferSize = 0; + unsigned char *LHABuffer, *OutBuffer; + + if (argc != 2) { + fprintf(stderr, "Error: archive file not specified\n"); + return 1; + } + + /* open archive file */ + infd = open(argv[1], O_RDONLY); + if (infd == -1) { + fprintf(stderr, "Error: Failed to open \"%s\": %s\n", argv[1], + strerror(errno)); + return 1; + } + + LHABufferSize = lseek(infd, 0, SEEK_END); + if (LHABufferSize < 0) { + fprintf(stderr, "Error: Failed to lseek \"%s\": %s\n", argv[1], + strerror(errno)); + return 1; + } + + LHABuffer = mmap(NULL, LHABufferSize, PROT_READ, MAP_PRIVATE, infd, 0); + if (LHABuffer == ((void *)-1)) { + fprintf(stderr, "Error: Failed to mmap %s: %s\n", argv[1], + strerror(errno)); + return 1; + } + + header_size = LH5HeaderParse(LHABuffer, LHABufferSize, &original_size, + &packed_size, &filename, &header_crc); + if (!header_size) + return 1; + + if ((header_size + packed_size) < LHABufferSize) { + fprintf(stderr, "Error: LHA archive is bigger than \"%s\".\n", + argv[1]); + return 1; + } + + outfd = open(filename, O_RDWR | O_TRUNC | O_CREAT, S_IRWXU); + if (outfd == -1) { + fprintf(stderr, "Error: Failed to open \"%s\": %s\n", filename, + strerror(errno)); + return 1; + } + + /* grow file */ + if (lseek(outfd, original_size - 1, SEEK_SET) == -1) { + fprintf(stderr, "Error: Failed to grow \"%s\": %s\n", filename, + strerror(errno)); + return 1; + } + + if (write(outfd, "", 1) != 1) { + fprintf(stderr, "Error: Failed to write to \"%s\": %s\n", + filename, strerror(errno)); + return 1; + } + + OutBuffer = + mmap(NULL, original_size, PROT_READ | PROT_WRITE, MAP_SHARED, outfd, + 0); + if (OutBuffer == ((void *)-1)) { + fprintf(stderr, "Error: Failed to mmap %s: %s\n", filename, + strerror(errno)); + return 1; + } + + LH5Decode(LHABuffer + header_size, packed_size, OutBuffer, + original_size); + + if (CRC16Calculate(OutBuffer, original_size) != header_crc) { + fprintf(stderr, "Warning: invalid CRC on \"%s\"\n", filename); + return 1; + } + + if (munmap(OutBuffer, original_size)) + fprintf(stderr, "Warning: Failed to munmap \"%s\": %s\n", + filename, strerror(errno)); + + if (close(outfd)) + fprintf(stderr, "Warning: Failed to close \"%s\": %s\n", + filename, strerror(errno)); + + free(filename); + + /* get rid of our input file */ + if (munmap(LHABuffer, LHABufferSize)) + fprintf(stderr, "Warning: Failed to munmap \"%s\": %s\n", + argv[1], strerror(errno)); + + if (close(infd)) + fprintf(stderr, "Warning: Failed to close \"%s\": %s\n", + argv[1], strerror(errno)); + + return 0; +} diff --git a/bios_extract/src/lzari_extract.c b/bios_extract/src/lzari_extract.c new file mode 100644 index 0000000..4742d30 --- /dev/null +++ b/bios_extract/src/lzari_extract.c @@ -0,0 +1,263 @@ +// modified by Luigi Auriemma for memory2memory decompression and removing encoding +/************************************************************** + LZARI.C -- A Data Compression Program + (tab = 4 spaces) +*************************************************************** + 4/7/1989 Haruhiko Okumura + Use, distribute, and modify this program freely. + Please send me your improved versions. + PC-VAN SCIENCE + NIFTY-Serve PAF01022 + CompuServe 74050,1022 +**************************************************************/ +#include +#include +#include +#include + +/********** Bit I/O **********/ + +static +unsigned char *infile = NULL, + *infilel = NULL, + *outfile = NULL, + *outfilel = NULL; +static int xgetc(void *skip) { + if(infile >= infilel) return(-1); + return(*infile++); +} +static int xputc(int chr, void *skip) { + if(outfile >= outfilel) return(-1); + *outfile++ = chr; + return(chr); +} +static +unsigned long int textsize = 0; + +static +int GetBit(void) /* Get one bit (0 or 1) */ +{ + static unsigned int buffer, mask = 0; + + if ((mask >>= 1) == 0) { + buffer = xgetc(infile); mask = 128; + } + return ((buffer & mask) != 0); +} + +/********** LZSS with multiple binary trees **********/ + +#define N 4096 /* size of ring buffer */ +#define F 60 /* upper limit for match_length */ +#define THRESHOLD 2 /* encode string into position and length + if match_length is greater than this */ +#define NIL N /* index for root of binary search trees */ + +unsigned char text_buf[N + F - 1]; /* ring buffer of size N, + with extra F-1 bytes to facilitate string comparison */ +int match_position, match_length, /* of longest match. These are + set by the InsertNode() procedure. */ + lson[N + 1], rson[N + 257], dad[N + 1]; /* left & right children & + parents -- These constitute binary search trees. */ + +/********** Arithmetic Compression **********/ + +/* If you are not familiar with arithmetic compression, you should read + I. E. Witten, R. M. Neal, and J. G. Cleary, + Communications of the ACM, Vol. 30, pp. 520-540 (1987), + from which much have been borrowed. */ + +#define M 15 + +/* Q1 (= 2 to the M) must be sufficiently large, but not so + large as the unsigned long 4 * Q1 * (Q1 - 1) overflows. */ + +#define Q1 (1UL << M) +#define Q2 (2 * Q1) +#define Q3 (3 * Q1) +#define Q4 (4 * Q1) +#define MAX_CUM (Q1 - 1) + +#define N_CHAR (256 - THRESHOLD + F) + /* character code = 0, 1, ..., N_CHAR - 1 */ + +unsigned long int low = 0, high = Q4, value = 0; +int shifts = 0; /* counts for magnifying low and high around Q2 */ +int char_to_sym[N_CHAR], sym_to_char[N_CHAR + 1]; +unsigned int + sym_freq[N_CHAR + 1], /* frequency for symbols */ + sym_cum[N_CHAR + 1], /* cumulative freq for symbols */ + position_cum[N + 1]; /* cumulative freq for positions */ + +static +void StartModel(void) /* Initialize model */ +{ + int ch, sym, i; + + sym_cum[N_CHAR] = 0; + for (sym = N_CHAR; sym >= 1; sym--) { + ch = sym - 1; + char_to_sym[ch] = sym; sym_to_char[sym] = ch; + sym_freq[sym] = 1; + sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym]; + } + sym_freq[0] = 0; /* sentinel (!= sym_freq[1]) */ + position_cum[N] = 0; + for (i = N; i >= 1; i--) + position_cum[i - 1] = position_cum[i] + 10000 / (i + 200); + /* empirical distribution function (quite tentative) */ + /* Please devise a better mechanism! */ +} + +static +void UpdateModel(int sym) +{ + int i, c, ch_i, ch_sym; + + if (sym_cum[0] >= MAX_CUM) { + c = 0; + for (i = N_CHAR; i > 0; i--) { + sym_cum[i] = c; + c += (sym_freq[i] = (sym_freq[i] + 1) >> 1); + } + sym_cum[0] = c; + } + for (i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ; + if (i < sym) { + ch_i = sym_to_char[i]; ch_sym = sym_to_char[sym]; + sym_to_char[i] = ch_sym; sym_to_char[sym] = ch_i; + char_to_sym[ch_i] = sym; char_to_sym[ch_sym] = i; + } + sym_freq[i]++; + while (--i >= 0) sym_cum[i]++; +} + +static +int BinarySearchSym(unsigned int x) + /* 1 if x >= sym_cum[1], + N_CHAR if sym_cum[N_CHAR] > x, + i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */ +{ + int i, j, k; + + i = 1; j = N_CHAR; + while (i < j) { + k = (i + j) / 2; + if (sym_cum[k] > x) i = k + 1; else j = k; + } + return i; +} + +static +int BinarySearchPos(unsigned int x) + /* 0 if x >= position_cum[1], + N - 1 if position_cum[N] > x, + i such that position_cum[i] > x >= position_cum[i + 1] otherwise */ +{ + int i, j, k; + + i = 1; j = N; + while (i < j) { + k = (i + j) / 2; + if (position_cum[k] > x) i = k + 1; else j = k; + } + return i - 1; +} + +static +void StartDecode(void) +{ + int i; + + for (i = 0; i < M + 2; i++) + value = 2 * value + GetBit(); +} + +static +int DecodeChar(void) +{ + int sym, ch; + unsigned long int range; + + range = high - low; + sym = BinarySearchSym((unsigned int) + (((value - low + 1) * sym_cum[0] - 1) / range)); + high = low + (range * sym_cum[sym - 1]) / sym_cum[0]; + low += (range * sym_cum[sym ]) / sym_cum[0]; + for ( ; ; ) { + if (low >= Q2) { + value -= Q2; low -= Q2; high -= Q2; + } else if (low >= Q1 && high <= Q3) { + value -= Q1; low -= Q1; high -= Q1; + } else if (high > Q2) break; + low += low; high += high; + value = 2 * value + GetBit(); + } + ch = sym_to_char[sym]; + UpdateModel(sym); + return ch; +} + +static +int DecodePosition(void) +{ + int position; + unsigned long int range; + + range = high - low; + position = BinarySearchPos((unsigned int) + (((value - low + 1) * position_cum[0] - 1) / range)); + high = low + (range * position_cum[position ]) / position_cum[0]; + low += (range * position_cum[position + 1]) / position_cum[0]; + for ( ; ; ) { + if (low >= Q2) { + value -= Q2; low -= Q2; high -= Q2; + } else if (low >= Q1 && high <= Q3) { + value -= Q1; low -= Q1; high -= Q1; + } else if (high > Q2) break; + low += low; high += high; + value = 2 * value + GetBit(); + } + return position; +} + +/********** Encode and Decode **********/ + +int unlzari(unsigned char *in, int insz, unsigned char *out, int outsz, char common) { + int i, j, k, r, c; + unsigned long int count; + + infile = in; + infilel = in + insz; + outfile = out; + outfilel = out + outsz; + + textsize = (xgetc(infile)); + textsize |= (xgetc(infile) << 8); + textsize |= (xgetc(infile) << 16); + textsize |= (xgetc(infile) << 24); + //if (textsize == 0) return(-1); + if (textsize == 0) return(0); + if (textsize < 0) return(-1); + + StartDecode(); StartModel(); + for (i = 0; i < N - F; i++) text_buf[i] = common; + r = N - F; + for (count = 0; count < textsize; ) { + if(infile >= infilel) break; + c = DecodeChar(); + if (c < 256) { + xputc(c, outfile); text_buf[r++] = c; + r &= (N - 1); count++; + } else { + i = (r - DecodePosition() - 1) & (N - 1); + j = c - 255 + THRESHOLD; + for (k = 0; k < j; k++) { + c = text_buf[(i + k) & (N - 1)]; + xputc(c, outfile); text_buf[r++] = c; + r &= (N - 1); count++; + } + } + } + return(outfile - out); +} diff --git a/bios_extract/src/lzhuf_extract.c b/bios_extract/src/lzhuf_extract.c new file mode 100644 index 0000000..c3b12fc --- /dev/null +++ b/bios_extract/src/lzhuf_extract.c @@ -0,0 +1,394 @@ +// modified by Luigi Auriemma for memory2memory decompression and removing encoding +/************************************************************** + lzhuf.c + written by Haruyasu Yoshizaki 1988/11/20 + some minor changes 1989/04/06 + comments translated by Haruhiko Okumura 1989/04/07 + getbit and getbyte modified 1990/03/23 by Paul Edwards + so that they would work on machines where integers are + not necessarily 16 bits (although ANSI guarantees a + minimum of 16). This program has compiled and run with + no errors under Turbo C 2.0, Power C, and SAS/C 4.5 + (running on an IBM mainframe under MVS/XA 2.2). Could + people please use YYYY/MM/DD date format so that everyone + in the world can know what format the date is in? + external storage of filesize changed 1990/04/18 by Paul Edwards to + Intel's "little endian" rather than a machine-dependant style so + that files produced on one machine with lzhuf can be decoded on + any other. "little endian" style was chosen since lzhuf + originated on PC's, and therefore they should dictate the + standard. + initialization of something predicting spaces changed 1990/04/22 by + Paul Edwards so that when the compressed file is taken somewhere + else, it will decode properly, without changing ascii spaces to + ebcdic spaces. This was done by changing the ' ' (space literal) + to 0x20 (which is the far most likely character to occur, if you + don't know what environment it will be running on. +**************************************************************/ +#include +#include +#include +#include + +static +unsigned char *infile = NULL, + *infilel = NULL, + *outfile = NULL, + *outfilel = NULL; +static int xgetc(void *skip) { + if(infile >= infilel) return(-1); + return(*infile++); +} +static int xputc(int chr, void *skip) { + if(outfile >= outfilel) return(-1); + *outfile++ = chr; + return(chr); +} +static +unsigned long int textsize = 0; + +/********** LZSS compression **********/ + +#define N 4096 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +static +unsigned char + text_buf[N + F - 1]; + +/* Huffman coding */ + +#define N_CHAR (256 - THRESHOLD + F) + /* kinds of characters (character code = 0..N_CHAR-1) */ +#define T (N_CHAR * 2 - 1) /* size of table */ +#define R (T - 1) /* position of root */ +#define MAX_FREQ 0x8000 /* updates tree when the */ +typedef unsigned char uchar; + + +/* table for encoding and decoding the upper 6 bits of position */ + +/* for encoding */ +uchar p_len[64] = { + 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +uchar p_code[64] = { + 0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68, + 0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C, + 0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC, + 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, + 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, + 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +/* for decoding */ +uchar d_code[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, + 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, + 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, + 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, + 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, + 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, + 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, + 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, + 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, +}; + +uchar d_len[256] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +}; + +unsigned freq[T + 1]; /* frequency table */ + +int prnt[T + N_CHAR]; /* pointers to parent nodes, except for the */ + /* elements [T..T + N_CHAR - 1] which are used to get */ + /* the positions of leaves corresponding to the codes. */ + +int son[T]; /* pointers to child nodes (son[], son[] + 1) */ + +unsigned getbuf = 0; +uchar getlen = 0; + +static int GetBit(void) /* get one bit */ +{ + unsigned i; + + while (getlen <= 8) { + if ((int)(i = xgetc(infile)) < 0) return(0); //i = 0; + getbuf |= i << (8 - getlen); + getlen += 8; + } + i = getbuf; + getbuf <<= 1; + getlen--; + return (int)((i & 0x8000) >> 15); +} + +static int GetByte(void) /* get one byte */ +{ + unsigned i; + + while (getlen <= 8) { + if ((int)(i = xgetc(infile)) < 0) return(0); //i = 0; + getbuf |= i << (8 - getlen); + getlen += 8; + } + i = getbuf; + getbuf <<= 8; + getlen -= 8; + return (int)((i & 0xff00) >> 8); +} + +unsigned putbuf = 0; +uchar putlen = 0; + +/* initialization of tree */ + +static void StartHuff(void) +{ + int i, j; + + for (i = 0; i < N_CHAR; i++) { + freq[i] = 1; + son[i] = i + T; + prnt[i + T] = i; + } + i = 0; j = N_CHAR; + while (j <= R) { + freq[j] = freq[i] + freq[i + 1]; + son[j] = i; + prnt[i] = prnt[i + 1] = j; + i += 2; j++; + } + freq[T] = 0xffff; + prnt[R] = 0; +} + + +/* reconstruction of tree */ + +static void reconst(void) +{ + int i, j, k; + unsigned f, l; + + /* collect leaf nodes in the first half of the table */ + /* and replace the freq by (freq + 1) / 2. */ + j = 0; + for (i = 0; i < T; i++) { + if (son[i] >= T) { + freq[j] = (freq[i] + 1) / 2; + son[j] = son[i]; + j++; + } + } + /* begin constructing tree by connecting sons */ + for (i = 0, j = N_CHAR; j < T; i += 2, j++) { + k = i + 1; + f = freq[j] = freq[i] + freq[k]; + for (k = j - 1; f < freq[k]; k--); + k++; + l = (j - k) * 2; + memmove(&freq[k + 1], &freq[k], l); + freq[k] = f; + memmove(&son[k + 1], &son[k], l); + son[k] = i; + } + /* connect prnt */ + for (i = 0; i < T; i++) { + if ((k = son[i]) >= T) { + prnt[k] = i; + } else { + prnt[k] = prnt[k + 1] = i; + } + } +} + + +/* increment frequency of given code by one, and update tree */ + +static void update(int c) +{ + int i, j, k, l; + + if (freq[R] == MAX_FREQ) { + reconst(); + } + c = prnt[c + T]; + do { + k = ++freq[c]; + + /* if the order is disturbed, exchange nodes */ + if ((unsigned)k > freq[l = c + 1]) { + while ((unsigned)k > freq[++l]); + l--; + freq[c] = freq[l]; + freq[l] = k; + + i = son[c]; + prnt[i] = l; + if (i < T) prnt[i + 1] = l; + + j = son[l]; + son[l] = i; + + prnt[j] = c; + if (j < T) prnt[j + 1] = c; + son[c] = j; + + c = l; + } + } while ((c = prnt[c]) != 0); /* repeat up to root */ +} + +unsigned code, len; + +static int DecodeChar(void) +{ + unsigned c; + + c = son[R]; + + /* travel from root to leaf, */ + /* choosing the smaller child node (son[]) if the read bit is 0, */ + /* the bigger (son[]+1} if 1 */ + while (c < T) { + c += GetBit(); + c = son[c]; + } + c -= T; + update(c); + return (int)c; +} + +static int DecodePosition(void) +{ + unsigned i, j, c; + + /* recover upper 6 bits from table */ + i = GetByte(); + c = (unsigned)d_code[i] << 6; + j = d_len[i]; + + /* read lower 6 bits verbatim */ + j -= 2; + while (j--) { + i = (i << 1) + GetBit(); + } + return (int)(c | (i & 0x3f)); +} + +/* compression */ + +int unlzh(unsigned char *in, int insz, unsigned char *out, int outsz) { + int i, j, k, r, c; + unsigned long int count; + + infile = in; + infilel = in + insz; + outfile = out; + outfilel = out + outsz; + + /*textsize = (xgetc(infile)); + textsize |= (xgetc(infile) << 8); + textsize |= (xgetc(infile) << 16); + textsize |= (xgetc(infile) << 24); + if (textsize == 0) + return(-1);*/ + textsize = outsz; + + StartHuff(); + for (i = 0; i < N - F; i++) + text_buf[i] = 0x20; + r = N - F; + for (count = 0; count < textsize; ) { + c = DecodeChar(); + if (c < 256) { + if (xputc(c, outfile) == -1) { + return(-1); + } + text_buf[r++] = (unsigned char)c; + r &= (N - 1); + count++; + } else { + i = (r - DecodePosition() - 1) & (N - 1); + j = c - 255 + THRESHOLD; + for (k = 0; k < j; k++) { + c = text_buf[(i + k) & (N - 1)]; + if (xputc(c, outfile) == -1) { + return(-1); + } + text_buf[r++] = (unsigned char)c; + r &= (N - 1); + count++; + } + } + } + return(outfile - out); +} diff --git a/bios_extract/src/lzss_extract.c b/bios_extract/src/lzss_extract.c new file mode 100644 index 0000000..8e0c53b --- /dev/null +++ b/bios_extract/src/lzss_extract.c @@ -0,0 +1,92 @@ +/* + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "lzss_extract.h" + +static inline int +LZSSBufferWrite(int fd, unsigned char *Buffer, int *BufferCount, + unsigned char value) +{ + Buffer[*BufferCount] = value; + *BufferCount += 1; + + if (*BufferCount == 0x1000) { + if (write(fd, Buffer, 0x1000) != 0x1000) { + fprintf(stderr, "Error writing to output file: %s", + strerror(errno)); + return 1; + } + *BufferCount = 0; + } + + return 0; +} + +int LZSSExtract(unsigned char *Input, int InputSize, int fd) +{ + unsigned char Buffer[0x1000]; + unsigned short BitBuffer = 0; + int i = 0, k, BitCount = 8, BufferCount = 0; + + while (i < InputSize) { + + if (BitCount == 8) { + BitBuffer = Input[i]; + BitCount = -1; + } else if ((BitBuffer >> BitCount) & 0x01) { + if (LZSSBufferWrite(fd, Buffer, &BufferCount, Input[i])) + return 1; + } else if ((i + 1) < InputSize) { + int offset = + ((Input[i] | ((Input[i + 1] & 0xF0) << 4)) - + 0xFEE) & 0xFFF; + int length = (Input[i + 1] & 0x0F) + 3; + + for (k = 0; k < length; k++) { + if (LZSSBufferWrite + (fd, Buffer, &BufferCount, + Buffer[(offset + k) & 0xFFF])) + return 1; + } + i++; + } else { + fprintf(stderr, + "Error: requesting data beyond end of input file.\n"); + return 1; + } + + i++; + BitCount++; + } + + if (BufferCount) { + if (write(fd, Buffer, BufferCount) != BufferCount) { + fprintf(stderr, "Error writing to output file: %s", + strerror(errno)); + return 1; + } + } + + return 0; +} diff --git a/bios_extract/src/lzss_extract.h b/bios_extract/src/lzss_extract.h new file mode 100644 index 0000000..f97103f --- /dev/null +++ b/bios_extract/src/lzss_extract.h @@ -0,0 +1,24 @@ +/* + * Copyright 2009 Luc Verhaegen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef LZSS_EXTRACT_H +#define LZSS_EXTRACT_H + +int LZSSExtract(unsigned char *Input, int InputSize, int fd); + +#endif /* LZSS_EXTRACT_H */ diff --git a/bios_extract/src/phoenix.c b/bios_extract/src/phoenix.c new file mode 100644 index 0000000..563b343 --- /dev/null +++ b/bios_extract/src/phoenix.c @@ -0,0 +1,1244 @@ +/* + * Copyright 2009 Luc Verhaegen + * Copyright 2000-2003 Anthony Borisow + * Copyright 2021 RichardG + * Portions based on PHOEDECO (c) 1998-2006 Veit Kannegieser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE 1 /* for memmem */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "bios_extract.h" +#include "lh5_extract.h" +#include "lzss_extract.h" + +struct bcpHeader { + char signature[6]; + uint8_t major_revision; + uint8_t minor_revision; + uint16_t length; +}; + +/* Our own structure just to store important parameters */ +struct Phoenix { + uint8_t version; + uint8_t type; + uint8_t compression; + unsigned char commonCharacterLZSS; +}; + +static struct Phoenix phx = { 0, 0, 0, ' ' }; + +#define COMP_LZSS 0 +#define COMP_LZARI 1 +#define COMP_LZHUF 2 +#define COMP_LZINT 3 + +struct bcpCompress { + struct bcpHeader head; + uint8_t flags; + uint8_t alg; + union { + struct { + uint16_t unc_start_offset; + uint32_t size_comp_data; + uint16_t bcpiRamBiosStart; + uint16_t bcpiWorkAreaStart; + uint16_t bcpiLowMemStart; + uint16_t bcpiLowMemSize; + uint8_t commonCharacterLZSS; + uint16_t oldRamBiosStart; + uint16_t oldSetupScanStart; + uint16_t oldSetupScanSize; + } main; + struct { + uint32_t size_comp_data; + uint16_t unc_start_offset; + uint16_t bcpiWorkAreaStart; + uint8_t commonCharacterLZSS; + } alt; + }; + +}; + +#define GUID_FFVMODULE "FED91FBA-D37B-4EEA-8729-2EF29FB37A78" +#define GUID_ESCD "FD21E8FD-2525-4A95-BB90-47EC5763FF9E" +#define GUID_RAWCODE "F6AE0F63-5F8C-4316-A2EA-76B9AF762756" + +/* -------------- Phoenix module file type parsing -------------- */ + +/* See http://wiki.phoenix.com/wiki/index.php/EFI_FV_FILETYPE for + * additional information */ + +struct PhoenixFFVFileType { + uint8_t Id; + char *Name; +}; + +static struct PhoenixFFVFileType + PhoenixFFVFileTypes[] = { + {0x00, "ALL"}, + {0x01, "BIN"}, + {0x02, "SECTION"}, + {0x03, "CEIMAIN"}, + {0x04, "PEIMAIN"}, + {0x05, "DXEMAIN"}, + {0x06, "PEI"}, + {0x07, "DXE"}, + {0x08, "COMBINED_PEIM_DRIVER"}, + {0x09, "APP"}, + {0x0B, "FFV"}, + {0xC2, "CEI"}, + {0xC3, "XIP"}, + {0xC4, "BB"}, + {0xD0, "SDXE"}, + {0xD1, "DXESDXE"}, + {0xF0, "GAP"}, + {0, NULL}, +}; + +static char *get_file_type(uint8_t id) +{ + short i = 0; + while (PhoenixFFVFileTypes[i].Name != NULL) { + if (PhoenixFFVFileTypes[i].Id == id) + return PhoenixFFVFileTypes[i].Name; + i++; + } + return "UNKNOWN"; +} + +/* -------------- Phoenix section file type parsing -------------- */ + +/* See http://wiki.phoenix.com/wiki/index.php/EFI_SECTION_TYPE for + * additional information */ + +struct PhoenixFFVSectionType { + uint8_t Id; + char *Name; +}; + +static struct PhoenixFFVSectionType PhoenixFFVSectionTypes[] = { + {0x01, "COMPRESSION"}, + {0x02, "GUID_DEFINED"}, + {0x10, "PE32"}, + {0x11, "PIC"}, + {0x12, "TE"}, + {0x13, "DXE_DEPEX"}, + {0x14, "VERSION"}, + {0x15, "USER_INTERFACE"}, + {0x16, "COMPATIBILITY16"}, + {0x17, "FIRMWARE_VOLUME_IMAGE"}, + {0x18, "FREEFORM_SUBTYPE_GUID"}, + {0x19, "BIN"}, + {0x1A, "PE64"}, + {0x1B, "PEI_DEPEX"}, + {0xC0, "SOURCECODE"}, + {0xC1, "FFV"}, + {0xC2, "RE32"}, + {0xC3, "XIP16"}, + {0xC4, "XIP32"}, + {0xC5, "XIP64"}, + {0xC6, "PLACE16"}, + {0xC7, "PLACE32"}, + {0xC8, "PLACE64"}, + {0xCF, "PCI_DEVICE"}, + {0xD0, "PDB"}, + {0, NULL}, +}; + +static char *get_section_type(uint8_t id) +{ + short i = 0; + while (PhoenixFFVSectionTypes[i].Name != NULL) { + if (PhoenixFFVSectionTypes[i].Id == id) + return PhoenixFFVSectionTypes[i].Name; + i++; + } + return "UNKNOWN"; +} + +/* -------------- Phoenix module name parsing -------------- */ + +struct PhoenixModuleName { + char Id; + char *Name; +}; + +static struct PhoenixModuleName PhoenixModuleNames[] = { + {'A', "acpi"}, + {'B', "bioscode"}, + {'C', "update"}, + {'D', "display"}, + {'E', "setup"}, + {'F', "font"}, + {'G', "decompcode"}, + {'I', "bootblock"}, + {'L', "logo"}, + {'M', "miser"}, + {'N', "rompilotload"}, + {'O', "network"}, + {'P', "rompilotinit"}, + {'R', "oprom"}, + {'S', "strings"}, + {'T', "template"}, + {'U', "user"}, + {'X', "romexec"}, + {'W', "wav"}, + {'H', "tcpa_H"}, /* TCPA (Trusted Computing), USBKCLIB? */ + {'K', "tcpa_K"}, /* TCPA (Trusted Computing), "AUTH"? */ + {'Q', "tcpa_Q"}, /* TCPA (Trusted Computing), "SROM"? */ + {'<', "tcpa_<"}, + {'*', "tcpa_*"}, + {'?', "tcpa_?"}, + {'$', "biosentry"}, + {'J', "SmartCardPAS"}, +}; + +struct PhoenixID { + char Name[6]; + uint16_t Flags; + uint16_t Length; +}; + +struct PhoenixModuleHeader { + uint32_t Previous; + uint8_t Signature[3]; + uint8_t Id; + uint8_t Type; + uint8_t HeadLen; + uint8_t Compression; + uint16_t Offset; + uint16_t Segment; + uint32_t ExpLen; + uint32_t FragLength; + uint32_t NextFrag; +}; + +struct PhoenixBCD6F1 { + uint8_t Signature[6]; + uint8_t HeadLen; + uint8_t DB07; + uint16_t Segment; + uint32_t ExpLen; + uint32_t FragLength; +}; + +struct PhoenixFFVModule { + uint8_t Signature; + uint8_t Flags; + uint16_t Checksum; /* Can be splitted to header and data checksums */ + uint16_t LengthLo; + uint8_t LengthHi; + uint8_t FileType; + char Name[16]; /* GUID name */ +}; + +struct PhoenixFFVSectionHeader { + uint16_t SizeLo; + uint8_t SizeHi; + uint8_t Type; +}; + +struct PhoenixFFVCompressionHeader { + uint16_t TotalLengthLo; + uint8_t TotalLengthHi; + uint8_t CompType; + uint16_t PackedLenLo; + uint8_t PackedLenHi; + uint8_t Unk2; + uint16_t RealLenLo; + uint8_t RealLenHi; + uint8_t Unk3; +}; + +static char *PhoenixModuleNameGet(char Id) +{ + int i; + + for (i = 0; PhoenixModuleNames[i].Name; i++) + if (PhoenixModuleNames[i].Id == Id) + return PhoenixModuleNames[i].Name; + return NULL; +} + +static void +phx_write_file(unsigned char *BIOSImage, char *filename, short filetype, + int offset, uint32_t length) +{ + int fd; + + if (filename[0] == '\0') { + sprintf(filename, "%s_0x%08x-0x%08x", get_file_type(filetype), + offset, offset + length); + } + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, + strerror(errno)); + return; + } + write(fd, BIOSImage + offset + 0x18, length - 0x18); + close(fd); +} + +/* ---------- Extraction code ---------- */ + +#define MODULE_SIGNATURE_INVALID(Module) (Module->Signature[0] || (Module->Signature[1] != 0x31) || (Module->Signature[2] != 0x31)) + +static int PhoenixModule(unsigned char *BIOSImage, int BIOSLength, int Offset) +{ + struct PhoenixModuleHeader *Module, *NewModule; + + char *filename, *ModuleName, IsFragment = 0; + unsigned char *Buffer; + unsigned char *ModuleData; + uint32_t Packed; + int fd, ExtractResult; + + Module = (struct PhoenixModuleHeader *)(BIOSImage + Offset); + + if (MODULE_SIGNATURE_INVALID(Module)) { + /* Additional checks (apparently for Intel BIOSes) ported from phoedeco */ + if (Offset > 0x20000) { + NewModule = (struct PhoenixModuleHeader *)(BIOSImage + Offset - 0x20000); + if (!MODULE_SIGNATURE_INVALID(NewModule)) { + Offset -= 0x20000; + Module = NewModule; + goto valid_signature; + } + } + if (Offset < (BIOSLength - 0x20000)) { + NewModule = (struct PhoenixModuleHeader *)(BIOSImage + Offset + 0x20000); + if (!MODULE_SIGNATURE_INVALID(NewModule)) { + Offset += 0x20000; + Module = NewModule; + goto valid_signature; + } + } + + fprintf(stderr, "Error: Invalid module signature at 0x%05X\n", + Offset); + return 0; + } + +valid_signature: + if ((Offset + Module->HeadLen + 4 + le32toh(Module->FragLength)) > + BIOSLength) { + fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n", + Offset); + return le32toh(Module->Previous); + } + + /* NextFrag is either the unpacked length again *or* the virtual address + of the next fragment. As long as BIOSses stay below 256MB, this works */ + if ((le32toh(Module->NextFrag) & 0xF0000000) == 0xF0000000) { + struct PhoenixFragment { + uint32_t NextFrag; + uint8_t NextBank; + uint32_t FragLength; + } *Fragment; + + int FragOffset; + uint32_t FragLength = le32toh(Module->FragLength); + + if (FragLength > le32toh(Module->ExpLen)) { + fprintf(stderr, + "Error: First fragment exceeds total size at %05X\n", + Offset); + /* Assume this is an invalid fragment module */ + goto BadFragment; + } + + /* This over-allocates, but the total compressed size is not available here */ + ModuleData = malloc(le32toh(Module->ExpLen)); + if (!ModuleData) { + fprintf(stderr, + "Error: Can't reassemble fragments, no memory for %d bytes\n", + le32toh(Module->ExpLen)); + /* Assume this is an invalid fragment module */ + goto BadFragment; + } + + memcpy(ModuleData, BIOSImage + Offset + Module->HeadLen, + FragLength); + + Packed = FragLength; + FragOffset = le32toh(Module->NextFrag) & (BIOSLength - 1); + + printf("extra fragments: "); + while (FragOffset) { + Fragment = + (struct PhoenixFragment *)(BIOSImage + FragOffset); + FragLength = le32toh(Fragment->FragLength); + printf("(%05X, %d bytes) ", FragOffset, FragLength); + + if (Packed + FragLength > le32toh(Module->ExpLen)) { + printf("\nFragment too big at %05X for %05X\n", + FragOffset, Offset); + free(ModuleData); + /* Assume this is an invalid fragment module */ + goto BadFragment; + } + memcpy(ModuleData + Packed, BIOSImage + FragOffset + 9, + FragLength); + Packed += FragLength; + FragOffset = + le32toh(Fragment->NextFrag) & (BIOSLength - 1); + } + printf("\n"); + + IsFragment = 1; + } else { +BadFragment: + ModuleData = BIOSImage + Offset + Module->HeadLen; + Packed = le32toh(Module->FragLength); + } + + ModuleName = PhoenixModuleNameGet(Module->Type); + if (ModuleName) { + filename = malloc(strlen(ModuleName) + 13); + sprintf(filename, "%s_%1d_%05X.rom", ModuleName, Module->Id, Offset + Module->HeadLen); + } else { + filename = malloc(15); + sprintf(filename, "%02X_%1d_%05X.rom", Module->Type, Module->Id, Offset + Module->HeadLen); + } + + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, + strerror(errno)); + free(filename); + if (IsFragment) + free(ModuleData); + return le32toh(Module->Previous); + } + + switch (Module->Compression) { + case 2: /* LZARI */ + printf("0x%05X (%6d bytes) -> %s\t(%d bytes) (LZARI)", + Offset + Module->HeadLen + 4, Packed, filename, + le32toh(Module->ExpLen)); + Buffer = MMapOutputFile(filename, le32toh(Module->ExpLen)); + if (!Buffer) + break; + + /* The first 4 bytes of the LZARI packing method is just the total + * expanded length; skip them */ + unlzari(ModuleData + 4, Packed - 4, Buffer, + le32toh(Module->ExpLen), phx.commonCharacterLZSS); + munmap(Buffer, le32toh(Module->ExpLen)); + break; + + case 3: /* LZSS */ + printf("0x%05X (%6d bytes) -> %s\t(%d bytes) (LZSS)", + Offset + Module->HeadLen + 4, Packed, filename, + le32toh(Module->ExpLen)); + + /* The first 4 bytes of the LZSS packing method is just the total + * expanded length; skip them */ + LZSSExtract(ModuleData + 4, Packed - 4, fd); + break; + + case 4: /* LZHUF */ + printf("0x%05X (%6d bytes) -> %s\t(%d bytes) (LZHUF)", + Offset + Module->HeadLen + 4, Packed, filename, + le32toh(Module->ExpLen)); + Buffer = MMapOutputFile(filename, le32toh(Module->ExpLen)); + if (!Buffer) + break; + + /* The first 4 bytes of the LZHUF packing method is just the total + * expanded length; skip them */ + unlzh(ModuleData + 4, Packed - 4, Buffer, + le32toh(Module->ExpLen)); + munmap(Buffer, le32toh(Module->ExpLen)); + break; + + case 5: /* LH5 */ + printf("0x%05X (%6d bytes) -> %s\t(%d bytes) (LH5)", + Offset + Module->HeadLen + 4, Packed, filename, + le32toh(Module->ExpLen)); + Buffer = MMapOutputFile(filename, le32toh(Module->ExpLen)); + if (!Buffer) + break; + + /* The first 4 bytes of the LH5 packing method is just the total + * expanded length; skip them */ + ExtractResult = LH5Decode(ModuleData + 4, Packed - 4, Buffer, + le32toh(Module->ExpLen)); + munmap(Buffer, le32toh(Module->ExpLen)); + /* Write compressed data if decompression failed. */ + if (ExtractResult) + goto Uncompressed; + break; + + case 0: /* not compressed at all */ + /* 2460v105: packed length 0, and phoedeco picks up the unpacked length + just use expanded length if packed length is invalid */ + if ((Packed == 0) || (Packed & 0xFF000000)) + Packed = le32toh(Module->ExpLen); + printf("0x%05X (%6d bytes) -> %s", Offset + Module->HeadLen, + Packed, filename); +Uncompressed: + write(fd, ModuleData, Packed); + break; + + default: + printf("0x%05X (%6d bytes) -> %s\t(%d bytes) (unsupported %d)", + Offset + Module->HeadLen, Packed, filename, + le32toh(Module->ExpLen), Module->Compression); + write(fd, ModuleData, Packed); + break; + } + + close(fd); + free(filename); + + if (IsFragment) + free(ModuleData); + + if (le16toh(Module->Offset) || le16toh(Module->Segment)) { + if (!Module->Compression) + printf("\t\t"); + printf("\t [0x%04X:0x%04X]\n", le16toh(Module->Segment) << 12, + le16toh(Module->Offset)); + } else + printf("\n"); + + return le32toh(Module->Previous); +} + +static int +PhoenixExtractFFV(unsigned char *BIOSImage, int BIOSLength, int Offset) +{ + struct PhoenixFFVSectionHeader *SectionHeader; + struct PhoenixFFVCompressionHeader *CompHeader; + struct PhoenixFFVModule *Module; + char Name[16], filename[24]; + char *ModuleName; + uint32_t Length, PackedLen, RealLen; + unsigned char *RealData; + + Module = (struct PhoenixFFVModule *)(BIOSImage + Offset); + + if (Module->Signature != 0xF8) { + /* ignore and move on to the next byte... */ + return 1; + } + + Length = ((le16toh(Module->LengthHi) << 16) | Module->LengthLo) - 1; + if ((Offset + Length) >= BIOSLength) { + fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n", + Offset); + return 1; + } + + /* TODO: Improve module name parsing */ + if (Module->FileType == 0xF0) { + strcpy(Name, "GAP"); + filename[0] = '\0'; + } else if ((uint8_t) Module->Name[8] != 0xFF) { + strcpy(Name, "GUID?"); + filename[0] = '\0'; + } else { + /* get rid of the pesky 0xFF in the middle of the name */ + memcpy(Name, Module->Name, 8); + memcpy(Name + 8, Module->Name + 9, 7); + Name[15] = '\0'; + + if (Name[0] == '_' && strlen(Name) == 4) { + ModuleName = PhoenixModuleNameGet(Name[1]); + if (ModuleName) { + snprintf(filename, sizeof(filename), + "%s_%c%c.rom", ModuleName, Name[2], + Name[3]); + } else { + snprintf(filename, sizeof(filename), "%s.rom", + Name); + } + } else { + strncpy(filename, Name, sizeof(filename)); + } + } + + printf("\t%-15s (%08X-%08X) %08X %02X %02X %s [%s]\n", + Name, Offset, Offset + Length, Length, Module->Flags, + Module->FileType, filename, get_file_type(Module->FileType)); + + switch (Module->FileType) { + case 0xF0: + break; + + /* ---------- SECTION file type ---------- */ + case 0x02: + SectionHeader = + (struct PhoenixFFVSectionHeader *)(BIOSImage + Offset + + 0x18); + if (Name[1] == 'G' || !*filename) { + break; + } + + /* COMPRESSION section */ + if (SectionHeader->Type == 0x01) { + CompHeader = + (struct PhoenixFFVCompressionHeader *)(BIOSImage + + Offset + + 0x18); + /* some blocks have a (8 byte?) header we need to skip */ + if (CompHeader->TotalLengthLo != Length - 0x18 + && CompHeader->Unk3) { + /* FIXME more advanced parsing of sections */ + CompHeader = + (struct PhoenixFFVCompressionHeader *) + ((unsigned char *)CompHeader + + CompHeader->TotalLengthLo); + } + PackedLen = + (CompHeader-> + PackedLenHi << 16) | CompHeader->PackedLenLo; + RealLen = + (CompHeader-> + RealLenHi << 16) | CompHeader->RealLenLo; + //printf("CompHeader->Type = %d\n", CompHeader->CompType); + + if (CompHeader->CompType == 0) /* Not compressed at all */ + break; + + if (!RealLen) /* FIXME temporary hack */ + break; + + RealData = MMapOutputFile(filename, RealLen); + if (!RealData) { + fprintf(stderr, + "Failed to mmap file for uncompressed data.\n"); + break; + } + if ((phx.compression == COMP_LZHUF) + || (phx.compression == COMP_LZINT)) { + if (LH5Decode + ((unsigned char *)CompHeader + + sizeof(struct PhoenixFFVCompressionHeader), + PackedLen, RealData, RealLen) == -1) { + munmap(RealData, RealLen); + fprintf(stderr, + "Failed to uncompress section with LHA5.\n"); + /* dump original section in this case */ + phx_write_file(BIOSImage, filename, + Module->FileType, Offset, + Length); + } else + printf("COMPRESSED\n"); + } else + printf("Unsupported compression!\n"); + munmap(RealData, RealLen); + break; + } + printf("\t\tSECTION: %s\n", + get_section_type(SectionHeader->Type)); + phx_write_file(BIOSImage, filename, Module->FileType, Offset, + Length); + break; + + default: + phx_write_file(BIOSImage, filename, Module->FileType, Offset, + Length); + break; + } + return Length; +} + +/* Parse initial volumedir layout: + * - 1 byte Type indicates either raw code or an FFV module + * - 4 byte Base provides the offset into the image to find the specified volume + * - 4 byte Length + */ +void +PhoenixVolume1(unsigned char *BIOSImage, int BIOSLength, int Offset, int ModLen) +{ + struct PhoenixVolumeDirEntry { + uint8_t Type; + uint32_t Base; + uint32_t Length; + } *Modules; + + char Name[16]; + int fd, HoleNum = 0; + uint8_t Type; + uint32_t Base, Length, NumModules, ModNum; + + Modules = (struct PhoenixVolumeDirEntry *)(BIOSImage + Offset + 0x18); + NumModules = (ModLen - 0x18) / sizeof(struct PhoenixVolumeDirEntry); + + printf("FFV modules: %u\n", NumModules); + + for (ModNum = 0; ModNum < NumModules; ModNum++) { + Type = Modules[ModNum].Type; + Base = Modules[ModNum].Base & (BIOSLength - 1); + Length = Modules[ModNum].Length - 1; + printf("[%2u]: (%08X-%08X) %02x\n", ModNum, Base, Base + Length, + Type); + + switch (Type) { + case 0x01: + printf("\tHole (raw code)\n"); + snprintf(Name, sizeof(Name), "hole_%02x.bin", + HoleNum++); + fd = open(Name, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, + "Error: unable to open %s: %s\n\n", + Name, strerror(errno)); + continue; + } + write(fd, BIOSImage + Base, Length); + close(fd); + break; + + case 0x02: + /* FFV modules */ + Offset = Base; + while (Offset < Base + Length) { + Offset += + PhoenixExtractFFV(BIOSImage, BIOSLength, + Offset); + } + break; + } + } +} + +/* Parse GUID-based volumedir layout: + * - 4 bytes of unknown data + * - 4 byte Length + * - array of module entries: + * - 16 byte GUID indicating module type + * - 4 byte Base + * - 4 byte Length + */ +void PhoenixVolume2(unsigned char *BIOSImage, int BIOSLength, int Offset) +{ + struct PhoenixVolumeDirEntry2 { + /* these are stored little endian */ + uint32_t guid1; + uint16_t guid2; + uint16_t guid3; + /* these are big endian */ + uint16_t guid4; + /*uint48_t guid5; */ + uint16_t guid5; + uint32_t guid6; + uint32_t Base; + uint32_t Length; + }; + + struct PhoenixVolumeDir2 { + uint16_t Unk1; + uint16_t Unk2; + uint32_t Length; + struct PhoenixVolumeDirEntry2 Modules[]; + } *Volume; + + char Name[16], guid[37]; + int fd, HoleNum = 0; + uint32_t Base, Length, NumModules, ModNum; + + Volume = (struct PhoenixVolumeDir2 *)(BIOSImage + Offset + 0x18); + NumModules = + (Volume->Length - 8) / sizeof(struct PhoenixVolumeDirEntry2); + + printf("FFV modules: %u\n", NumModules); + + for (ModNum = 0; ModNum < NumModules; ModNum++) { + sprintf(guid, "%08X-%04X-%04X-%04X-%04X%08X", + le32toh(Volume->Modules[ModNum].guid1), + le16toh(Volume->Modules[ModNum].guid2), + le16toh(Volume->Modules[ModNum].guid3), + be16toh(Volume->Modules[ModNum].guid4), + be16toh(Volume->Modules[ModNum].guid5), + be32toh(Volume->Modules[ModNum].guid6) + ); + Base = Volume->Modules[ModNum].Base & (BIOSLength - 1); + Length = Volume->Modules[ModNum].Length - 1; + printf("[%2u]: (%08X-%08X) %s\n", ModNum, Base, Base + Length, + guid); + + if (!strcmp(guid, GUID_FFVMODULE)) { + /* FFV modules */ + Offset = Base; + while (Offset < Base + Length) { + Offset += + PhoenixExtractFFV(BIOSImage, BIOSLength, + Offset); + } + } else if (!strcmp(guid, GUID_ESCD)) { + /* Extended System Configuration Data (and similar?) */ + printf("\tESCD\n"); + fd = open("ESCD.bin", O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, + "Error: unable to open ESCD.bin: %s\n\n", + strerror(errno)); + continue; + } + write(fd, BIOSImage + Base, Length); + close(fd); + } else if (!strcmp(guid, GUID_RAWCODE)) { + /* Raw BIOS code */ + printf("\tHole (raw code)\n"); + snprintf(Name, sizeof(Name), "hole_%02x.bin", + HoleNum++); + fd = open(Name, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, + "Error: unable to open %s: %s\n\n", + Name, strerror(errno)); + continue; + } + write(fd, BIOSImage + Base, Length); + close(fd); + } else { + fprintf(stderr, "\tUnknown FFV module GUID: %s\n", + guid); + } + } +} + +void PhoenixFFVDirectory(unsigned char *BIOSImage, int BIOSLength, int Offset) +{ + char Name[16]; + uint32_t Length; + struct PhoenixFFVModule *Module; + + Module = (struct PhoenixFFVModule *)(BIOSImage + Offset); + + if (Module->Signature != 0xF8) { + fprintf(stderr, "Error: Invalid module signature at 0x%05X\n", + Offset); + return; + } + + Length = (le16toh(Module->LengthHi) << 16) | Module->LengthLo; + + if ((Offset + Length) > BIOSLength) { + fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n", + Offset); + return; + } + + /* get rid of the pesky 0xFF in the middle of the name */ + memcpy(Name, Module->Name, 8); + memcpy(Name + 8, Module->Name + 9, 7); + Name[15] = '\0'; + if (!strcmp(Name, "volumedir.bin")) { + PhoenixVolume1(BIOSImage, BIOSLength, Offset, Length); + } else if (!strcmp(Name, "volumedir.bin2")) { + PhoenixVolume2(BIOSImage, BIOSLength, Offset); + } else { + fprintf(stderr, + "FFV points to something other than the volumedir: %s\n", + Name); + } +} + +Bool PhoenixFFV(unsigned char *BIOSImage, int BIOSLength, struct PhoenixID *FFV) +{ + uint32_t Offset; + + Offset = + le32toh(*((uint32_t *) (((char *)FFV) + 0xA))) & (BIOSLength - 1); + + if (!Offset) { + fprintf(stderr, "BCPFFV module offset is NULL.\n"); + return FALSE; + } + + PhoenixFFVDirectory(BIOSImage, BIOSLength, Offset); + + return TRUE; +} + +void PhoenixBCD6F1Decode(unsigned char *PackedBuffer, int PackedBufferSize, + unsigned char *OutputBuffer, int OutputBufferSize) { + /* This is a slightly modified Ghidra decompilation of phoedeco's + x86 assembly implementation. Might be unmodified LZSS for all I + know, but let's play it safe. */ + char *DAT_00729668 = malloc(0x1000); + memset(DAT_00729668, ' ', 0x1000); + char bVar1; + unsigned char *pbVar2; + int iVar3; + uint uVar4; + uint uVar5; + uint uVar6; + unsigned char *unaff_ESI = PackedBuffer; + unsigned char *pbVar7; + unsigned char *unaff_EDI = OutputBuffer; + + pbVar2 = unaff_ESI + PackedBufferSize; + uVar4 = 0; + uVar5 = 0xfee; + do { + uVar4 = uVar4 >> 1; + pbVar7 = unaff_ESI; + if ((uVar4 & 0x100) == 0) { + if (unaff_ESI == pbVar2) break; + pbVar7 = unaff_ESI + 1; + uVar4 = 0xff00 | *unaff_ESI; + } + if ((uVar4 & 1) == 0) { + if (pbVar7 == pbVar2) break; + if (pbVar7 + 1 == pbVar2) break; + unaff_ESI = pbVar7 + 2; + uVar6 = (uint)pbVar7[1]; + iVar3 = (uVar6 & 0xf) + 3; + uVar6 = (uint)*pbVar7 | (uVar6 & 0xf0) << 4; + pbVar7 = unaff_EDI; + do { + bVar1 = DAT_00729668[uVar6]; + uVar6 = (uVar6 + 1) & 0xfff; + unaff_EDI = pbVar7 + 1; + *pbVar7 = bVar1; + DAT_00729668[uVar5] = bVar1; + uVar5 = (uVar5 + 1) & 0xfff; + iVar3 = iVar3 + -1; + pbVar7 = unaff_EDI; + } while (iVar3 != 0); + } + else { + if (pbVar7 == pbVar2 || unaff_EDI >= (OutputBuffer + OutputBufferSize)) break; + unaff_ESI = pbVar7 + 1; + bVar1 = *pbVar7; + *unaff_EDI = bVar1; + DAT_00729668[uVar5] = bVar1; + uVar5 = (uVar5 + 1) & 0xfff; + unaff_EDI = unaff_EDI + 1; + } + } while( 1 ); + + free(DAT_00729668); +} + +/* + * + */ +Bool +PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t Offset1, uint32_t BCPSegmentOffset) +{ + struct PhoenixID *ID, *SYS = NULL, *FFV = NULL; + struct PhoenixModuleHeader *Module; + struct PhoenixBCD6F1 *BCD6F1; + uint32_t Offset, Length; + int fd; + unsigned char *p, *Buffer, + module_signature[] = {0x00, 0x31, 0x31}, + bcd6f1_signature[] = {'B', 'C', 0xd6, 0xf1, 0x00, 0x00, 0x12}, + optrom_signature[] = {0x55, 0xaa}, + bootrom_signature[] = {0x70, 0xe7}; + char filename[256]; + + printf("Found Phoenix BIOS \"%s\"\n", (char *)(BIOSImage + Offset1)); + + /* TODO: Print more information about image */ + /* TODO: Group modules by firmware volumes */ + + /* + * For newer Phoenix BIOSes, the BIOS has a trailing block that does not + * match the signature as tested in PhoenixModule. We adjust the length + * variable to handle that scenario. For example try the new BIOSes for the + * SuperMicro motherboards X7DA8 and X7DB8. X7DB8 is supported by Coreboot. + */ + if (BIOSLength > 0x100000 && BIOSOffset > 0) { + BIOSLength = BIOSLength + BIOSOffset - 0x100000; + } + + for (ID = (struct PhoenixID *)(BIOSImage + BCPSegmentOffset + 10); + ((void *)ID < (void *)(BIOSImage + BIOSLength)) && ID->Name[0]; + ID = + (struct PhoenixID *)(((unsigned char *)ID) + + le16toh(ID->Length))) { +#if 0 + printf + ("PhoenixID: Name %c%c%c%c%c%c, Flags 0x%04X, Length %d, Position %05X\n", + ID->Name[0], ID->Name[1], ID->Name[2], ID->Name[3], + ID->Name[4], ID->Name[5], le16toh(ID->Flags), + le16toh(ID->Length), (unsigned int) (((unsigned char *) ID) - BIOSImage)); +#endif + if (!le16toh(ID->Length)) + break; + + if (!strncmp(ID->Name, "BCPSYS", 6)) { + SYS = ID; + if (FFV) + break; + } else if (!strncmp(ID->Name, "BCPFFV", 6)) { + FFV = ID; + if (SYS) + break; + } + } + + if (!SYS) { + SYS = (struct PhoenixID *) memmem(BIOSImage, BIOSLength - 6, "BCPSYS", 6); + + if (!SYS) { + fprintf(stderr, "Error: Failed to locate BCPSYS offset.\n"); + return FALSE; + } + } + + /* Get some info */ + char Date[9], Time[9], Version[9]; + + strncpy(Date, ((char *)SYS) + 0x0F, 8); + Date[8] = 0; + strncpy(Time, ((char *)SYS) + 0x18, 8); + Time[8] = 0; + strncpy(Version, ((char *)SYS) + 0x37, 8); + Version[8] = 0; + + printf("Version \"%s\", created on %s at %s.\n", Version, Date, Time); + + if (!FFV) + FFV = (struct PhoenixID *) memmem(BIOSImage, BIOSLength - 6, "BCPFFV", 6); + + /* BCPCMP parsing */ + + unsigned char *bcpcmp = memmem(BIOSImage, BIOSLength - 6, "BCPCMP", 6); + if (!bcpcmp) { + fprintf(stderr, "Error: Failed to locate BCPCMP offset.\n"); + return FALSE; + } + + uint32_t bcpoff = bcpcmp - BIOSImage; + struct bcpCompress *bcpComp = + (struct bcpCompress *)(BIOSImage + bcpoff); + phx.compression = bcpComp->alg; + if ((bcpComp->head.major_revision == 0) && (bcpComp->head.minor_revision == 0)) + phx.commonCharacterLZSS = bcpComp->alt.commonCharacterLZSS; + else + phx.commonCharacterLZSS = bcpComp->main.commonCharacterLZSS; + + Offset = le32toh(*((uint32_t *) (((char *)SYS) + 0x77))); + Offset &= (BIOSLength - 1); + if (!Offset) { + fprintf(stderr, "BCPSYS module offset is NULL.\n"); + if (!FFV) { + return FALSE; + } + return PhoenixFFV(BIOSImage, BIOSLength, FFV); + } + + while (Offset) { + Offset = PhoenixModule(BIOSImage, BIOSLength, Offset); + Offset &= BIOSLength - 1; + } + + /* All scans below based on phoedeco */ + + /* BC D6 F1 (Phoenix 4.0x) */ + fprintf(stderr, "Now scanning for BC D6 F1.\n"); + p = BIOSImage; + while (p && (p < (BIOSImage + BIOSLength))) { + p = memmem(p, BIOSLength - sizeof(bcd6f1_signature) - (p - BIOSImage), bcd6f1_signature, sizeof(bcd6f1_signature)); + if (!p) + break; + + BCD6F1 = (struct PhoenixBCD6F1 *)p; + + sprintf(filename, "segment_%04X.rom", BCD6F1->Segment); + printf("0x%05lX (%6d bytes) -> %s\t(%d bytes)\n", + p - BIOSImage, le32toh(BCD6F1->FragLength), filename, + le32toh(BCD6F1->ExpLen)); + Buffer = MMapOutputFile(filename, le32toh(BCD6F1->ExpLen)); + if (!Buffer) + break; + + p += sizeof(struct PhoenixBCD6F1); + if (phx.compression == 0) + PhoenixBCD6F1Decode(p, le32toh(BCD6F1->FragLength), + Buffer, le32toh(BCD6F1->ExpLen)); + else + unlzari(p, le32toh(BCD6F1->FragLength), Buffer, + le32toh(BCD6F1->ExpLen), phx.commonCharacterLZSS); + + munmap(Buffer, le32toh(BCD6F1->ExpLen)); + } + + /* Uncompressed option ROMs */ + fprintf(stderr, "Now scanning for uncompressed option ROMs.\n"); + p = BIOSImage; + while (p && (p < (BIOSImage + BIOSLength))) { + p = memmem(p, BIOSLength - 32 - (p - BIOSImage), optrom_signature, sizeof(optrom_signature)); + if (!p) + break; + + Length = p[0x02] * 512; + if (((p - BIOSImage) & 0x7ff) || !Length) { + p += sizeof(optrom_signature); + continue; + } + + Offset = le16toh(*((uint16_t *) &p[0x18])); + sprintf(filename, "oprom_%05lX.rom", p - BIOSImage); + + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, + strerror(errno)); + return FALSE; + } + printf("0x%05lX (%6d bytes) -> %s\n", + p - BIOSImage, Length, filename); + write(fd, p, Length); + close(fd); + + p += Length; + } + + /* Boot ROM */ + fprintf(stderr, "Now scanning for boot ROM.\n"); + p = BIOSImage; + while (p && (p < (BIOSImage + BIOSLength))) { + p = memmem(p, BIOSLength - sizeof(bootrom_signature) - (p - BIOSImage), bootrom_signature, sizeof(bootrom_signature)); + if (!p) + break; + + if (((p - BIOSImage) & 0x7ff) || memcmp(p + 0x0e, "IBM AT Compatible Phoenix", 25)) { + p += sizeof(bootrom_signature); + continue; + } + + fd = open("boot.rom", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open boot.rom: %s\n\n", + strerror(errno)); + return FALSE; + } + printf("0x%05lX (%6d bytes) -> boot.rom\n", + p - BIOSImage, 8192); + write(fd, p, 8192); + close(fd); + + p += 8192; + } + + /* Bruteforce scan */ + fprintf(stderr, "Now performing bruteforce scan.\n"); + p = BIOSImage; + while (p && (p < (BIOSImage + BIOSLength))) { + p = memmem(p, BIOSLength - (p - BIOSImage), module_signature, sizeof(module_signature)); + if (!p) + break; + p -= 4; + + Module = (struct PhoenixModuleHeader *)p; + if (!MODULE_SIGNATURE_INVALID(Module)) + PhoenixModule(BIOSImage, BIOSLength, p - BIOSImage); + + p += sizeof(struct PhoenixModuleHeader); + } + + /* NAPI scan */ + fprintf(stderr, "Now scanning for NAPI.\n"); + p = BIOSImage; + while (p && (p < (BIOSImage + BIOSLength))) { + p = memmem(p, BIOSLength - 8192 - (p - BIOSImage), "NAPI", 4); + if (!p) + break; + + sprintf(filename, "napi_%05lX.rom", p - BIOSImage); + + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, + strerror(errno)); + return FALSE; + } + printf("0x%05lX (%6d bytes) -> %s\n", + p - BIOSImage, 8192, filename); + write(fd, p, 8192); + close(fd); + + p += 8192; + } + + /* ACFG scan */ + fprintf(stderr, "Now scanning for ACFG.\n"); + p = BIOSImage; + while (p && (p < (BIOSImage + BIOSLength))) { + p = memmem(p, BIOSLength - 8 - (p - BIOSImage), "ACFG", 4); + if (!p) + break; + p -= 2; + + Length = le16toh(*((uint16_t *) &p)); + if ((Length < 8) || ((p[7] != 1) && (p[7] != 2))) { + p += 4; + continue; + } + + sprintf(filename, "acfg_%05lX.rom", p - BIOSImage); + + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, + strerror(errno)); + return FALSE; + } + printf("0x%05lX (%6d bytes) -> %s\n", + p - BIOSImage, Length, filename); + write(fd, p, Length); + close(fd); + + p += Length; + } + + /* Extract uncompressed data */ + if ((bcpComp->head.major_revision == 0) && (bcpComp->head.minor_revision == 0)) { + Offset = BIOSLength - 0x10000 + le16toh(bcpComp->alt.unc_start_offset); + Length = 0x10000 - le16toh(bcpComp->alt.unc_start_offset); + } else { + Offset = BIOSLength - 0x10000 + le16toh(bcpComp->main.unc_start_offset); + Length = 0x10000 - le16toh(bcpComp->main.unc_start_offset); + } + fd = open("uncompressed.rom", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open uncompressed.rom: %s\n\n", + strerror(errno)); + return FALSE; + } + printf("0x%05X (%6d bytes) -> uncompressed.rom\n", + Offset, Length); + write(fd, BIOSImage + Offset, Length); + close(fd); + + return TRUE; +} diff --git a/bios_extract/src/systemsoft.c b/bios_extract/src/systemsoft.c new file mode 100644 index 0000000..b4bd81f --- /dev/null +++ b/bios_extract/src/systemsoft.c @@ -0,0 +1,232 @@ +/* + * Decompression utility for SystemSoft and Insyde MobilePRO BIOSes. + * + * Copyright 2021 RichardG + * Based on SYSODECO (c) 2000-2004 Veit Kannegieser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE 1 /* for memmem */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bios_extract.h" +#include "compat.h" +#include "lh5_extract.h" + +Bool +SystemSoftExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, + uint32_t SYSBIOSOffset, uint32_t EE88Offset) +{ + Bool IsPart, IsInsyde; + uint16_t Magic, Length; + uint32_t Offset, AdditionalBlockCount; + char filename[256], ModuleName[32]; + int fd, i, j; + + struct part { + uint16_t Magic; /* EE 88 */ + unsigned char Name[8]; + uint16_t AdditionalBlocks; + uint16_t PackedLen; + uint16_t Offset; + uint16_t Segment; + uint16_t Checksum; + } *part; + + struct microcode { + uint32_t Magic; + uint32_t Unused; + uint16_t Year; + uint8_t Day; + uint8_t Month; + } *microcode; + + struct additional { + uint16_t Magic; /* DD 88 */ + char Data[31]; + } *additional; + + printf("Found SystemSoft/Insyde BIOS\n"); + + /* dump modules */ + Offset = 0; + while (Offset < (BIOSLength - 20)) { + IsPart = IsInsyde = 0; + Length = 0; + + part = (struct part *)(BIOSImage + Offset); + Magic = le16toh(part->Magic); + if ((Magic == 0x88EE) || (Magic == 0x88FF)) { + /* part */ + if (((part->Name[0] < 'A') || (part->Name[0] > 'Z')) && + ((part->Name[1] < 'A') || (part->Name[1] > 'Z')) && + (((part->Name[2] < 'A') || (part->Name[2] > 'Z')) && + (part->Name[2] != ' ') && (part->Name[2] != 0x00))) { + Offset += 2; + continue; + } + + Length = le16toh(part->PackedLen); + sprintf(filename, "ssbody_%05X.rom", Offset); + if (Magic == 0x88FF) { + /* another tool called insydeco might be able to decompress + Insyde modules, but it's been lost to time... */ + strcpy(ModuleName, "Insyde module"); + } else { + IsPart = 1; + + ModuleName[0] = ModuleName[sizeof(part->Name) + 1] = '"'; + ModuleName[sizeof(part->Name) + 2] = 0; + for (i = 0; i < sizeof(part->Name); i++) { + if ((part->Name[i] == 0x00) || (part->Name[i] == '\n')) + ModuleName[i + 1] = ' '; + else + ModuleName[i + 1] = part->Name[i]; + } + } + + AdditionalBlockCount = (le16toh(part->AdditionalBlocks) >> 4) & 7; + Offset += 20 + (33 * AdditionalBlockCount); + } else if (Magic == 0xAA55) { + /* option ROM */ + Length = part->Name[0] * 512; + sprintf(filename, "ssopt_%05X.rom", Offset); + sprintf(ModuleName, "Option ROM"); + } else if ((Magic == 0x5349) && (part->Name[0] == 'A')) { + /* ISA ROM */ + Length = 0x2000; + sprintf(filename, "ssisa_%05X.rom", Offset); + sprintf(ModuleName, "ISA ROM"); + } else if ((Magic == 0x061E) && (part->Name[0] == 0x8A) && + (part->Name[1] == 0xD8) && (part->Name[2] == 0xB7)) { + /* "Battery Management?" */ + Length = 0x800; + sprintf(filename, "ssbat_%05X.rom", Offset); + sprintf(ModuleName, "Battery Management?"); + } else { + microcode = (struct microcode *)(BIOSImage + Offset); + if ((le32toh(microcode->Magic) == 0x00000001) && + (le16toh(microcode->Year) >= 1990) && + (le16toh(microcode->Year) <= 2090) && + (microcode->Day >= 1) && (microcode->Day <= 31) && + (microcode->Month >= 1) && (microcode->Month <= 12)) { + /* CPU microcode */ + Length = 0x800; + sprintf(filename, "ssucode_%05X.rom", Offset); + sprintf(ModuleName, "CPU Microcode"); + } else if (((Magic & 0xFF) == 0x00) || ((Magic & 0xFF) == 0xFF)) { + /* ignore 0x00/0xFF sequences */ + while ((Offset < BIOSLength) && + ((BIOSImage[Offset] == 0x00) || + (BIOSImage[Offset] == 0xFF))) + Offset++; + continue; + } else if ((BIOSLength - Offset) < 0x4000) { + Length = BIOSLength - Offset; + sprintf(filename, "ssboot.rom"); + sprintf(ModuleName, "Boot Block"); + } else { + Offset += 2; + continue; + } + } + + if (!Length) + break; + + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, + strerror(errno)); + return FALSE; + } + + printf("0x%05X (%6d bytes) -> %-20s", + Offset, Length, filename); + + if (IsPart) { + /* arithmetic decoding */ + unsigned char *PackedData = BIOSImage + Offset, + *DecodeBuffer = malloc(65536); + uint32_t DecodeBufferPos = 0; + memset(DecodeBuffer, 0, 65536); + + while (Length > 0) { + if (DecodeBufferPos >= 65536) + break; + + i = *PackedData++; + Length--; + if (i <= 0x0F) { + i += 1; + memcpy(DecodeBuffer + DecodeBufferPos, PackedData, i); + PackedData += i; + Length -= i; + DecodeBufferPos += i; + } else { + j = (i >> 4) + 1; + i = ((i & 0x0F) << 8) | *PackedData++; + Length--; + if (i > DecodeBufferPos) + break; + /* copy manually, as memcpy and memmove corrupt data */ + i = DecodeBufferPos - i; + while (j--) + DecodeBuffer[DecodeBufferPos++] = DecodeBuffer[i++]; + } + } + + write(fd, DecodeBuffer, DecodeBufferPos); + free(DecodeBuffer); + + Offset = PackedData - BIOSImage; + + printf(" (%6d bytes)\t%s\n", DecodeBufferPos, ModuleName); + + for (i = 0; i < AdditionalBlockCount; i++) { + additional = (struct additional *)(((unsigned char *)part) + 20 + (33 * i)); + + printf("\t\t\t\t\t\t\t\t\""); + for (j = 0; j < sizeof(additional->Data); j++) { + if ((additional->Data[j] == 0x00) || + (additional->Data[j] == '\n')) + putchar(' '); + else + putchar(additional->Data[j]); + } + printf("\"\n"); + } + } else { + write(fd, BIOSImage + Offset, Length); + + Offset += Length; + + printf("\t\t\t%s\n", ModuleName); + } + + close(fd); + } + + return TRUE; +} diff --git a/bios_extract/util/gitconfig/commit-msg b/bios_extract/util/gitconfig/commit-msg new file mode 100644 index 0000000..82f0581 --- /dev/null +++ b/bios_extract/util/gitconfig/commit-msg @@ -0,0 +1,173 @@ +#!/bin/sh +# +# Part of Gerrit Code Review (http://code.google.com/p/gerrit/) +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +CHANGE_ID_AFTER="Bug|Issue" +MSG="$1" + +# Check for, and add if missing, a unique Change-Id +# +add_ChangeId() { + clean_message=`sed -e ' + /^diff --git a\/.*/{ + s/// + q + } + /^Signed-off-by:/d + /^#/d + ' "$MSG" | git stripspace` + if test -z "$clean_message" + then + return + fi + + # Does Change-Id: already exist? if so, exit (no change). + if grep -i '^Change-Id: I[0-9a-f]\{40\}$' "$MSG" >/dev/null + then + return + fi + + id=`_gen_ChangeId` + T="$MSG.tmp.$$" + AWK=awk + if [ -x /usr/xpg4/bin/awk ]; then + # Solaris AWK is just too broken + AWK=/usr/xpg4/bin/awk + fi + + # How this works: + # - parse the commit message as (textLine+ blankLine*)* + # - assume textLine+ to be a footer until proven otherwise + # - exception: the first block is not footer (as it is the title) + # - read textLine+ into a variable + # - then count blankLines + # - once the next textLine appears, print textLine+ blankLine* as these + # aren't footer + # - in END, the last textLine+ block is available for footer parsing + $AWK ' + BEGIN { + # while we start with the assumption that textLine+ + # is a footer, the first block is not. + isFooter = 0 + footerComment = 0 + blankLines = 0 + } + + # Skip lines starting with "#" without any spaces before it. + /^#/ { next } + + # Skip the line starting with the diff command and everything after it, + # up to the end of the file, assuming it is only patch data. + # If more than one line before the diff was empty, strip all but one. + /^diff --git a/ { + blankLines = 0 + while (getline) { } + next + } + + # Count blank lines outside footer comments + /^$/ && (footerComment == 0) { + blankLines++ + next + } + + # Catch footer comment + /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) { + footerComment = 1 + } + + /]$/ && (footerComment == 1) { + footerComment = 2 + } + + # We have a non-blank line after blank lines. Handle this. + (blankLines > 0) { + print lines + for (i = 0; i < blankLines; i++) { + print "" + } + + lines = "" + blankLines = 0 + isFooter = 1 + footerComment = 0 + } + + # Detect that the current block is not the footer + (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) { + isFooter = 0 + } + + { + # We need this information about the current last comment line + if (footerComment == 2) { + footerComment = 0 + } + if (lines != "") { + lines = lines "\n"; + } + lines = lines $0 + } + + # Footer handling: + # If the last block is considered a footer, splice in the Change-Id at the + # right place. + # Look for the right place to inject Change-Id by considering + # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first, + # then Change-Id, then everything else (eg. Signed-off-by:). + # + # Otherwise just print the last block, a new line and the Change-Id as a + # block of its own. + END { + unprinted = 1 + if (isFooter == 0) { + print lines "\n" + lines = "" + } + changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):" + numlines = split(lines, footer, "\n") + for (line = 1; line <= numlines; line++) { + if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) { + unprinted = 0 + print "Change-Id: I'"$id"'" + } + print footer[line] + } + if (unprinted) { + print "Change-Id: I'"$id"'" + } + }' "$MSG" > $T && mv $T "$MSG" || rm -f $T +} +_gen_ChangeIdInput() { + echo "tree `git write-tree`" + if parent=`git rev-parse "HEAD^0" 2>/dev/null` + then + echo "parent $parent" + fi + echo "author `git var GIT_AUTHOR_IDENT`" + echo "committer `git var GIT_COMMITTER_IDENT`" + echo + printf '%s' "$clean_message" +} +_gen_ChangeId() { + _gen_ChangeIdInput | + git hash-object -t commit --stdin +} + + +add_ChangeId diff --git a/bios_extract/xfv/Decompress.c b/bios_extract/xfv/Decompress.c new file mode 100644 index 0000000..35a294c --- /dev/null +++ b/bios_extract/xfv/Decompress.c @@ -0,0 +1,1041 @@ +/*++ + +Copyright (c) 2004, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + + Decompress.c + +Abstract: + + Decompressor. Algorithm Ported from OPSD code (Decomp.asm) + +--*/ + +#include + +#include "efihack.h" + +EFI_STATUS +EFIAPI +EfiGetInfo ( + IN VOID *Source, + IN UINT32 SrcSize, + OUT UINT32 *DstSize, + OUT UINT32 *ScratchSize + ); + +EFI_STATUS +EFIAPI +EfiDecompress ( + IN VOID *Source, + IN UINT32 SrcSize, + IN OUT VOID *Destination, + IN UINT32 DstSize, + IN OUT VOID *Scratch, + IN UINT32 ScratchSize + ); + +EFI_STATUS +EFIAPI +TianoGetInfo ( + IN VOID *Source, + IN UINT32 SrcSize, + OUT UINT32 *DstSize, + OUT UINT32 *ScratchSize + ); + +EFI_STATUS +EFIAPI +TianoDecompress ( + IN VOID *Source, + IN UINT32 SrcSize, + IN OUT VOID *Destination, + IN UINT32 DstSize, + IN OUT VOID *Scratch, + IN UINT32 ScratchSize + ); + +// +// Decompression algorithm begins here +// +#define BITBUFSIZ 32 +#define MAXMATCH 256 +#define THRESHOLD 3 +#define CODE_BIT 16 +#define UINT8_MAX 0xff +#define BAD_TABLE - 1 + +// +// C: Char&Len Set; P: Position Set; T: exTra Set +// +#define NC (0xff + MAXMATCH + 2 - THRESHOLD) +#define CBIT 9 +#define MAXPBIT 5 +#define TBIT 5 +#define MAXNP ((1U << MAXPBIT) - 1) +#define NT (CODE_BIT + 3) +#if NT > MAXNP +#define NPT NT +#else +#define NPT MAXNP +#endif + +typedef struct { + UINT8 *mSrcBase; // Starting address of compressed data + UINT8 *mDstBase; // Starting address of decompressed data + UINT32 mOutBuf; + UINT32 mInBuf; + + UINT16 mBitCount; + UINT32 mBitBuf; + UINT32 mSubBitBuf; + UINT16 mBlockSize; + UINT32 mCompSize; + UINT32 mOrigSize; + + UINT16 mBadTableFlag; + + UINT16 mLeft[2 * NC - 1]; + UINT16 mRight[2 * NC - 1]; + UINT8 mCLen[NC]; + UINT8 mPTLen[NPT]; + UINT16 mCTable[4096]; + UINT16 mPTTable[256]; + + // + // The length of the field 'Position Set Code Length Array Size' in Block Header. + // For EFI 1.1 de/compression algorithm, mPBit = 4 + // For Tiano de/compression algorithm, mPBit = 5 + // + UINT8 mPBit; +} SCRATCH_DATA; + +STATIC +VOID +FillBuf ( + IN SCRATCH_DATA *Sd, + IN UINT16 NumOfBits + ) +/*++ + +Routine Description: + + Shift mBitBuf NumOfBits left. Read in NumOfBits of bits from source. + +Arguments: + + Sd - The global scratch data + NumOfBits - The number of bits to shift and read. + +Returns: (VOID) + +--*/ +{ + Sd->mBitBuf = (UINT32) (Sd->mBitBuf << NumOfBits); + + while (NumOfBits > Sd->mBitCount) { + + Sd->mBitBuf |= (UINT32) (Sd->mSubBitBuf << (NumOfBits = (UINT16) (NumOfBits - Sd->mBitCount))); + + if (Sd->mCompSize > 0) { + // + // Get 1 byte into SubBitBuf + // + Sd->mCompSize--; + Sd->mSubBitBuf = 0; + Sd->mSubBitBuf = Sd->mSrcBase[Sd->mInBuf++]; + Sd->mBitCount = 8; + + } else { + // + // No more bits from the source, just pad zero bit. + // + Sd->mSubBitBuf = 0; + Sd->mBitCount = 8; + + } + } + + Sd->mBitCount = (UINT16) (Sd->mBitCount - NumOfBits); + Sd->mBitBuf |= Sd->mSubBitBuf >> Sd->mBitCount; +} + +STATIC +UINT32 +GetBits ( + IN SCRATCH_DATA *Sd, + IN UINT16 NumOfBits + ) +/*++ + +Routine Description: + + Get NumOfBits of bits out from mBitBuf. Fill mBitBuf with subsequent + NumOfBits of bits from source. Returns NumOfBits of bits that are + popped out. + +Arguments: + + Sd - The global scratch data. + NumOfBits - The number of bits to pop and read. + +Returns: + + The bits that are popped out. + +--*/ +{ + UINT32 OutBits; + + OutBits = (UINT32) (Sd->mBitBuf >> (BITBUFSIZ - NumOfBits)); + + FillBuf (Sd, NumOfBits); + + return OutBits; +} + +STATIC +UINT16 +MakeTable ( + IN SCRATCH_DATA *Sd, + IN UINT16 NumOfChar, + IN UINT8 *BitLen, + IN UINT16 TableBits, + OUT UINT16 *Table + ) +/*++ + +Routine Description: + + Creates Huffman Code mapping table according to code length array. + +Arguments: + + Sd - The global scratch data + NumOfChar - Number of symbols in the symbol set + BitLen - Code length array + TableBits - The width of the mapping table + Table - The table + +Returns: + + 0 - OK. + BAD_TABLE - The table is corrupted. + +--*/ +{ + UINT16 Count[17]; + UINT16 Weight[17]; + UINT16 Start[18]; + UINT16 *Pointer; + UINT16 Index3; + UINT16 Index; + UINT16 Len; + UINT16 Char; + UINT16 JuBits; + UINT16 Avail; + UINT16 NextCode; + UINT16 Mask; + + for (Index = 1; Index <= 16; Index++) { + Count[Index] = 0; + } + + for (Index = 0; Index < NumOfChar; Index++) { + Count[BitLen[Index]]++; + } + + Start[1] = 0; + + for (Index = 1; Index <= 16; Index++) { + Start[Index + 1] = (UINT16) (Start[Index] + (Count[Index] << (16 - Index))); + } + + if (Start[17] != 0) { + /*(1U << 16)*/ + return (UINT16) BAD_TABLE; + } + + JuBits = (UINT16) (16 - TableBits); + + for (Index = 1; Index <= TableBits; Index++) { + Start[Index] >>= JuBits; + Weight[Index] = (UINT16) (1U << (TableBits - Index)); + } + + for (; Index <= 16; Index++) { + Weight[Index] = (UINT16) (1U << (16 - Index)); + } + + Index = (UINT16) (Start[TableBits + 1] >> JuBits); + + if (Index != 0) { + Index3 = (UINT16) (1U << TableBits); + while (Index != Index3) { + Table[Index++] = 0; + } + } + + Avail = NumOfChar; + Mask = (UINT16) (1U << (15 - TableBits)); + + for (Char = 0; Char < NumOfChar; Char++) { + + Len = BitLen[Char]; + if (Len == 0) { + continue; + } + + NextCode = (UINT16) (Start[Len] + Weight[Len]); + + if (Len <= TableBits) { + + for (Index = Start[Len]; Index < NextCode; Index++) { + Table[Index] = Char; + } + + } else { + + Index3 = Start[Len]; + Pointer = &Table[Index3 >> JuBits]; + Index = (UINT16) (Len - TableBits); + + while (Index != 0) { + if (*Pointer == 0) { + Sd->mRight[Avail] = Sd->mLeft[Avail] = 0; + *Pointer = Avail++; + } + + if (Index3 & Mask) { + Pointer = &Sd->mRight[*Pointer]; + } else { + Pointer = &Sd->mLeft[*Pointer]; + } + + Index3 <<= 1; + Index--; + } + + *Pointer = Char; + + } + + Start[Len] = NextCode; + } + // + // Succeeds + // + return 0; +} + +STATIC +UINT32 +DecodeP ( + IN SCRATCH_DATA *Sd + ) +/*++ + +Routine Description: + + Decodes a position value. + +Arguments: + + Sd - the global scratch data + +Returns: + + The position value decoded. + +--*/ +{ + UINT16 Val; + UINT32 Mask; + UINT32 Pos; + + Val = Sd->mPTTable[Sd->mBitBuf >> (BITBUFSIZ - 8)]; + + if (Val >= MAXNP) { + Mask = 1U << (BITBUFSIZ - 1 - 8); + + do { + + if (Sd->mBitBuf & Mask) { + Val = Sd->mRight[Val]; + } else { + Val = Sd->mLeft[Val]; + } + + Mask >>= 1; + } while (Val >= MAXNP); + } + // + // Advance what we have read + // + FillBuf (Sd, Sd->mPTLen[Val]); + + Pos = Val; + if (Val > 1) { + Pos = (UINT32) ((1U << (Val - 1)) + GetBits (Sd, (UINT16) (Val - 1))); + } + + return Pos; +} + +STATIC +UINT16 +ReadPTLen ( + IN SCRATCH_DATA *Sd, + IN UINT16 nn, + IN UINT16 nbit, + IN UINT16 Special + ) +/*++ + +Routine Description: + + Reads code lengths for the Extra Set or the Position Set + +Arguments: + + Sd - The global scratch data + nn - Number of symbols + nbit - Number of bits needed to represent nn + Special - The special symbol that needs to be taken care of + +Returns: + + 0 - OK. + BAD_TABLE - Table is corrupted. + +--*/ +{ + UINT16 Number; + UINT16 CharC; + UINT16 Index; + UINT32 Mask; + + Number = (UINT16) GetBits (Sd, nbit); + + if (Number == 0) { + CharC = (UINT16) GetBits (Sd, nbit); + + for (Index = 0; Index < 256; Index++) { + Sd->mPTTable[Index] = CharC; + } + + for (Index = 0; Index < nn; Index++) { + Sd->mPTLen[Index] = 0; + } + + return 0; + } + + Index = 0; + + while (Index < Number) { + + CharC = (UINT16) (Sd->mBitBuf >> (BITBUFSIZ - 3)); + + if (CharC == 7) { + Mask = 1U << (BITBUFSIZ - 1 - 3); + while (Mask & Sd->mBitBuf) { + Mask >>= 1; + CharC += 1; + } + } + + FillBuf (Sd, (UINT16) ((CharC < 7) ? 3 : CharC - 3)); + + Sd->mPTLen[Index++] = (UINT8) CharC; + + if (Index == Special) { + CharC = (UINT16) GetBits (Sd, 2); + while ((INT16) (--CharC) >= 0) { + Sd->mPTLen[Index++] = 0; + } + } + } + + while (Index < nn) { + Sd->mPTLen[Index++] = 0; + } + + return MakeTable (Sd, nn, Sd->mPTLen, 8, Sd->mPTTable); +} + +STATIC +VOID +ReadCLen ( + SCRATCH_DATA *Sd + ) +/*++ + +Routine Description: + + Reads code lengths for Char&Len Set. + +Arguments: + + Sd - the global scratch data + +Returns: (VOID) + +--*/ +{ + UINT16 Number; + UINT16 CharC; + UINT16 Index; + UINT32 Mask; + + Number = (UINT16) GetBits (Sd, CBIT); + + if (Number == 0) { + CharC = (UINT16) GetBits (Sd, CBIT); + + for (Index = 0; Index < NC; Index++) { + Sd->mCLen[Index] = 0; + } + + for (Index = 0; Index < 4096; Index++) { + Sd->mCTable[Index] = CharC; + } + + return ; + } + + Index = 0; + while (Index < Number) { + + CharC = Sd->mPTTable[Sd->mBitBuf >> (BITBUFSIZ - 8)]; + if (CharC >= NT) { + Mask = 1U << (BITBUFSIZ - 1 - 8); + + do { + + if (Mask & Sd->mBitBuf) { + CharC = Sd->mRight[CharC]; + } else { + CharC = Sd->mLeft[CharC]; + } + + Mask >>= 1; + + } while (CharC >= NT); + } + // + // Advance what we have read + // + FillBuf (Sd, Sd->mPTLen[CharC]); + + if (CharC <= 2) { + + if (CharC == 0) { + CharC = 1; + } else if (CharC == 1) { + CharC = (UINT16) (GetBits (Sd, 4) + 3); + } else if (CharC == 2) { + CharC = (UINT16) (GetBits (Sd, CBIT) + 20); + } + + while ((INT16) (--CharC) >= 0) { + Sd->mCLen[Index++] = 0; + } + + } else { + + Sd->mCLen[Index++] = (UINT8) (CharC - 2); + + } + } + + while (Index < NC) { + Sd->mCLen[Index++] = 0; + } + + MakeTable (Sd, NC, Sd->mCLen, 12, Sd->mCTable); + + return ; +} + +STATIC +UINT16 +DecodeC ( + SCRATCH_DATA *Sd + ) +/*++ + +Routine Description: + + Decode a character/length value. + +Arguments: + + Sd - The global scratch data. + +Returns: + + The value decoded. + +--*/ +{ + UINT16 Index2; + UINT32 Mask; + + if (Sd->mBlockSize == 0) { + // + // Starting a new block + // + Sd->mBlockSize = (UINT16) GetBits (Sd, 16); + Sd->mBadTableFlag = ReadPTLen (Sd, NT, TBIT, 3); // there be segfaulting dragons + if (Sd->mBadTableFlag != 0) { + return 0; + } + + ReadCLen (Sd); + + Sd->mBadTableFlag = ReadPTLen (Sd, MAXNP, Sd->mPBit, (UINT16) (-1)); + if (Sd->mBadTableFlag != 0) { + return 0; + } + } + + Sd->mBlockSize--; + Index2 = Sd->mCTable[Sd->mBitBuf >> (BITBUFSIZ - 12)]; + + if (Index2 >= NC) { + Mask = 1U << (BITBUFSIZ - 1 - 12); + + do { + if (Sd->mBitBuf & Mask) { + Index2 = Sd->mRight[Index2]; + } else { + Index2 = Sd->mLeft[Index2]; + } + + Mask >>= 1; + } while (Index2 >= NC); + } + // + // Advance what we have read + // + FillBuf (Sd, Sd->mCLen[Index2]); + + return Index2; +} + +STATIC +VOID +Decode ( + SCRATCH_DATA *Sd + ) +/*++ + +Routine Description: + + Decode the source data and put the resulting data into the destination buffer. + +Arguments: + + Sd - The global scratch data + +Returns: (VOID) + + --*/ +{ + UINT16 BytesRemain; + UINT32 DataIdx; + UINT16 CharC; + + BytesRemain = (UINT16) (-1); + + DataIdx = 0; + + for (;;) { + CharC = DecodeC (Sd); + if (Sd->mBadTableFlag != 0) { + return ; + } + + if (CharC < 256) { + // + // Process an Original character + // + if (Sd->mOutBuf >= Sd->mOrigSize) { + return ; + } else { + Sd->mDstBase[Sd->mOutBuf++] = (UINT8) CharC; + } + + } else { + // + // Process a Pointer + // + CharC = (UINT16) (CharC - (UINT8_MAX + 1 - THRESHOLD)); + + BytesRemain = CharC; + + DataIdx = Sd->mOutBuf - DecodeP (Sd) - 1; + + BytesRemain--; + while ((INT16) (BytesRemain) >= 0) { + Sd->mDstBase[Sd->mOutBuf++] = Sd->mDstBase[DataIdx++]; + if (Sd->mOutBuf >= Sd->mOrigSize) { + return ; + } + + BytesRemain--; + } + } + } + + return ; +} + +EFI_STATUS +GetInfo ( + IN VOID *Source, + IN UINT32 SrcSize, + OUT UINT32 *DstSize, + OUT UINT32 *ScratchSize + ) +/*++ + +Routine Description: + + The internal implementation of *_DECOMPRESS_PROTOCOL.GetInfo(). + +Arguments: + + Source - The source buffer containing the compressed data. + SrcSize - The size of source buffer + DstSize - The size of destination buffer. + ScratchSize - The size of scratch buffer. + +Returns: + + EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + UINT8 *Src; + + *ScratchSize = sizeof (SCRATCH_DATA); + + Src = Source; + if (SrcSize < 8) { + return EFI_INVALID_PARAMETER; + } + + *DstSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); + return EFI_SUCCESS; +} + +EFI_STATUS +Decompress ( + IN VOID *Source, + IN UINT32 SrcSize, + IN OUT VOID *Destination, + IN UINT32 DstSize, + IN OUT VOID *Scratch, + IN UINT32 ScratchSize, + IN UINT8 Version + ) +/*++ + +Routine Description: + + The internal implementation of *_DECOMPRESS_PROTOCOL.Decompress(). + +Arguments: + + Source - The source buffer containing the compressed data. + SrcSize - The size of source buffer + Destination - The destination buffer to store the decompressed data + DstSize - The size of destination buffer. + Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. + ScratchSize - The size of scratch buffer. + Version - The version of de/compression algorithm. + Version 1 for EFI 1.1 de/compression algorithm. + Version 2 for Tiano de/compression algorithm. + +Returns: + + EFI_SUCCESS - Decompression is successfull + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + UINT32 Index; + UINT32 CompSize; + UINT32 OrigSize; + EFI_STATUS Status; + SCRATCH_DATA *Sd; + UINT8 *Src; + UINT8 *Dst; + + Status = EFI_SUCCESS; + Src = Source; + Dst = Destination; + + if (ScratchSize < sizeof (SCRATCH_DATA)) { + fprintf(stderr, "ScratchSize\n"); + return EFI_INVALID_PARAMETER; + } + + Sd = (SCRATCH_DATA *) Scratch; + + if (SrcSize < 8) { + fprintf(stderr, "SrcSize < 8\n"); + return EFI_INVALID_PARAMETER; + } + + CompSize = Src[0] + (Src[1] << 8) + (Src[2] << 16) + (Src[3] << 24); + OrigSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); + + // + // If compressed file size is 0, return + // + if (OrigSize == 0) { + return Status; + } + + if (SrcSize < CompSize + 8) { + fprintf(stderr, "SrcSize < CompSize + 8\n"); + return EFI_INVALID_PARAMETER; + } + + if (DstSize != OrigSize) { + fprintf(stderr, "DstSize != OrigSize\n"); + return EFI_INVALID_PARAMETER; + } + + Src = Src + 8; + + for (Index = 0; Index < sizeof (SCRATCH_DATA); Index++) { + ((UINT8 *) Sd)[Index] = 0; + } + // + // The length of the field 'Position Set Code Length Array Size' in Block Header. + // For EFI 1.1 de/compression algorithm(Version 1), mPBit = 4 + // For Tiano de/compression algorithm(Version 2), mPBit = 5 + // + switch (Version) { + case 1: + Sd->mPBit = 4; + break; + + case 2: + Sd->mPBit = 5; + break; + + default: + // + // Currently, only have 2 versions + // + fprintf(stderr, "wrong Version\n"); + return EFI_INVALID_PARAMETER; + } + + Sd->mSrcBase = Src; + Sd->mDstBase = Dst; + Sd->mCompSize = CompSize; + Sd->mOrigSize = OrigSize; + + // + // Fill the first BITBUFSIZ bits + // + FillBuf (Sd, BITBUFSIZ); + + // + // Decompress it + // + Decode (Sd); + + if (Sd->mBadTableFlag != 0) { + // + // Something wrong with the source + // + fprintf(stderr, "Sd->mBadTableFlag != 0\n"); + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} + +EFI_STATUS +EFIAPI +EfiGetInfo ( + IN VOID *Source, + IN UINT32 SrcSize, + OUT UINT32 *DstSize, + OUT UINT32 *ScratchSize + ) +/*++ + +Routine Description: + + The implementation of EFI_DECOMPRESS_PROTOCOL.GetInfo(). + +Arguments: + + This - The protocol instance pointer + Source - The source buffer containing the compressed data. + SrcSize - The size of source buffer + DstSize - The size of destination buffer. + ScratchSize - The size of scratch buffer. + +Returns: + + EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + return GetInfo ( + Source, + SrcSize, + DstSize, + ScratchSize + ); +} + +EFI_STATUS +EFIAPI +EfiDecompress ( + IN VOID *Source, + IN UINT32 SrcSize, + IN OUT VOID *Destination, + IN UINT32 DstSize, + IN OUT VOID *Scratch, + IN UINT32 ScratchSize + ) +/*++ + +Routine Description: + + The implementation of EFI_DECOMPRESS_PROTOCOL.Decompress(). + +Arguments: + + This - The protocol instance pointer + Source - The source buffer containing the compressed data. + SrcSize - The size of source buffer + Destination - The destination buffer to store the decompressed data + DstSize - The size of destination buffer. + Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. + ScratchSize - The size of scratch buffer. + +Returns: + + EFI_SUCCESS - Decompression is successfull + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + // + // For EFI 1.1 de/compression algorithm, the version is 1. + // + return Decompress ( + Source, + SrcSize, + Destination, + DstSize, + Scratch, + ScratchSize, + 2 //1 + ); +} + +EFI_STATUS +EFIAPI +TianoGetInfo ( + IN VOID *Source, + IN UINT32 SrcSize, + OUT UINT32 *DstSize, + OUT UINT32 *ScratchSize + ) +/*++ + +Routine Description: + + The implementation of EFI_TIANO_DECOMPRESS_PROTOCOL.GetInfo(). + +Arguments: + + This - The protocol instance pointer + Source - The source buffer containing the compressed data. + SrcSize - The size of source buffer + DstSize - The size of destination buffer. + ScratchSize - The size of scratch buffer. + +Returns: + + EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + return GetInfo ( + Source, + SrcSize, + DstSize, + ScratchSize + ); +} + +EFI_STATUS +EFIAPI +TianoDecompress ( + IN VOID *Source, + IN UINT32 SrcSize, + IN OUT VOID *Destination, + IN UINT32 DstSize, + IN OUT VOID *Scratch, + IN UINT32 ScratchSize + ) +/*++ + +Routine Description: + + The implementation of EFI_TIANO_DECOMPRESS_PROTOCOL.Decompress(). + +Arguments: + + This - The protocol instance pointer + Source - The source buffer containing the compressed data. + SrcSize - The size of source buffer + Destination - The destination buffer to store the decompressed data + DstSize - The size of destination buffer. + Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. + ScratchSize - The size of scratch buffer. + +Returns: + + EFI_SUCCESS - Decompression is successfull + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + // + // For Tiano de/compression algorithm, the version is 2. + // + return Decompress ( + Source, + SrcSize, + Destination, + DstSize, + Scratch, + ScratchSize, + 2 + ); +} diff --git a/bios_extract/xfv/README.txt b/bios_extract/xfv/README.txt new file mode 100644 index 0000000..35afaed --- /dev/null +++ b/bios_extract/xfv/README.txt @@ -0,0 +1,33 @@ +This kit contains the tools to extract files from an EFI firmware +image. + + Step 1: Getting the image +--------------------------- +You can get a firmware image by extracting it from any vendor updater +package, or by using the 'dumpfv.efi' program from rEFIt. +The suffix of the image name is often ".fd". + + Step 2: Extracting the image +------------------------------ +Run "xfv.py" with the image file name as a parameter. +You may want to capture output from xfv for later reference, e.g.: + + ./xfv.py MBP11_0044_02B.fd | tee mbp11-output.txt + + Some notes +------------ +Early Apple firmware images (e.g. the original images for the iMac, MBP +and Mac mini models as present on the Firmware Restoration CD) include +file names for drivers and applications. Later images (e.g. the Boot +Camp firmware updates) don't. However, the GUID of the files is fixed, +so a future version of xfv may support building a dictionary of file +names and using them for images that don't have embedded file names. + +The current version only supports one "firmware volume" per file. The +firmware images actually contain 4 parts: + + Offset Size Content + 000000 1A0000 Main firmware volume: DXE core, DXE drivers + 1A0000 010000 Firmware volume, use unknown + 1B0000 030000 Contents unknown + 1E0000 020000 Firmware volume: PEI core, PEI drivers diff --git a/bios_extract/xfv/efidecomp.c b/bios_extract/xfv/efidecomp.c new file mode 100644 index 0000000..7e7e8b3 --- /dev/null +++ b/bios_extract/xfv/efidecomp.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +#include "efihack.h" + + +EFI_STATUS +EFIAPI +EfiGetInfo ( + IN VOID *Source, + IN UINT32 SrcSize, + OUT UINT32 *DstSize, + OUT UINT32 *ScratchSize + ); + +EFI_STATUS +EFIAPI +EfiDecompress ( + IN VOID *Source, + IN UINT32 SrcSize, + IN OUT VOID *Destination, + IN UINT32 DstSize, + IN OUT VOID *Scratch, + IN UINT32 ScratchSize + ); + +int main(int argc, char **argv) +{ + char *buffer; + long buflen, fill; + ssize_t got; + char *dstbuf, *scratchbuf; + UINT32 DstSize; + UINT32 ScratchSize; + EFI_STATUS Status; + + // read all data from stdin + buflen = 32768; + fill = 0; + buffer = malloc(buflen); + if (buffer == NULL) { + fprintf(stderr, "Out of memory!\n"); + return 1; + } + for (;;) { + if (fill == buflen) { + long newbuflen; + + newbuflen = buflen << 1; + buffer = realloc(buffer, newbuflen); + if (buffer == NULL) { + fprintf(stderr, "Out of memory!\n"); + return 1; + } + buflen = newbuflen; + } + + got = read(0, buffer + fill, buflen - fill); + if (got < 0) { + fprintf(stderr, "Error during read: %d\n", errno); + return 1; + } else if (got == 0) { + break; // EOF + } else { + fill += got; + } + } + + //fprintf(stderr, "got %d bytes\n", fill); + + // inspect data + Status = EfiGetInfo(buffer, fill, &DstSize, &ScratchSize); + if (Status != EFI_SUCCESS) { + fprintf(stderr, "EFI ERROR (get info)\n"); + return 1; + } + dstbuf = malloc(DstSize); + scratchbuf = malloc(ScratchSize); + if (dstbuf == NULL || scratchbuf == NULL) { + fprintf(stderr, "Out of memory!\n"); + return 1; + } + + // decompress data + Status = EfiDecompress(buffer, fill, dstbuf, DstSize, scratchbuf, ScratchSize); + if (Status != EFI_SUCCESS) { + fprintf(stderr, "EFI ERROR (decompress)\n"); + return 1; + } + + // write to stdout + while (DstSize > 0) { + got = write(1, dstbuf, DstSize); + if (got < 0) { + fprintf(stderr, "Error during write: %d\n", errno); + return 1; + } else { + dstbuf += got; + DstSize -= got; + } + } + + return 0; +} diff --git a/bios_extract/xfv/efihack.h b/bios_extract/xfv/efihack.h new file mode 100644 index 0000000..ed95db5 --- /dev/null +++ b/bios_extract/xfv/efihack.h @@ -0,0 +1,24 @@ + +#include +#define VOID void +#define UINT8 uint8_t +#define UINT16 uint16_t +#define UINT32 uint32_t +#define UINT64 uint64_t +#define INT8 int8_t +#define INT16 int16_t +#define INT32 int32_t +#define INT64 int64_t + +#define EFI_STATUS UINT32 +#define EFI_SUCCESS (0) +#define EFI_INVALID_PARAMETER (-5) + + +#define EFIAPI + +#define IN +#define OUT +#define OPTIONAL +#define STATIC static +#undef UINT8_MAX diff --git a/bios_extract/xfv/xfv.py b/bios_extract/xfv/xfv.py new file mode 100644 index 0000000..b03becc --- /dev/null +++ b/bios_extract/xfv/xfv.py @@ -0,0 +1,362 @@ +#!/usr/bin/python + +import sys +import os +from struct import unpack + +fvh_count = 0 + +### Formatting: GUIDs + +def format_guid(guid_s): + parts = unpack(" len(fvdata): + print "WARNING: File too short, header gives length as 0x%X bytes" % fvlen + else: + print "Size per header: 0x%X bytes" % fvlen + offset = fvhdrlen + + global fvh_count + #fvhdir = "fvh-%d" % fvh_count + fvhdir = "fvh-" + name + fvh_count = fvh_count + 1 + try: + os.mkdir(fvhdir) + except: + pass + os.chdir(fvhdir) + + ### Decode files + + print "Listing files" + print "-----" + + while True: + if offset == fvlen: + print "-----" + print "End of volume (size reached cleanly)" + break + if offset + 24 > fvlen: + print "-----" + print "End of volume (size reached uncleanly)" + break + + (fileguid, fileintcheck, filetype, fileattr, filelenandstate) = unpack("< 16s H B B L", fvdata[offset:offset+24]) + if filetype == 0xff: + print "-----" + print "End of volume (filler data found)" + break + fileentrylen = filelenandstate & 0xffffff + filestate = filelenandstate >> 24 + filelen = fileentrylen - 24 + if fileattr & 1: + print " has tail!" + filelen = filelen - 2 + + fileoffset = offset + 24 + nextoffset = (offset + fileentrylen + 7) & ~7 + + print "%08X %08X" % (fileoffset, nextoffset) + + filedata = fvdata[fileoffset:fileoffset+filelen] + compressed = False + if filetype != 1 and filelen > 3 and filedata[3] == "\x01": + compressed = True + filedata = decompress(filedata) + + if compressed: + print "%08X %s %s C %d (%d)" % (offset, format_guid(fileguid), format_filetype(filetype), len(filedata), filelen) + else: + print "%08X %s %s U %d" % (offset, format_guid(fileguid), format_filetype(filetype), filelen) + + if filetype != 0xF0: + handle_file("file-%s.%s" % (format_guid(fileguid), extention_filetype(filetype)), filetype, filedata) + else: + print "(skipping)" + + offset = nextoffset + + os.chdir('..') + return fvlen + +if sys.platform == 'win32': + efidecomp_path = os.path.join(os.path.dirname(__file__), "UEFI_Decompressor") + efidecomp_cmd = '"%s" %s %s' +else: + efidecomp_path = os.path.dirname(os.path.realpath(__file__)) + "/efidecomp" + efidecomp_cmd = "%s < %s > %s" + +### Handle decompression of a compressed section + +def decompress2(compdata): + f = file("_tmp_decompress", "wb") + f.write(compdata) + f.close() + + cmd = efidecomp_cmd % ( efidecomp_path, '_tmp_decompress', '_tmp_result') + print "cmd: %r" % cmd + os.system(cmd) + + f = file("_tmp_result", "rb") + decompdata = f.read() + f.close() + return decompdata + +def decompress(compdata): + (sectlenandtype, uncomplen, comptype) = unpack("< L L B", compdata[0:9]) + sectlen = sectlenandtype & 0xffffff + if sectlen < len(compdata): + print "WARNING: Compressed section is not the only section! (%d/%d)" % (sectlen, len(compdata)) + if comptype == 0: + return compdata[9:] + elif comptype == 1: + print "WARNING: this code path might not work"; + f = file("_tmp_decompress", "wb") + f.write(compdata[9:]) + f.close() + + cmd = efidecomp_cmd % ( efidecomp_path, '_tmp_decompress', '_tmp_result') + #cmd = "./efidecomp <_tmp_decompress >_tmp_result" + os.system(cmd) + + f = file("_tmp_result", "rb") + decompdata = f.read() + f.close() + + if len(decompdata) < uncomplen: + print "WARNING: Decompressed data too short!" + return decompdata + + elif comptype == 2: + f = file("_tmp_decompress", "wb") + f.write(compdata[13:sectlen+4]) # for some reason there is junk in 9:13 that I don't see in the raw files?! yuk. + f.close() + + os.system("lzmadec <_tmp_decompress >_tmp_result") + + f = file("_tmp_result", "rb") + decompdata = f.read() + f.close() + + if len(decompdata) < uncomplen: + print "WARNING: Decompressed data too short!" + return decompdata + else: + print "ERROR: Unknown compression type %d" % comptype + return compdata + +### Handle the contents of one firmware file + + +def handle_file(filename, filetype, filedata): + if filetype == 1: + f = file("%s.raw" % filename, "wb") + f.write(filedata) + f.close() + if filetype != 1: + handle_sections(filename, 0, filedata) + +def get_filename(imagedata): + imagelen = len(imagedata) + filename_override = None + + # first try to find a filename + offset = 0 + while offset + 4 <= imagelen: + (sectlenandtype,) = unpack("< L", imagedata[offset:offset + 4]) + sectlen = sectlenandtype & 0xffffff + secttype = sectlenandtype >> 24 + nextoffset = (offset + sectlen + 3) & ~3 + dataoffset = offset + 4 + datalen = sectlen - 4 + sectdata = imagedata[dataoffset:dataoffset + datalen] + + if secttype == 0x15: + filename_override = sectdata[:-2].decode( + "utf-16le").encode("utf-8") + print " Filename '%s'" % filename_override + + offset = nextoffset + return filename_override + +### Handle section data (i.e. multiple sections), recurse if necessary + +def handle_sections(filename, sectindex, imagedata): + imagelen = len(imagedata) + + # first try to find a filename + filename_override = get_filename(imagedata) + + # then analyze the sections for good + offset = 0 + while offset + 4 <= imagelen: + (sectlenandtype,) = unpack("< L", imagedata[offset:offset + 4]) + sectlen = sectlenandtype & 0xffffff + secttype = sectlenandtype >> 24 + nextoffset = (offset + sectlen + 3) & ~3 + dataoffset = offset + 4 + datalen = sectlen - 4 + + if secttype == 2: + (sectguid, sectdataoffset, sectattr) = unpack("< 16s H H", imagedata[offset+4:offset+24]) + dataoffset = offset + sectdataoffset + datalen = sectlen - sectdataoffset + if sectguid == "\xB0\xCD\x1B\xFC\x31\x7D\xAA\x49\x93\x6A\xA4\x60\x0D\x9D\xD0\x83": + # CRC32 section + sectindex = handle_sections(filename, sectindex, imagedata[ + dataoffset:dataoffset + datalen]) + else: + print " %02d GUID %s" % (sectindex, format_guid(sectguid)) + sectindex += 1 + sectindex = handle_sections(filename, sectindex, imagedata[ + dataoffset:dataoffset + datalen]) + elif secttype == 1: # compressed + sectdata = imagedata[dataoffset:dataoffset+datalen] + decdata = decompress2(sectdata) + print " %02d COMPRESSED %d => %d" % (sectindex, datalen, len(decdata)) + if filename_override == None: + filename_override = get_filename(decdata) + sectindex += 1 + sectindex = handle_sections(filename, sectindex, decdata) + else: + secttype_name = "UNKNOWN(%02X)" % secttype + ext = "data" + sectdata = imagedata[dataoffset:dataoffset + datalen] + extraprint = "" + + if secttype == 0x1: + secttype_name = "COMPRESSED" + ext = "comp" + if secttype == 0x10: + secttype_name = "PE32" + ext = "efi" + elif secttype == 0x11: + secttype_name = "PIC" + ext = "pic.efi" + elif secttype == 0x12: + secttype_name = "TE" + ext = "te" + elif secttype == 0x13: + secttype_name = "DXE_DEPEX" + ext = "depex" + elif secttype == 0x14: + secttype_name = "VERSION" + ext = "ver" + elif secttype == 0x15: + secttype_name = "USER_INTERFACE" + ext = None + elif secttype == 0x16: + secttype_name = "COMPATIBILITY16" + ext = "bios" + elif secttype == 0x17: + secttype_name = "FIRMWARE_VOLUME_IMAGE" + ext = "fd" + elif secttype == 0x18: + secttype_name = "FREEFORM_SUBTYPE_GUID" + ext = "guid" + elif secttype == 0x19: + secttype_name = "RAW" + ext = "raw" + if sectdata[0:8] == "\x89PNG\x0D\x0A\x1A\x0A": + ext = "png" + elif sectdata[0:4] == "icns": + ext = "icns" + elif secttype == 0x1B: + secttype_name = "PEI_DEPEX" + ext = None + + print " %02d %s %d%s" % ( + sectindex, secttype_name, datalen, extraprint) + + if ext is not None: + use_filename = "%s-%02d" % (filename, sectindex) + if filename_override is not None: + use_filename = filename_override + f = file("%s.%s" % (use_filename, ext), "wb") + f.write(sectdata) + f.close() + + if secttype == 0x17: + print "*** Recursively analyzing the contained firmware volume..." + handle_fv(sectdata, filename) + + sectindex += 1 + + offset = nextoffset + + return sectindex + +### main code + +if __name__ == '__main__': + if len(sys.argv) > 1: + filename = sys.argv[1] + if len(sys.argv) > 2: + offset = int(sys.argv[2], 16) + else: + offset = 0 + analyze_diskfile(filename, offset) + else: + print "Usage: xfv.py bios.rom [start offset]"