diff --git a/BUGS b/BUGS new file mode 100644 index 0000000000..b0cbfdfcea --- /dev/null +++ b/BUGS @@ -0,0 +1,51 @@ + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +Things not (yet) supported: +=========================== + +dos/exe +------- + * normal dos/exes with new exe headers + * max ~24000 relocation records (...should be enough for everyone ;-) + * exe + sys combined images + +wc/le +----- + * 16-bit selector alias fixups + * 16-bit offset relocation for objects larger than 4 kbyte + * 16:16 fixups + +If you need any of the above (they're very rare), send us an url of a +test file. + + * 16-bit objects are not loaded into DOS memory + * There is still a problem with the wdosx extender: if you compress a + wc/le file which does NOT contain the wdosx extender, and after this + you bind the wdosx stub to the compressed file, then it will work. + Otherwise it won't. + * unpacked pmwlite compressed programs might not work when compressed + with upx (pmwunlite bug mainly :-) + +win32/pe +-------- + * writeable shared sections (`--force' *may* work) + * certificates in the image + +djgpp2/coff +----------- + * all overlays (except Allegro pakfiles) are silently stripped + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..b4951ab753 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..444ded8d94 --- /dev/null +++ b/LICENSE @@ -0,0 +1,138 @@ +-----BEGIN PGP SIGNED MESSAGE----- + + + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +PLEASE CAREFULLY READ THIS LICENSE AGREEMENT, ESPECIALLY IF YOU PLAN +TO MODIFY THE UPX SOURCE CODE OR USE A MODIFIED UPX VERSION. + + +ABSTRACT +======== + + UPX and UCL are copyrighted software distributed under the terms + of the GNU General Public License (hereinafter the "GPL"). + + The stub which is imbedded in each UPX compressed program is part + of UPX and UCL, and contains code that is under our copyright. The + terms of the GNU General Public License still apply as compressing + a program is a special form of linking with our stub. + + As a special exception we grant the free usage of UPX for all + executables, including commercial programs. + See below for details and restrictions. + + +COPYRIGHT +========= + + UPX and UCL are copyrighted software. All rights remain with the authors. + + UPX is Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + UPX is Copyright (C) 1996-2000 Laszlo Molnar + + UCL is Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + + +GNU GENERAL PUBLIC LICENSE +========================== + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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. + + UPX and UCL are distributed in the hope that they 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. + + +SPECIAL EXCEPTION FOR COMPRESSED EXECUTABLES +============================================ + + The stub which is imbedded in each UPX compressed program is part + of UPX and UCL, and contains code that is under our copyright. The + terms of the GNU General Public License still apply as compressing + a program is a special form of linking with our stub. + + Hereby Markus F.X.J. Oberhumer and Laszlo Molnar grant you special + permission to freely use and distribute all UPX compressed programs + (including commercial ones), subject to the following restrictions: + + 1. You must compress your program with a completely unmodified UPX + version; either with our precompiled version, or (at your option) + with a self compiled version of the unmodified UPX sources as + distributed by us. + 2. This also implies that the UPX stub must be completely unmodfied, i.e. + the stub imbedded in your compressed program must be byte-identical + to the stub that is produced by the official unmodified UPX version. + 3. The decompressor and any other code from the stub must exclusively get + used by the unmodified UPX stub for decompressing your program at + program startup. No portion of the stub may get read, copied, + called or otherwise get used or accessed by your program. + + +ANNOTATIONS +=========== + + - You can use a modified UPX version or modified UPX stub only for + programs that are compatible with the GNU General Public License. + + - We grant you special permission to freely use and distribute all UPX + compressed programs. But any modification of the UPX stub (such as, + but not limited to, removing our copyright string or making your + program non-decompressible) will immediately revoke your right to + use and distribute a UPX compressed program. + + - UPX is not a software protection tool; by requiring that you use + the unmodified UPX version for your proprietary programs we + make sure that any user can decompress your program. This protects + both you and your users as nobody can hide malicious code - + any program that cannot be decompressed is highly suspicious + by definition. + + - You can integrate all or part of UPX and UCL into projects that + are compatible with the GNU GPL, but obviously you cannot grant + any special exceptions beyond the GPL for our code in your project. + + - We want to actively support manufacturers of virus scanners and + similar security software. Please contact us if you would like to + incorporate parts of UPX or UCL into such a product. + + + +Markus F.X.J. Oberhumer Laszlo Molnar +markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + +Linz, Austria, 25 Feb 2000 + + + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.3ia +Charset: noconv + +iQCVAwUBOLaLS210fyLu8beJAQFYVAP/ShzENWKLTvedLCjZbDcwaBEHfUVcrGMI +wE7frMkbWT2zmkdv9hW90WmjMhOBu7yhUplvN8BKOtLiolEnZmLCYu8AGCwr5wBf +dfLoClxnzfTtgQv5axF1awp4RwCUH3hf4cDrOVqmAsWXKPHtm4hx96jF6L4oHhjx +OO03+ojZdO8= +=CS52 +-----END PGP SIGNATURE----- diff --git a/LOADER.TXT b/LOADER.TXT new file mode 100644 index 0000000000..f46fcef5e2 --- /dev/null +++ b/LOADER.TXT @@ -0,0 +1,89 @@ +This documentation was written for those brave souls who want to +understand and/or modify the UPX assembly stubs - the small snippets +that do the runtime decompression when a compressed program is started. + +So, how the runtime stub/loader generation works? + +You might have already noticed that for some file formats the loaders +are quite simple (linux/i386 & tos) while in the other cases the +loaders look very suspicious: they're full of `%ifdef's and contain +loads of cryptic comments like `__PERELOC2__'. + +If you look at the C++ source files, however you can notice that these +comment strings (without the leading and trailing underscores) are used +in the following way (they are all 8 character length strings): + + addLoader("PEMAIN20", + ih.entry ? "PEDOJUMP" : "PERETURN", + "IDENTSTR""UPX1HEAD", + NULL + ); + +Basically that's all you have to know: when you want to add a section +of assembly code to the runtime loader, you just write + +l_foo.asm +--------- + ;__FOOBAR00__ + + xor eax, eax + label1: + jmps label1 + + ;__FOOBARZZ__ + +p_foo.cpp +--------- + + addLoader("FOOBAR00", NULL); + +This will add the assembly section starting from __FOOBAR00__ and ending +before __FOOBARZZ__ to the loader. You can add an %ifdef - %endif pair +before these comments if you wish - but these conditionals will NOT be +seen by the assembler, they are just syntactic sugar to make the code a +little bit more readable and understandable. (Note however, that only +%ifdefs which are started on the 1st column are removed by the upx +assembly preprocessor program, so you can still use preprocessor +conditionals if you wish - just write them starting from the 2nd +column.) + +That's nice, you could say, but how cross section jumps and calls are +handled? Well, that is the nicest part of this stuff - they are handled +automatically. All you have to do is to add the required sections to the +loader using `addLoader()' and the rest is done by upx. It will resolve +every conditional or unconditional jumps or subrutine calls for you. + +This functionality (we could say it's a simple linker) is achived by the +assembly preprocessor (src/stub/scripts/app.pl) and a little C++ module +(src/linker.cpp). And of course NASM - the Netwide Assembler. You can +see what's going on behind the scenes - just do: + + cd src/stubs + make maintainer-clean + make all + +This will rebuild all the loaders - and keep the temporary files (*.as[xy]) +which are seen by the assembler. + +Currently this loader/stub building method only works with ix86 +assembly - both app.pl and linker.cpp heavily rely on this when dealing +with cross section references. + +And finally some important features/requirements you should be aware of: + + - as previously stated - preprocessor conditionals starting on the 1st + column are removed by app.pl + - sections are separated by comments in the form `;__X1234567__' + - jumps are recognized by searching for a word which starts with `j' + and followed by a label - this also means that `jmp short label1' + will NOT be recognized (but you can use a macro called `jmps' for it + by adding `%define jmps jmp short' to the beginning of the file) + - at the end of the file you need something like this + + eof: + ; __XTHEENDX__ + section .data + dd -1 + dw eof + +That's all for now. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..bba625f9b7 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +# Toplevel Makefile for UPX + +all: + $(MAKE) -C src/stub + $(MAKE) -C src + $(MAKE) -C doc + +clean: + $(MAKE) -C src/stub $@ + $(MAKE) -C src $@ + $(MAKE) -C doc $@ + +distclean: clean + +dist: + sh ./maint/util/laszlo.sh + +.PHONY: all clean distclean dist + +.NOEXPORT: + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000000..5605a71650 --- /dev/null +++ b/NEWS @@ -0,0 +1,197 @@ +============================================================================ +User visible changes for UPX +============================================================================ + +Changes in 1.01 (09 Apr 2000) + * win32/pe: fixed an uncompression problem in DLLs with empty + fixup sections + * win32/pe: fixed another rare uncompression problem - a field in the + PE header was set incorrectly + +Changes in 1.00 (26 Mar 2000) + * documentation updates + * watcom/le: do not duplicate the non-resident name table + * win32/pe: fixed an import handling problem: sometimes too much data + could be deleted from a file -> the uncompressed file would not work + anymore + +Changes in 0.99.3 (07 Mar 2000) + * win32/pe: fixed a rare problem in the stub string handling part + +Changes in 0.99.2 (02 Mar 2000) + * dos/exe: fixed a typo causing an internal error (introduced in 0.99.1) + +Changes in 0.99.1 (29 Feb 2000) + * win32/pe: fixed some object alignments which were causing + problems when loading compressed DLLs under Windows NT/2000 + +Changes in 0.99 (25 Feb 2000) + * FULL SOURCE CODE RELEASED UNDER THE TERMS OF THE GNU GPL + * win32/pe: changed default to `--strip-relocs=1' + * dos/com and dos/sys: fixed a bad decompressor problem + * linux/i386: the counter for the progress indicator was off by one + +Changes in 0.94 (06 Dec 1999) + * win32/pe: the stub now calls ExitProcess in case of import errors + * under DOS and Windows, the environment variable UPX now accepts + a '#' as replacement for '=' because of a COMMAND.COM limitation + +Changes in 0.93 (22 Nov 1999) + * win32/pe: fixed --strip-relocs problem with uncompression + * win32/pe: fixed a bug which could produce a broken decompressor stub + * linux/i386: yet another FreeBSD compatibility fix + +Changes in 0.92 (14 Nov 1999) + * win32/pe: really fixed that one line (see below) + +Changes in 0.91 (13 Nov 1999) + * win32/pe: an important one-line fix for the newly introduced problems + * dos/com and dos/sys: fixed an internal error + * dos/exe: correctly restore cs when uncompressing + +Changes in 0.90 (10 Nov 1999) + * all formats: `--overlay=copy' now is the default overlay mode + * improved compression ratio for most files + * win32/pe: uncompression is finally supported + * win32/pe: never compress REGISTRY resources + * win32/pe: headersize was not set in PE header + * win32/pe: resource handling is rewritten + * win32/pe: the last :-) TLS problem is fixed + * win32/pe: somewhat less memory is required during compression + * linux/i386: fixed compression of scripts which was broken since 0.71 + * linux/i386: more FreeBSD compatibility issues + * changed option: `-i' now prints some more details during compression + (not finished yet) + +Changes in 0.84 (04 Oct 1999) + * dos/exe: fixed a rare problem where the decompressor could crash + * some other minor fixes + +Changes in 0.83 (17 Sep 1999) + * dos/exe: fixed minimal memory requirement problem for some files + * win32/pe: fixed a bug which caused a crash in some compressed files + * linux/i386: various improvements in the stub; also, for the sake + of FreeBSD users, the stub is now branded as Linux/ELF + +Changes in 0.82 (16 Aug 1999) + * dos/exe: fixed a decompressor bug which could cause crash on some files + * linux/i386: section headers are now stripped from the stub so that + `strip' won't ruin a compressed file any longer + * wc/le: support for stack not in the last object disabled again + * win32/pe: removed some unneeded data + +Changes in 0.81 (04 Aug 1999) + * win32/pe: fixed an important bug in import handling + * dos/com: fixed an internal error that could happen with very small files + +Changes in 0.80 (03 Aug 1999) + * you can set some default options in the environment var `UPX' + * dos/com: the decompressor stub now checks for enough free memory + * dos/exe: decompressor rewritten, some bugs are fixed + * dos/exe: new option `--no-reloc': no relocation data is put into + the DOS header + * tmt/adam: added support for more stubs, detect already packed files + * tmt/adam: new option `--copy-overlay' + * wc/le: reduced memory requirement during uncompression + * wc/le: support files which do not contain their stack in the last object + * wc/le: fixed a bug which could cause a crash, improved relocation + handling + * wc/le: new option `--copy-overlay' + * win32/pe: `--compress-icons=2' is now the default + * win32/pe: even better TLS support + * win32/pe: versioninfo works on NT + * win32/pe: import by ordinal from kernel32.dll works + * win32/pe: other import improvements: importing a nonexistant DLL + results in a usual Windows message, importing a nonexistant function + results in program exit (instead of crash ;-) + * win32/pe: new option: `--compress-resources=0' + * win32/pe: reduced memory requirement during uncompression, some + files might even require LESS memory when they're compressed + * win32/pe: TYPELIBs should work now + * win32/pe: improved relocation handling, 16-bit relocations should work + * win32/pe: new option `--strip-relocs' (only if you know what you are doing) + * win32/pe: new option `--copy-overlay' + * important internal changes: now the stubs are built at runtime + +Changes in 0.72 (12 May 1999) + * tmt/adam: fixed a serious problem in the decompressor stub; all + compressed tmt files should be recompressed + * win32/pe: fixed the 'shared sections not supported' warning: + read-only shared sections are fine + * win32/pe: never compress TYPELIB resources + * win32/pe: compressed files are hopefully less suspicious to heuristic + virus scanners now + * linux/i386: minor decompressor stub updates, nicer progress bar + +Changes in 0.71 (19 Apr 1999) + * dos/exe: added option `--no-overlay' + * linux/i386: various improvements in the stub, most notably the + overhead for an extra cleanup process has been removed + * win32/pe: added support for export forwarders + * win32/pe: added support for DLLs without entry point or imports + * win32/pe: yet another .bss fix + * win32/pe: new option `--compress-icons=2': compress all icons + which are not in the first icon directory + * win32/pe: rearranged stub to avoid false alerts from some virus scanners + +Changes in 0.70 (30 Mar 1999) + * added support for linux/i386 executables + * improved compression ratio quite a bit + * added new compression level `--best' to squeeze out even some more bytes + * win32/pe: TLS support is much better now + * win32/pe: --compress-icons=0 should now work as well + * the usual minor fixes for win32/pe + +Changes in 0.62 (16 Mar 1999) + * win32/pe: --compress-icons and --compress-exports are on now by default + * win32/pe: --compress-icons should really work now + * win32/pe: fixed a problem with embedded .bss sections + +Changes in 0.61 (08 Mar 1999) + * atari/tos: fixed a problem where the bss segment could become too small + +Changes in 0.60 (06 Mar 1999) + * win32/pe: fixed file corruption when the size of the export data is invalid + * win32/pe: fixed a problem with empty resource data + * win32/pe: compressed file alignment set to minimum value + * win32/pe: made all compressed sections writeable + * fixed some other win32/pe bugs + * fixed an address optimization problem for some not Watcom LE files + * fixed a bug which could make UPX hang when an exe header contained + an illegal value + * added some compression flags for the win32/pe format + * added support for Atari ST executables (atari/tos) + * improved compression ratio + * improved compression speed + +Changes in 0.51 (14 Jan 1999) + * fixed a small bug in the PE header that would prevent some compressed + win32/pe executables from running under Windows NT and WINE + +Changes in 0.50 (03 Jan 1999) + * added support for PE format executables (win32/pe & rtm32/pe) + * added support for TMT executables (tmt/adam) + * fixed a dos/sys bug that affected OpenDOS + +Changes in 0.40 (05 Oct 1998) + * improved compression ratio + * fixed a small but fatal bug in dos/sys introduced in 0.30 + * fixed a rare bug in dos/exe + * worked around a bug in djgpp's strip 2.8 + * Allegro packfile support should work now + * added dos/exeh compression method (works on 386+) + +Changes in 0.30 (27 Jul 1998) + * fixed a serious bug in the 32-bit compressors - please don't use + djgpp/coff and watcom/le compressed files from previous versions, + some of them are possibly damaged ! + * the 16-bit uncompressors are a little bit shorter & faster + * fixed progress indicator for VESA and SVGA text modes + +Changes in 0.20 (05 Jul 1998) + * second public beta release + * too many changes to list here + +Changes in 0.05 (26 May 1998) + * first public beta release + diff --git a/PROJECTS b/PROJECTS new file mode 100644 index 0000000000..7ed132a52d --- /dev/null +++ b/PROJECTS @@ -0,0 +1,92 @@ + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +Here are some possible projects from which you can choose if you'd like to +contribute to UPX. Be sure to get in touch with us first to avoid +duplicate efforts. + + +User interface: +--------------- + + - Write a nifty Win32 GUI (in C++), but try to keep the size of the + final executable small. Compression should be in a separate + thread that calls do_one_file() [see src/work.cpp]. + + - Draw a nice UPX icon. + + +More formats: +------------- + + - add more file formats / platforms (there were volunteers for FreeBSD, + BeOS, Windows CE, Amiga, C64, ...) + + - add support for Linux kernels + + - add support for self-extracting HTML pages using a Javascript stub + (like the AlgART HTML Packer "AHP") + + +Existing formats: +----------------- + + - dos/com: add support for very long files - convert to exe if necessary + + - dos/sys: add support for sys/exe combos + + - dos/exe: implement filter support + + - djgpp2/coff: add support for DLX dynamic loading + (see SEAL http://www.home.sk/public/seal/ ) + + - linux/i386: + - add support for compressed kernels + - use the new assembly preprocessor stuff (ie. process the file + headers internally) + - UPX uncompressor daemon + - rewrite the stub in assembly (not sure if this is a good idea...) + - etc... + - linux/i386: add a special elf/i386 format that understands + ELF files and has filter support + + - watcom/le: add support for per section compression, LX support, VXD + + - win32/pe: display a nice dialog box in the stub in case + of problems (like importing a DLL or function fails) + - win32/pe: finer control over resource compression, per section + compression + - win32/pe: compressing screensavers looses the description - probably + should not compress a special resource type + + +Compression ratio: +------------------ + + - invent more effective filters + + - I (Markus) will continue to work on better compression algorithms, + so be sure to do better than me if you plan working on this ;-) + + +Other: +------ + + - anything else you think that could improve UPX... + + +Thanks for your contribution. + diff --git a/README b/README new file mode 100644 index 0000000000..0fb59f2777 --- /dev/null +++ b/README @@ -0,0 +1,143 @@ + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +WELCOME +======= + +Welcome to UPX 1.00, the first production release (after almost two years +of beta testing). + +Please don't forget to read the new LICENSE - UPX is now distributed +under the GNU General Public License (GPL) with special exceptions +allowing the distribution of all compressed executables, including +commercial programs. + + +INTRODUCTION +============ + +UPX is an advanced executable file compressor. UPX will typically +reduce the file size of programs and DLLs by around 50%-70%, thus +reducing disk space, network load times, download times and +other distribution and storage costs. + +Programs and libraries compressed by UPX are completely self-contained +and run exactly as before, with no runtime or memory penalty for most +of the supported formats. + +UPX supports a number of different executable formats, including +Win95/98/2000/NT programs and DLLs, DOS programs, and Linux executables. + +UPX is free software distributed under the term of the GNU General +Public License. Full source code is available. + +UPX may be distributed and used freely, even with commercial applications. +See the UPX License Agreement for details. + +UPX is rated number one in the well known Archive Comparison Test. Visit +http://web.act.by.net/~act/act-exepack.html + +UPX aims to be Commercial Quality Freeware. + + +SHORT DOCUMENTATION +=================== + +`upx program.exe' will compress a program or DLL. For best compression +results try `upx --best program.exe'. + +Please see the file UPX.DOC for the full documentation. The files +NEWS and BUGS also contain various tidbits of information. + + +DISCLAIMER +========== + +UPX comes with ABSOLUTELY NO WARRANTY; for details see the file LICENSE. + +Having said that, we think that UPX is quite stable now. Indeed we +have compressed lots of files without any problems. Also, the +current version has undergone several months of beta testing - +actually it's almost 2 years since our first public beta. + +This is the first production quality release, and we plan that future +releases will be backward compatible with this version. + +Please report all problems or suggestions to the authors. Thanks. + + +THE FUTURE +========== + + - We'd really love to support handheld systems like the PalmPilot because + compression makes a lot of sense here. And - because of the atari/tos + format - we already have a working decompressor in 68000 assembly. + Unfortunately we know next to nothing about the operating system + architecture of such handhelds, so we need some information from + an expert. Please contact us if you think you can help. + + - The Linux approach could probably get ported to a lot of other Unix + variants, at least for other i386 architectures it shouldn't be too + much work. If someone sends me a fresh hard disk and an official + FreeBSD/OpenBSD/NetBSD/Solaris/BeOS... CD I might take a look at it ;-) + + - We will *NOT* add any sort of protection and/or encryption. + This only gives people a false feeling of security because + by definition all protectors/compressors can be broken. + And don't trust any advertisment of authors of other executable + compressors about this topic - just do a websearch on something + like "procdump"... + + - Fix all remaining bugs - keep your reports coming ;-) + + - See the file PROJECTS in the source code distribution if you want + to contribute. + + +COPYRIGHT +========= + +Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +Copyright (C) 1996-2000 Laszlo Molnar + +This program may be used freely, and you are welcome to +redistribute it under certain conditions. + +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 +UPX License Agreement for more details. + +You should have received a copy of the UPX License Agreement along +with this program; see the file LICENSE. If not, visit the UPX home page. + + +Share and enjoy, +Markus & Laszlo + + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + + +[ The term UPX is a shorthand for the Ultimate Packer for eXecutables + and holds no connection with potential owners of registered trademarks + or other rights. ] + +[ Feel free to contact us if you have commercial compression requirements + or interesting job offers. ] + diff --git a/README.SRC b/README.SRC new file mode 100644 index 0000000000..6e6b4c3e2b --- /dev/null +++ b/README.SRC @@ -0,0 +1,153 @@ + The UPX Hacker's Guide + ====================== + + +Foreword +-------- + + The precompiled UPX versions are linked against the NRV compression + library instead of the UCL library. Using same compression algorithms, + NRV achieves a better compression ratio. NRV is not publicly + available, though, and probably never will be. + + While you may be disappointed that you don't have access to the + latest state-of-the-art compression technology this is actually + a safe guard for all of us. The UPX source code release makes + it very easy for any evil-minded person to do all sort of bad + things. By not providing the very best compression ratio it is much + more difficult to create fake or otherwise disguised UPX versions (or + similar trojans), as any end user will notice when the compression + has gotten worse with a new "version" or "product". + + Finally please be aware that you now have your hands on the source + code of the most sophisticated executable packer ever. + Let's join our forces to make it even better :-) + + Share and enjoy, + Markus & Laszlo + + +Introduction +------------ + + Welcome to the UPX source code release! + + UPX is not a toy for kids. Apart from basic knowledge about executables + and data compression you will need to be firm in C++, assembler, + Perl and Makefiles. Probably some other things as well. + + If you cant't manage to compile it then the sources are + probably not for you. Don't email us for help. + + The authors use Linux for development. You might want to as well. + + +Short overview +-------------- + + The UPX source code consists of two mainly independent parts: + + 1) The src/stubs directory contains the decompression stubs that + will get added to each compressed executable. + The stubs are mainly written in assembler and get "compiled" + into ordinary C header files. + + 2) The src directory contains the actual packer sources. The stubs + are #included by the individual executable format handlers. + + +Prerequisites +------------- + + - first of all you need to build the UCL compression library + http://wildsau.idv.uni-linz.ac.at/mfx/ucl.html + + +Tools needed to build/modify the UPX sources +-------------------------------------------- + + - A modern C++ compiler like gcc 2.95.2 or Visual C++ 6 + (egcs 1.1.x may work, half-baked implementations like Borland C++ 5.5 won't) + + - GNU make 3.77 for Win32 + ftp://agnes.dida.physik.uni-essen.de/home/janjaap/mingw32/newnew/ + + - GNU make 3.77 for DOS + ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/ + + +To compile the packer sources +----------------------------- + + set the environment variable UCLDIR to point to your UCL installation, e.g. + + set UCLDIR=c:\src\ucl-0.90 (DOS) + export UCLDIR=$HOME/local/src/ucl-0.90 (Unix) + + then type + + make target=linux # on linux + make target=djgpp2 # for djgpp2 + make target=mingw32 # for mingw32 + make target=no-cygwin # for mingw32 as of cygwin b20.1 + make target=msc # for Visual C++ 6.0 + + +If you want to modify the stub sources you'll also need +------------------------------------------------------- + + - Nasm - the Netwide Assembler 0.98 + http://www.cryogen.com/Nasm + + - Perl 5.004 or better + ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/perl*b.zip + + - A68K - a 68000 macro assembler + http://wildsau.idv.uni-linz.ac.at/mfx/download/upx/tools/ + + - djasm - an assembler for the djgpp stub + http://wildsau.idv.uni-linz.ac.at/mfx/download/upx/tools/ + ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2/djdev203.zip + + - Linux (for the linux/i386 stubs) + + +Misc. notes +----------- + + As the docs say: UPX is a portable, extendable and endian neutral + program, so if you want to add some new stuff, try not to break these + nice properties. + + - Use the types LE16, LE32, BE16 and BE32 for fields in file headers. + - Use [sg]et_[bl]e(16|32) for getting/setting values in the data + stream. + - Use gcc extensions and other compiler specific stuff only through + macros. + - Keep in mind that it should be possible to build UPX on braindead + file systems (FAT). Don't use long file names or other things + that break building under plain DOS. + +*** + +Some conventions: + + - follow our coding style + - indent level = 4 + - expand all tabulators + + - Use throwSomeException() functions instead of throw SomeException(): + this makes the code shorter if used often. + +*** + +Patches/Contributions + + - Please send us bug fixes/contributions only using + + diff -u oldfile newfile + + or + + diff -uNr olddirectory newdirectory + diff --git a/THANKS b/THANKS new file mode 100644 index 0000000000..cbbf78da85 --- /dev/null +++ b/THANKS @@ -0,0 +1,55 @@ + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +.___.. . + | |_ _.._ ;_/ __ + | [ )(_][ )| \_) +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + +Adam Ierymenko + for severals ideas for the Linux version +Andi Kleen and Jamie Lokier + for the /proc/self/fd/X and other Linux suggestions +Atli Mar Gudmundsson + for several comments on the win32/pe stub +Charles W. Sandmann + for the idea with the stubless decompressor in djgpp2/coff +Ice + for debugging the PE headersize problem down +Joergen Ibsen and d'b + for the relocation & address optimization ideas +John S. Fine + for the new version of the dos/exe decompressor +Lukundoo + for beta testing +Oleg V. Volkov + for various FreeBSD specific informations +The Owl & G-RoM + for the --compress-icons fix +Ralph Roth + for reporting several bugs +Salvador Eduardo Tropea + for beta testing +The WINE project (http://www.winehq.com/) + for lots of useful information found in their PE loader sources +DJ Delorie for djgpp +Linus for Linux +Natascha + +Everybody else who was kind enough to spend time testing UPX, use it in +their packages and report bugs, who are too numerous to mention here. +UPX would not be what it is today without your invaluable help. + diff --git a/TODO b/TODO new file mode 100644 index 0000000000..f9e4098630 --- /dev/null +++ b/TODO @@ -0,0 +1,186 @@ +- The UCL license is GPL, this will cause problems for the antivirus +programs which can't use the uncompressor because of this. Or am I wrong? + +- Rearrange this TODO list, sort by categories, add a priority (1-5) + + +For 0.95: + +- FIXME: what do we want in 1.00 ??? + +- add NRV2D for dos/exe (use by default when >= 400 kB) + +- implement switch `-m' for method: 1 NRV2B, 2 NRV2D + +- DLL problem under NT ? is this fixed ??? + + +PE +== + +2 - easy - mfx + ml) new switch(es) : do not compress resource types x,y,z + or resource names a,b,c +3 - medium - ml) rebuild exports +3 - difficult) don't compress the BSS section and other holes. +4 - medium - ml) fix when objectalign < 0x1000 +4 - easy - ml) put the original offset of moved resources somewhere into + the res.dir. (if it's safe to do) +4 - ??? - ml) fix FIXMEs +5 - medium - ml) try to put the preprocessed imports & relocs back to their + original section if possible this could save some virtual memory + address space + + +COM +=== + + +EXE +=== + + + +LE +== + +4 - easy - ml) the decompressors are already aligned, no need for an + extra alignment + +4 - difficult - ml) handle holes in the file + + +TMT +=== + +4 - easy - ml) the decompressors are already aligned, no need for an + extra alignment + + +LINUX +===== + +4 - ???) + - test with AOUT binaries + - test with Java binaries + - make a compressed program suid. what happens ? + +5 - difficult) rewrite the whole stub in assembly + + +SPARC +===== + +3 - easy - ml) finish support - should generate byte-identical versions + for all formats (except those which are using Packer::getRandomId()) + + +DJGPP2 +====== + +1 - ???) do we support overlays at all ??? + +5 - ???) fix file extension handling when the --coff option is set + + +FOR 0.95 +======== + +- enable automatic dependency tracking + +1 - mfx) determine when to use NRV2D, find best c_flags values for + NRV2B & NRV2D (see util/runtest.sh) + +5 - boring - mfx) Finally fix all screen and UI problems. How boring... + Probably should think about the whole system (printf vs. fprintf vs. + con_fprintf...) + +5 - policy - ml+mfx) Discuss a default C++ style (GNU indent 2.2.3) + with Laszlo and use it. Or maybe Artistic Style? + +10) Examine why pod2text breaks lines within something like `B<--coff>'. + (pod sucks anyway. unfortunately all alternatives as well.) + + +MISC +==== + +3 - difficult ml) The first two sentences of the README look too much like + those from the aspack docs. Laszlo, could you rewrite this ? + +1 - easy - mfx) Check whether the compressed size of the executable is + really smaller than the uncompressed + +1 - ??? - mfx) Implement a more informational interface. + Probably add some global functions in msg.cpp. + Should think about the requirements first, i.e. should there be + some simple hierarchies (header1, header2, ...) etc. ? + Could use the `-v' switch for enabling this (or `-i' ??). + - Something like this would be fine: (with colors :-) + [Processing kernel32.dll] + [Import] + 3 DLL. + Original size: 12345 bytes. + Preprocessed size: 6789 bytes. + [Relocations] + Original size: 50000 bytes. + Preprocessed size: 25000 bytes. + [Exports] + exports compressed (try --compress-export=0 if needed) + [TLS] + Added 56 bytes TLS data. + Added 5 fixup records. + [Resources] + Directory size: 1234 bytes. + Resource data size: 100000 bytes. + Compressed resources: 8 (99900 bytes). + Not compressed resources: 1 (100 bytes). + +3 - ???) delete temporary files when something wrong happens + +4 - easy - ml) Add a `--486' option to use bswap on the 32-bit formats. + +4 - easy - ml) consider removing 'sbb bp,bp' or "or ebp,-1" when not needed + +5 - ???) idea: --subfile=xx compress xx. subfile: we'd need a list_subfiles + for this... + + +FIXME: classify + +GOALS FOR 0.90 +============== + +- Provide additional NRV2D stubs for all 32-bit formats. Try to do this + in a generic way so that we can possibly add more algorithms. + Start by adding a `--nrv2d' switch. + +FOR 0.90 +======== + +/ update the output of `--help' + +- filters: implement the missing scan() function for f_cto32. + Maybe could use this to try to find an optimal filter (e.g. + if level >= 9) based on the counts. + +- filters: could we have use for a f->firstcall info field ? + +TODO +==== + +- all formats: more thoroughly test the exe-header in canPack() + and throw exceptions when encountering bad values. + +- experiment with filters + +- for small programs (e.g. < 64k) try an additional algorithm (like + the non yet finished NRV2C) to see if it gives better compression ? + +/ make checkerg++ work -> test + +- fix file extension handling when the --coff option is set + +- set up an "official" test suite + +- finish documentation + diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000000..725f5b837a --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,63 @@ +SHELL = /bin/sh + +top_srcdir = .. + +PACKAGE = upx +VERSION_DATE = 10 May 2000 +VERSION := $(shell sed -n 's/^.*UPX_VERSION_STRING.*"\(.*\)".*/\1/p' $(top_srcdir)/src/version.h) + +TRIMSPACE = cat +TRIMSPACE = sed -e 's/ *$$//' + +BUILT_SOURCES = upx.1 upx.doc upx.html upx.man upx.ps upx.tex + + +### +### +### + +all: $(BUILT_SOURCES) + +clean: + -rm -f $(BUILT_SOURCES) pod2html*cache + +distclean: clean + +.SUFFIXES: .1 .doc .html .man .pod .ps .tex .texi + +.PHONY: all clean distclean + + +### +### rules +### + +.pod.1: + pod2man --center=" " --release="$(PACKAGE) $(VERSION)" --date="$(VERSION_DATE)" $< | $(TRIMSPACE) > $@ + +.pod.html: +### pod2html $< | $(TRIMSPACE) > $@ + pod2html --noindex $< | $(TRIMSPACE) > $@ + @-rm -f pod2html*cache + +.pod.tex: + pod2latex $< + +.pod.doc: + pod2text < $< | $(TRIMSPACE) > $@ + +.1.man: + nroff -man $< | $(TRIMSPACE) > $@ + +.1.ps: + groff -man $< | $(TRIMSPACE) > $@ + + +### +### dependencies +### + +$(BUILT_SOURCES): $(top_srcdir)/src/version.h Makefile + +.NOEXPORT: + diff --git a/doc/upx.pod b/doc/upx.pod new file mode 100644 index 0000000000..9b82dea592 --- /dev/null +++ b/doc/upx.pod @@ -0,0 +1,612 @@ +=head1 NAME + +upx - compress or expand executable files + + + +=head1 SYNOPSIS + +B S<[ I ]> S<[ I ]> I... + + + +=head1 ABSTRACT + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +B is a portable, extendable, high-performance executable packer for +several different executable formats. It achieves an excellent compression +ratio and offers I<*very*> fast decompression. Your executables suffer +no memory overhead or other drawbacks for most of the formats supported. + +While you may use UPX freely for both non-commercial and commercial +executables (for details see the file LICENSE), we would highly +appreciate if you credit UPX and ourselves in the documentation, +possibly including a reference to the UPX home page. Thanks. + +[ Using UPX in non-OpenSource applications without proper credits +is considered not politically correct ;-) ] + + + +=head1 DISCLAIMER + +UPX comes with ABSOLUTELY NO WARRANTY; for details see the file LICENSE. + +Having said that, we think that UPX is quite stable now. Indeed we +have compressed lots of files without any problems. Also, the +current version has undergone several months of beta testing - +actually it's almost 2 years since our first public beta. + +This is the first production quality release, and we plan that future 1.xx +releases will be backward compatible with this version. + +Please report all problems or suggestions to the authors. Thanks. + + + +=head1 DESCRIPTION + +B is a versatile executable packer with the following features: + + - excellent compression ratio: compresses better than zip/gzip, + use UPX to decrease the size of your distribution ! + + - very fast decompression: about 10 MB/sec even on my old Pentium 133 + + - no memory overhead for your compressed executables for most of the + supported formats + + - safe: you can list, test and unpack your executables + Also, a checksum of both the compressed and uncompressed file is + maintained internally. + + - universal: UPX can pack a number of executable formats: + * dos/exe + * dos/sys + * dos/com + * djgpp2/coff + * watcom/le (supporting DOS4G, PMODE/W, DOS32a and CauseWay) + * win32/pe + * rtm32/pe + * tmt/adam + * linux/i386 + * atari/tos + + - portable: UPX is written in portable endian-neutral C++ + + - extendable: because of the class layout it's very easy to support + new executable formats or add new compression algorithms + + - free: UPX can be distributed and used freely. And from version 0.99 + the full source code of UPX is released under the GNU General Public + License (GPL) ! + +You probably understand now why we call UPX the "I" +executable packer. + + + +=head1 COMMANDS + +=head2 Compress + +This is the default operation, eg. B will compress the file +specified on the command line. + +=head2 Decompress + +All UPX supported file formats can be unpacked using the B<-d> switch, eg. +B will uncompress the file you've just compressed. + +=head2 Test + +The B<-t> command tests the integrity of the compressed and uncompressed +data, eg. B check whether your file can be safely +decompressed. Note, that this command doesn't check the whole file, only +the part that will be uncompressed during program execution. This means +that you should not use this command instead of a virus checker. + +=head2 List + +The B<-l> command prints out some information about the compressed files +specified on the command line as parameters, eg B +shows the compressed / uncompressed size and the compression ratio of +I. + + + +=head1 OPTIONS + +B<-q>: be quiet, suppress warnings + +B<-q -q> (or B<-qq>): be very quiet, suppress errors + +B<-q -q -q> (or B<-qqq>): produce no output at all + +B<--help>: prints the help + +B<--version>: print the version of UPX + +B<--stdout>: writes all output to stdout + +[ ...to be written... - type `B' for now ] + + + +=head1 COMPRESSION LEVELS & TUNING + +B offers ten different compression levels from B<-1> to B<-9>, +and B<--best>. The default compression level is B<-7>. + +=over 4 + +=item * + +Compression levels 1, 2 and 3 are pretty fast. + +=item * + +Compression levels 4, 5 and 6 achieve a good time/ratio performance. + +=item * + +Compression levels 7, 8 and 9 favor compression ratio over speed. + +=item * + +Compression level B<--best> may take a very long time. + +=back + +Note that compression level B<-9> can be quite slow for some large +files, but you definitely should use it when releasing a final version +of your program. (E.g. it took about 20 minutes to compress the almost +5 MB MAME 0.34 with B<-9> on my Pentium 133, but the resulting executable +was still ~65 kB smaller than when using B<-7>.) + +Since UPX 0.70 there is also an extra compression level B<--best> which +squeezes out even some more compression ratio. While it is usually fine +to use this option with your favorite .com file it may take several hours +to compress a multi-megabyte program. You have been warned. + +Tips for even better compression: + +=over 4 + +=item * + +Try if B<--overlay=strip> works. + +=item * + +For win32/pe programs there's B<--strip-relocs=0>. See notes below. + +=back + + + +=head1 OVERLAY HANDLING OPTIONS + +B handles overlays like many other executable packers do: it simply +copies the overlay after the compressed image. This works with some +files, but doesn't work with others. + +Since version 0.90 UPX defaults to B<--overlay=copy> for +all executable formats. + + --overlay=copy Copy any extra data attached to the file. [DEFAULT] + + --overlay=strip Strip any overlay from the program instead of + copying it. Be warned, this may make the compressed + program crash or otherwise unusable. + + --overlay=skip Refuse to compress any program which has an overlay. + + + +=head1 ENVIRONMENT + +The environment variable B can hold a set of default +options for UPX. These options are interpreted first and +can be overwritten by explicit command line parameters. +For example: + + for DOS/Windows: set UPX=-9 --compress-icons#1 + for sh/ksh/zsh: UPX="-9 --compress-icons=1"; export UPX + for csh/tcsh: setenv UPX "-9 --compress-icons=1" + +Under DOS/Windows you must use '#' instead of '=' when setting the +environment variable because of a command.com limitation. + +On Vax/VMS, the name of the environment variable is +UPX_OPT, to avoid a conflict with the symbol set for +invocation of the program. + +Not all of the options are valid in the environment variable - +UPX will tell you. + +You can use the B<--no-env> option to turn this support off. + + + +=head1 NOTES FOR THE SUPPORTED EXECUTABLE FORMATS + +=head2 NOTES FOR ATARI/TOS + +This is the executable format used by the Atari ST, a 68000 based +personal computer which was popular in the late '80s. Support +of this format is only because of nostalgic feelings of one of +the authors and serves no practical purpose :-). + +Packed programs will be byte-identical to the original after uncompression. +All debug information will be stripped, though. + +Extra options available for this executable format: + + (none) + + + +=head2 NOTES FOR DOS/COM + +Obviously UPX won't work with executables that want to read data from +themselves (like some commandline utilities that ship with Win95/98). + +Compressed programs only work on a 286+. + +Packed programs will be byte-identical to the original after uncompression. + +Maximum uncompressed size: ~65100 bytes. + +Extra options available for this executable format: + + --8086 Create an executable that works on any 8086 CPU. + + + +=head2 NOTES FOR DOS/EXE + +dos/exe stands for all "normal" 16-bit DOS executables. + +Obviously UPX won't work with executables that want to read data from +themselves (like some command line utilities that ship with Win95/98). + +Compressed programs only work on a 286+. + +Extra options available for this executable format: + + --8086 Create an executable that works on any 8086 CPU. + + --no-reloc Use no relocation records in the exe header. + + + +=head2 NOTES FOR DOS/SYS + +You can only compress plain sys files, sys/exe (two in one) +combos are not supported. + +Compressed programs only work on a 286+. + +Packed programs will be byte-identical to the original after uncompression. + +Maximum uncompressed size: ~65350 bytes. + +Extra options available for this executable format: + + --8086 Create an executable that works on any 8086 CPU. + + + +=head2 NOTES FOR DJGPP2/COFF + +First of all, it is recommended to use UPX *instead* of B. strip has +the very bad habit of replacing your stub with its own (outdated) version. +Additionally UPX corrects a bug/feature in strip v2.8.x: it +will fix the 4 KByte aligment of the stub. + +UPX includes the full functionality of stubify. This means it will +automatically stubify your COFF files. Use the option B<--coff> to +disable this behaviour (see below). + +UPX automatically handles Allegro packfiles. + +The DLM format (a rather exotic shared library extension) is not supported. + +Packed programs will be byte-identical to the original after uncompression. +All debug information and trailing garbage will be stripped, though. + +BTW, UPX is the successor of the DJP executable packer. + +Extra options available for this executable format: + + --coff Produce COFF output instead of EXE. By default + UPX keeps your current stub. + + + +=head2 NOTES FOR LINUX/i386 + +How it works: + + For ELF executables, UPX decompresses directly to memory, simulating + the mapping that the operating system kernel uses during exec(), + including the PT_INTERP program interpreter (if any). + The brk() is set by a special PT_LOAD segment in the compressed + executable itself. UPX then wipes the stack clean except for + arguments, environment variables, and Elf_auxv entries (this is + required by bugs in the startup code of /lib/ld-linux.so as of + May 2000), and transfers control to the program interpreter or + the e_entry address of the original executable. + + For shell script executables (files beginning with "#!/" or "#! /") + where the shell is known to accept "-c ", UPX decompresses + the file into low memory, then maps the shell (and its PT_INTERP), + and passes control to the shell with the entire decompressed file + as the argument after "-c". Known shells are sh, ash, bsh, csh, + ksh, tcsh, pdksh. Restriction: UPX 1.10 cannot use this method + for shell scripts which use the one optional string argument after + the shell name in the script (example: "#! /bin/sh option3\n".) + + For files which are not ELF and not a script for a known "-c" shell, + UPX uses kernel exec(), which first requires decompressing to a + file in the filesystem. Interestingly - + because of the good memory management of the Linux kernel - this + often does not introduce a noticable delay, and in fact there + will be no disk access at all if you have enough free memory as + the entire process takes places within the filesystem buffers. + + A compressed executable consists of the UPX stub and an overlay + which contains the original program in a compressed form. + + The UPX stub is a statically linked ELF executable and does + the following at program startup: + + 1) decompress the overlay to a temporary location in /tmp + 2) open the temporary file for reading + 3) try to delete the temporary file and start (execve) + the uncompressed program in /tmp using /proc//fd/X as + attained by step 2) + 4) if that fails, fork off a subprocess to clean up and + start the program in /tmp in the meantime + + The UPX stub is about 1700 bytes long, partly written in assembler + and only uses kernel syscalls. It is not linked against any libc. + +Benefits: + + - UPX can compress all executables, be it AOUT, ELF, libc4, libc5, + libc6, Shell/Perl/Python/... scripts, standalone Java .class + binaries, or whatever... + All scripts and programs will work just as before. + + - Compressed programs are completely self-contained. No need for + any external program. + + - UPX keeps your original program untouched. This means that + after decompression you will have a byte-identical version, + and you can use UPX as a file compressor just like gzip. + [ Note that UPX maintains a checksum of the file internally, + so it is indeed a reliable alternative. ] + + - As the stub only uses syscalls and isn't linked against libc it + should run under any Linux configuration that can run ELF + binaries and has working /proc support. + + - For the same reason compressed executables should run under + FreeBSD and other systems which can run Linux binaries. + [ Please send feedback on this topic ] + +Drawbacks: + + - For non-ELF, non-shell executables, you need additional free disk + space for the uncompressed program + in your /tmp directory. This program is deleted immediately after + decompression, but you still need it for the full execution time + of the program. + + - For non-ELF, non-shell executables, you must have /proc filesystem + support as the stub wants to open + /proc//exe and needs /proc//fd/X. This also means that you + cannot compress programs that are used during the boot sequence + before /proc is mounted, unless those programs are ELF or are + scripts for known "-c" shells. + + - `ldd' and `size' won't show anything useful because all they + see is the statically linked stub (since version 0.82 the section + headers are stripped from the UPX stub and `size' doesn't even + recognize the file format any longer - looks like a binutils bug). + + - For non-ELF, non-shell executables, utilities like `top' will + display numerical values in the process + name field. This is because Linux computes the process name from + the first argument of the last execve syscall (which is typically + something like /proc//fd/3). + + - For non-ELF, non-shell executables, to reduce memory requirements + during uncompression UPX splits the + original file into blocks, so the compression ratio is a little bit + worse than with the other executable formats (but still quite nice). + [ Advise from kernel experts which can tell me more about the + execve memory semantics is welcome. Maybe this shortcoming + could be removed. ] + + - For non-ELF, non-shell executables, because of temporary decompression + to disk the decompression speed + is not as fast as with the other executable formats. Still, I can see + no noticable delay when starting programs like my ~3 MB emacs (which + is less than 1 MB when compressed :-). + +Notes: + + - As UPX leaves your original program untouched it is advantageous + to strip it before compression. + + - It is not advisable to compress programs which usually have many + instances running (like `make') because the common segments of + compressed programs won't be shared any longer between different + processes. + + - If you compress a script you will lose platform independence - + this could be a problem if you are using NFS mounted disks. + + - Compression of suid, guid and sticky-bit programs is rejected + because of possible security implications. + + - For the same reason there is no sense in making any compressed + program suid. + + - Obviously UPX won't work with executables that want to read data + from themselves. E.g., this might be a problem for Perl scripts + which access their __DATA__ lines. + + - In case of internal errors the stub will abort with exitcode 127. + Typical reasons for this to happen are that the program has somehow + been modified after compression, you have run out of disk space + or your /proc filesystem is not yet mounted. + Running `strace -o strace.log compressed_exe' will tell you more. + + +Extra options available for this executable format: + + (none) + + + +=head2 NOTES FOR RTM32/PE + +Same as win32/pe. + + + +=head2 NOTES FOR TMT/ADAM + +This format is used by the TMT Pascal compiler - see http://www.tmt.com/ . + +Extra options available for this executable format: + + (none) + + + +=head2 NOTES FOR WATCOM/LE + +UPX has been successfully tested with the following extenders: + DOS4G, DOS4GW, PMODE/W, DOS32a, CauseWay. + The WDOS/X extender is partly supported (for details + see the file bugs BUGS). + +Yes, you can use your compressed executables with DOS4GW. + +The LX format is not yet supported. + +DLLs are not supported. + +Extra options available for this executable format: + + --le Produce an unbound LE output instead of + keeping the current stub. + + + +=head2 NOTES FOR WIN32/PE + +The PE support in UPX is quite stable now, but definitely there are +still some incompabilities with some files. + +Because of the way UPX (and other packers for this format) works, you +can see increased memory usage of your compressed files. If you start +several instances of huge compressed programs you're wasting memory +because the common segements of the program won't get shared +across the instances. +On the other hand if you're compressing only smaller programs, or +running only one instance of larger programs, then this penalty is +smaller, but it's still there. + +If you're running executables from network, then compressed programs +will load faster, and require less bandwidth during execution. + +DLLs are supported. + +Extra options available for this executable format: + + --compress-exports=0 Don't compress the export section. + Use this if you plan to run the compressed + program under Wine. + --compress-exports=1 Compress the export section. [DEFAULT] + Compression of the export section can improve the + compression ratio quite a bit but may not work + with all programs (like winword.exe). + UPX never compresses the export section of a DLL + regardless of this option. + + --compress-icons=0 Don't compress any icons. + --compress-icons=1 Compress all but the first icon. + --compress-icons=2 Compress all icons which are not in the + first icon directory. [DEFAULT] + + --compress-resources=0 Don't compress any resources at all. + + --force Force compression even when there is an + unexpected value in a header field. + Use with care. + + --strip-relocs=0 Don't strip relocation records. + --strip-relocs=1 Strip relocation records. [DEFAULT] + This option only works on executables with base + address greater or equal to 0x400000. Usually the + compressed files becomes smaller, but some files + may become larger. Note that the resulting file will + not work under Win32s. + UPX never strips relocations from a DLL + regardless of this option. + + + +=head1 DIAGNOSTICS + +Exit status is normally 0; if an error occurs, exit status +is 1. If a warning occurs, exit status is 2. + +B's diagnostics are intended to be self-explanatory. + + + +=head1 BUGS + +Please report all bugs immediately to the authors. + + + +=head1 AUTHORS + + Markus F.X.J. Oberhumer + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + + Laszlo Molnar + http://www.nexus.hu/upx + + + +=head1 COPYRIGHT + +Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +Copyright (C) 1996-2000 Laszlo Molnar + +This program may be used freely, and you are welcome to +redistribute it under certain conditions. + +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 +UPX License Agreement for more details. + +You should have received a copy of the UPX License Agreement along +with this program; see the file LICENSE. If not, visit the UPX home page. + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000000..a49b2957f6 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,476 @@ +# UPX Makefile (GNU make) - works with djgpp2/win32/unix +# +# usage: +# `make target=linux' # linux +# `make target=djggp2' # djggp2 +# `make target=mingw32' # mingw32 +# `make target=no-cygwin' # mingw32 as of cygwin b20.1 +# `make target=msc' # Visual C++ 6.0 +# + + +# configuration section + +ifeq ($(strip $(UCLDIR)),) +# change this to reflect where the UCL library is +UCLDIR = $(HOME)/local/src/ucl-0.90 +endif + +DEBUG = 1 + + +# ------------------------------------------------------- +# You should not have to change anything below this line. +# ------------------------------------------------------- + +SHELL = /bin/sh + + +.SUFFIXES: +.SUFFIXES: .a .c .cpp .exe .lib .o .obj + + +srcdir = . +top_srcdir = .. + + +# auto-detect the target unless given on the commandline +target = djgpp2 +ifneq ($(strip $(wildcard /usr/include/linux)),) +target = linux +endif +ifneq ($(strip $(wildcard /platform/sun4?/kernel/unix)),) +target = sparc +endif + + +# /*********************************************************************** +# // object files +# ************************************************************************/ + +# these use exceptions & RTTI +OBJECTS1 = \ + compress$o except$o file$o lefile$o \ + filter$o mem$o msg$o stdcxx$o work$o ui$o \ + packer$o packhead$o packmast$o \ + p_com$o p_djgpp2$o p_exe$o p_lx_elf$o p_lx_sep$o p_lx_sh$o \ + p_sys$o p_tmt$o p_tos$o \ + p_unix$o p_vmlinux$o p_w32pe$o p_wcle$o + +# no exceptions or RTTI +OBJECTS2 = \ + filteri$o help$o main$o mygetopt$o util$o linker$o \ + c_init$o c_file$o c_none$o c_screen$o \ + s_object$o s_djgpp2$o s_vcsa$o s_win32$o + +# pure C sources +OBJECTS3 = + +OBJECTS = $(OBJECTS1) $(OBJECTS2) $(OBJECTS3) + + +# /*********************************************************************** +# // compiler settings +# ************************************************************************/ + +# default to a unix-type compiler +CC = gcc +CXX = $(CC) +DEFS = +INCLUDES = -I$(srcdir) +CFLAGS_OUTPUT = -o $@ +CXXFLAGS_OUTPUT = $(CFLAGS_OUTPUT) + +LINK_EXE = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) +STUBEDIT_EXE = + +o = .o +a = .a +e = .exe + + +### +### gcc defaults +### + +##CFLAGS_O = -Os +CFLAGS_O = -O2 +##CFLAGS_WERROR = -Werror +CFLAGS_W = $(CFLAGS_WERROR) +CFLAGS_W += -Wall -W -Wcast-align -Wcast-qual -Wmissing-declarations -Wmissing-prototypes -Wshadow -Wwrite-strings +##CFLAGS_M = -fno-builtin +## CFLAGS_M += -malign-functions=0 -malign-jumps=0 -malign-loops=0 + +CFLAGS = $(CFLAGS_W) $(CFLAGS_O) $(CFLAGS_M) +CXXFLAGS = $(CFLAGS) -Wsynth -fconserve-space +CXXFLAGS1 = $(CXXFLAGS) +CXXFLAGS2 = $(CXXFLAGS) -fno-exceptions -fno-rtti + +ifeq ($(DEBUG),1) +DEFS += -DDEBUG +## DEFS += -DTESTING +endif + +ifeq ($(DEBUG),1) +LDFLAGS = -g +else +LDFLAGS = -s +endif +LDFLAGS += -Wl,-Map,$(basename $@).map +LDLIBS = -lz +LIBDIRS = + + +### +### compression library +### + +UCLDIR:=$(strip $(subst \,/,$(UCLDIR))) +NRVDIR:=$(strip $(subst \,/,$(NRVDIR))) +ifeq ($(strip $(wildcard $(NRVDIR)/include/nrv)),) + u = ucl + U = UCL + upx_exe = upx$e +else + u = nrv + U = NRV + upx_exe = upx_$u$e +endif +UDIR := $($(U)DIR) + +DEFS += -DWITH_$(U) +ifneq ($(strip $(wildcard $(UDIR)/include)),) +INCLUDES += -I$(UDIR)/include +endif +ifneq ($(strip $(wildcard $(UDIR)/src/.libs)),) +LIBDIRS += $(UDIR)/src/.libs +endif +ifeq ($(DEBUG),1) +ifneq ($(strip $(wildcard $(UDIR)/build/debug/src/.libs)),) +LIBDIRS += $(UDIR)/build/debug/src/.libs +endif +endif +ifneq ($(strip $(wildcard $(UDIR)/build/release/src/.libs)),) +LIBDIRS += $(UDIR)/build/release/src/.libs +endif +ifneq ($(strip $(wildcard $(UDIR)/build/src/.libs)),) +LIBDIRS += $(UDIR)/build/src/.libs +endif +ifneq ($(strip $(wildcard $(UDIR))),) +LIBDIRS += $(UDIR) +endif + +tmp := -Wl,--rpath, +LDRPATH := $(addprefix $(tmp),$(LIBDIRS)) +LIBDIRS += . +LDLIBDIRS := $(addprefix -L,$(LIBDIRS)) + +##LDFLAGS += $(LDRPATH) +LDFLAGS += $(LDLIBDIRS) +LDLIBS += -l$(u) + + +### +### linux +### + +ifeq ($(target),linux) +e = +DEFS += '-DUPX_CONFIG_H="config_h/linux.h"' +CFLAGS_M += -mno-schedule-prologue +CFLAGS_M += -march=i386 -mcpu=pentium +CFLAGS_WERROR = -Werror +LDLIBS += -lmcheck + +ifeq (1,2) # checkergcc + CC = checkergcc + CXX = checkerg++ +else +ifeq ($(DEBUG),1) + ##CFLAGS += -O0 -gstabs+3 + CFLAGS += -O0 -gstabs+3 +else + ##LDFLAGS += -static + STUBEDIT_EXE = objcopy -S -R .comment -R .note $@ && perl $(srcdir)/stub/scripts/brandelf.pl $@ && chmod 755 $@ +endif +endif + +endif # linux + + +### +### djgpp2 +### + +ifeq ($(target),djgpp2) +CFLAGS_M += -mno-schedule-prologue +CFLAGS_M += -march=i386 -mcpu=pentium +CFLAGS_WERROR = -Werror +STUBEDIT_EXE = stubedit $@ bufsize=0xfc00 +endif # djgpp2 + + +### +### cygwin / mingw32 +### + +ifeq ($(target),cygwin) +CFLAGS_M += -mno-schedule-prologue +CFLAGS_M += -march=i386 -mcpu=pentium +endif + +ifeq ($(target),mingw32) +CFLAGS_M += -mno-schedule-prologue +CFLAGS_M += -march=i386 -mcpu=pentium +endif + +# mingw32 as included in cygwin b20.1 +ifeq ($(target),no-cygwin) +CC = gcc -mno-cygwin +CFLAGS_M += -mno-schedule-prologue +CFLAGS_M += -march=i386 -mcpu=pentium +endif + + +### +### Microsoft 32-bit C/C++ Compiler 12.00 (aka Visual C++ 6) +### + +ifeq ($(target),msc) +o = .obj +a = .lib +CC = cl -nologo +CFLAGS = -W4 +CXXFLAGS1 = $(CFLAGS) -GX -GR +CXXFLAGS2 = $(CFLAGS) +LDFLAGS = +LINK_LDFLAGS = /link /map:$(basename $@).map + +ifneq ($(strip $(wildcard $(UDIR))),) +LIB := $(UDIR);$(LIB) +endif +export LIB + +ifeq (1,2) + # statically link libc + CC += -ML + LDLIBS = $(u)_s.lib zlib_s.lib setargv.obj +else + # link against msvcrt.dll + CC += -MD + LDLIBS = $(u).lib zlib.lib setargv.obj +endif +ifeq ($(DEBUG),1) + CFLAGS += -Od -ZI + LINK_LDFLAGS += /debug +else + CFLAGS += -O2 -Gs -GF + LINK_LDFLAGS += /release +endif + +##LINK_LDFLAGS += /verbose +LINK_EXE = $(CC) $(LDFLAGS) -Fe$@ $^ $(LDLIBS) $(LINK_LDFLAGS) + +endif # msc + + +### +### sparc +### + +ifeq ($(target),sparc) +e = +DEFS += '-DUPX_CONFIG_H="config_h/sparc.h"' +INCLUDES += -I/home/ethmola/local/include + +ifeq (1,2) # native compiler + CFLAGS = -O0 -g + CXXFLAGS1 = + CXXFLAGS2 = + CFLAGS_M = + DEFS += -DUSE_STDNAMESPACE +else # gcc + CFLAGS += -O0 -gstabs+ +endif + +ifeq (1,2) # purify + DEFS += -D__PURIFY__ + LDFLAGS = -g -L/home/ethmola/local/lib + LINK_EXE = purify $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) +else + LDFLAGS += -g -L/home/ethmola/local/lib + LINK_EXE = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) +endif + +endif # sparc + + +### +### Borland C++ 5.5 (DOES NOT WORK - INCOMPLETE C++ IMPLEMENTATION) +### + +ifeq ($(target),bcc) +o = .obj +a = .lib +CC = bcc32 +CFLAGS = -w -w-par +CXXFLAGS1 = $(CFLAGS) +CXXFLAGS2 = $(CFLAGS) +CFLAGS_OUTPUT = -o$@ +LDFLAGS = +LDLIBS = $(u).lib zlib.lib + +ifneq ($(strip $(wildcard $(UDIR))),) +LIB := $(UDIR);$(LIB) +endif +export LIB + +ifeq ($(DEBUG),1) + CFLAGS += +else + CFLAGS += -O2 +endif + +LINK_EXE = $(CC) $(LDFLAGS) -e$@ $^ $(LDLIBS) + +endif # bcc + + +### +### malloc debuggers (Linux only) +### + +ifeq (1,2) + LDLIBS += -lefence +endif + +ifeq (1,2) + CFLAGS += -DWITH_DMALLOC + LDLIBS += -ldmalloc +endif + +ifeq (1,2) + CFLAGS += -DWITH_GC -DLINUX_THREADS -D_REENTRANT + LDLIBS += -lgc -lpthread + # only needed when using -static: + ##LDFLAGS += -Wl,-defsym,_DYNAMIC=0 +endif + +ifeq (1,2) + CFLAGS += -DWITH_MSS + LDLIBS += -lmss +endif + + +# /*********************************************************************** +# // main targets +# ************************************************************************/ + +all: $(upx_exe) + +.PHONY: all unupx mostlyclean clean distclean maintainer-clean untabify tags + +$(upx_exe): $(OBJECTS) $(LIBS) + $(LINK_EXE) + $(STUBEDIT_EXE) + +unupx: + $(MAKE) target=msc unupx.dll + +unupx.dll: $(OBJECTS) $(LIBS) + $(LINK_DLL) + + +mostlyclean: + -rm -f *.d *.err *.i *.log *.map *~ gdb-trans* + +clean: mostlyclean + -rm -f *.a *.lib *.o *.obj tags TAGS ID + -rm -f upx upx.exe upx_nrv upx_nrv.exe upx_ucl upx_ucl.exe + +distclean: clean + +maintainer-clean: distclean + + +untabify: + mfxtu -d4 -t *.h *.cpp *.ch + mfxtu -d8 -t stub/[ln]*.asm stub/*.ash stub/*.[cs] + +tags TAGS: + ctags *.h *.cpp *.ch + +ID: + mkid *.h *.cpp *.ch + + +# /*********************************************************************** +# // rules +# ************************************************************************/ + +.c$o: + $(CC) $(DEFS) $(INCLUDES) $(CFLAGS) $(CFLAGS_OUTPUT) -c $< + +.cpp$o: + $(CXX) $(DEFS) $(INCLUDES) $(CXXFLAGS1) $(CXXFLAGS_OUTPUT) -c $< + +$(OBJECTS1): %$o : %.cpp + $(CXX) $(DEFS) $(INCLUDES) $(CXXFLAGS1) $(CXXFLAGS_OUTPUT) -c $< + +$(OBJECTS2): %$o : %.cpp + $(CXX) $(DEFS) $(INCLUDES) $(CXXFLAGS2) $(CXXFLAGS_OUTPUT) -c $< + +ifneq ($(strip $(OBJECTS3)),) +$(OBJECTS3): %$o : %.c + $(CC) $(DEFS) $(INCLUDES) $(CFLAGS) $(CFLAGS_OUTPUT) -c $< +endif + + +# /*********************************************************************** +# // dependencies +# ************************************************************************/ + +# FIXME: use automated dependencies + +main$o: mygetopt.h version.h +filter$o: filter.h +filteri$o: filter.h fcto_ml.ch fcto_ml2.ch +help$o: version.h +msg$o: ui.h +lefile$o: lefile.h +linker$o: linker.h +mygetopt$o: mygetopt.h +packer$o: packer.h filter.h linker.h ui.h version.h +packhead$o: packer.h +packmast$o: packmast.h packer.h lefile.h \ + p_com.h p_djgpp2.h p_exe.h p_lx_elf.h p_lx_sep.h p_lx_sh.h \ + p_sys.h p_tmt.h p_tos.h \ + p_unix.h p_vxd.h p_w32pe.h p_wcle.h +ui$o: packer.h ui.h +work$o: packer.h ui.h packmast.h + +p_com$o: packer.h p_com.h stub/l_com.h +p_djgpp2$o: packer.h p_djgpp2.h stub/stubify.h \ + stub/l_djgpp2.h +p_exe$o: packer.h p_exe.h stub/l_exe.h +p_lx_elf$o: packer.h p_lx_elf.h p_unix.h p_elf.h \ + stub/l_le_n2b.h stub/l_le_n2d.h +p_lx_sep$o: packer.h p_lx_sep.h +p_lx_sh$o: packer.h p_lx_elf.h p_unix.h p_elf.h \ + stub/l_sh_n2b.h stub/l_sh_n2d.h +p_sys$o: packer.h p_sys.h p_com.h stub/l_sys.h +p_tmt$o: packer.h p_tmt.h \ + stub/l_tmt.h +p_tos$o: packer.h p_tos.h \ + stub/l_t_n2b.h stub/l_t_n2bs.h \ + stub/l_t_n2d.h stub/l_t_n2ds.h +p_unix$o: packer.h p_unix.h p_elf.h \ + stub/l_lx_n2b.h stub/l_lx_n2d.h +p_vmlinux$o: packer.h p_unix.h p_elf.h \ + stub/l_lx_n2b.h stub/l_lx_n2d.h +p_w32pe$o: packer.h p_w32pe.h \ + stub/l_w32pe.h +p_wcle$o: packer.h p_wcle.h lefile.h \ + stub/l_wcle.h + +# vi:nowrap diff --git a/src/bele.h b/src/bele.h new file mode 100644 index 0000000000..7d106ed881 --- /dev/null +++ b/src/bele.h @@ -0,0 +1,261 @@ +/* bele.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_BELE_H +#define __UPX_BELE_H + + +/************************************************************************* +// access memory in BigEndian and LittleEndian byte order +**************************************************************************/ + +inline unsigned short get_be16(const void * bb, int off=0) +{ + const upx_bytep b = reinterpret_cast(bb) + off; + unsigned v; + v = (unsigned) b[1] << 0; + v |= (unsigned) b[0] << 8; + return (unsigned short) v; +} + +inline void set_be16(void * bb, unsigned v, int off=0) +{ + upx_bytep b = reinterpret_cast(bb) + off; + b[1] = (unsigned char) (v >> 0); + b[0] = (unsigned char) (v >> 8); +} + + +inline unsigned get_be32(const void * bb, int off=0) +{ + const upx_bytep b = reinterpret_cast(bb) + off; + unsigned v; + v = (unsigned) b[3] << 0; + v |= (unsigned) b[2] << 8; + v |= (unsigned) b[1] << 16; + v |= (unsigned) b[0] << 24; + return v; +} + +inline void set_be32(void * bb, unsigned v, int off=0) +{ + upx_bytep b = reinterpret_cast(bb) + off; + b[3] = (unsigned char) (v >> 0); + b[2] = (unsigned char) (v >> 8); + b[1] = (unsigned char) (v >> 16); + b[0] = (unsigned char) (v >> 24); +} + + +inline unsigned short get_le16(const void * bb, int off=0) +{ + const upx_bytep b = reinterpret_cast(bb) + off; + unsigned v; +#if defined(__i386__) + v = * (const unsigned short *) b; +#else + v = (unsigned) b[0] << 0; + v |= (unsigned) b[1] << 8; +#endif + return (unsigned short) v; +} + +inline void set_le16(void * bb, unsigned v, int off=0) +{ + upx_bytep b = reinterpret_cast(bb) + off; +#if defined(__i386__) + (* (unsigned short *) b) = (unsigned short) v; +#else + b[0] = (unsigned char) (v >> 0); + b[1] = (unsigned char) (v >> 8); +#endif +} + + +inline unsigned get_le24(const void * bb, int off=0) +{ + const upx_bytep b = reinterpret_cast(bb) + off; + unsigned v; + v = (unsigned) b[0] << 0; + v |= (unsigned) b[1] << 8; + v |= (unsigned) b[2] << 16; + return v; +} + +inline void set_le24(void * bb, unsigned v, int off=0) +{ + upx_bytep b = reinterpret_cast(bb) + off; + b[0] = (unsigned char) (v >> 0); + b[1] = (unsigned char) (v >> 8); + b[2] = (unsigned char) (v >> 16); +} + + +inline unsigned get_le32(const void * bb, int off=0) +{ + const upx_bytep b = reinterpret_cast(bb) + off; + unsigned v; +#if defined(__i386__) + v = * (const unsigned *) b; +#else + v = (unsigned) b[0] << 0; + v |= (unsigned) b[1] << 8; + v |= (unsigned) b[2] << 16; + v |= (unsigned) b[3] << 24; +#endif + return v; +} + +inline void set_le32(void * bb, unsigned v, int off=0) +{ + upx_bytep b = reinterpret_cast(bb) + off; +#if defined(__i386__) + (* (unsigned *) b) = v; +#else + b[0] = (unsigned char) (v >> 0); + b[1] = (unsigned char) (v >> 8); + b[2] = (unsigned char) (v >> 16); + b[3] = (unsigned char) (v >> 24); +#endif +} + + +/************************************************************************* +// classes for portable unaligned access +**************************************************************************/ + +class BE16 +{ + unsigned char d[2]; + +public: + BE16& operator = (const BE16 &v) { memcpy(d, v.d, sizeof(d)); return *this; } + + BE16& operator = (unsigned v) { set_be16(d, v); return *this; } + BE16& operator += (unsigned v) { set_be16(d, get_be16(d) + v); return *this; } + BE16& operator -= (unsigned v) { set_be16(d, get_be16(d) - v); return *this; } + BE16& operator &= (unsigned v) { set_be16(d, get_be16(d) & v); return *this; } + BE16& operator |= (unsigned v) { set_be16(d, get_be16(d) | v); return *this; } + + operator const unsigned () const { return get_be16(d); } +}; + + +class BE32 +{ + unsigned char d[4]; + +public: + BE32& operator = (const BE32 &v) { memcpy(d, v.d, sizeof(d)); return *this; } + + BE32& operator = (unsigned v) { set_be32(d, v); return *this; } + BE32& operator += (unsigned v) { set_be32(d, get_be32(d) + v); return *this; } + BE32& operator -= (unsigned v) { set_be32(d, get_be32(d) - v); return *this; } + BE32& operator &= (unsigned v) { set_be32(d, get_be32(d) & v); return *this; } + BE32& operator |= (unsigned v) { set_be32(d, get_be32(d) | v); return *this; } + + operator const unsigned () const { return get_be32(d); } +}; + + +class LE16 +{ + unsigned char d[2]; + +public: + LE16& operator = (const LE16 &v) { memcpy(d, v.d, sizeof(d)); return *this; } + + LE16& operator = (unsigned v) { set_le16(d, v); return *this; } + LE16& operator += (unsigned v) { set_le16(d, get_le16(d) + v); return *this; } + LE16& operator -= (unsigned v) { set_le16(d, get_le16(d) - v); return *this; } + LE16& operator &= (unsigned v) { set_le16(d, get_le16(d) & v); return *this; } + LE16& operator |= (unsigned v) { set_le16(d, get_le16(d) | v); return *this; } + + operator const unsigned () const { return get_le16(d); } +}; + + +class LE32 +{ + unsigned char d[4]; + +public: + LE32& operator = (const LE32 &v) { memcpy(d, v.d, sizeof(d)); return *this; } + + LE32& operator = (unsigned v) { set_le32(d, v); return *this; } + LE32& operator += (unsigned v) { set_le32(d, get_le32(d) + v); return *this; } + LE32& operator -= (unsigned v) { set_le32(d, get_le32(d) - v); return *this; } + LE32& operator &= (unsigned v) { set_le32(d, get_le32(d) & v); return *this; } + LE32& operator |= (unsigned v) { set_le32(d, get_le32(d) | v); return *this; } + + operator const unsigned () const { return get_le32(d); } +}; + + +/************************************************************************* +// global operators +**************************************************************************/ + +inline bool operator < (const BE16& v1, const BE16& v2) +{ + return (const unsigned)v1 < (const unsigned)v2; +} + +inline bool operator < (const BE32& v1, const BE32& v2) +{ + return (const unsigned)v1 < (const unsigned)v2; +} + +inline bool operator < (const LE16& v1, const LE16& v2) +{ + return (const unsigned)v1 < (const unsigned)v2; +} + +inline bool operator < (const LE32& v1, const LE32& v2) +{ + return (const unsigned)v1 < (const unsigned)v2; +} + + +/************************************************************************* +// misc +**************************************************************************/ + +// for use with qsort() +int be16_compare(const void *e1, const void *e2); +int be32_compare(const void *e1, const void *e2); +int le16_compare(const void *e1, const void *e2); +int le32_compare(const void *e1, const void *e2); + + +#endif /* already included */ + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/c_file.cpp b/src/c_file.cpp new file mode 100644 index 0000000000..6ed6632463 --- /dev/null +++ b/src/c_file.cpp @@ -0,0 +1,95 @@ +/* c_file.cpp -- file console output + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(USE_CONSOLE) + +/************************************************************************* +// +**************************************************************************/ + +static int init(FILE *f, int o, int now) +{ + UNUSED(f); + UNUSED(o); + UNUSED(now); + return CON_FILE; +} + + +static int set_fg(FILE *f, int fg) +{ + UNUSED(f); + UNUSED(fg); + return -1; +} + + +static void print0(FILE *f, const char *s) +{ +#if 1 + fputs(s,f); +#else + /* filter out all ANSI sequences */ + int c; + while ((c = *s++) != 0) + { + if (c == '\033' && *s == ']') + { + while (*s && *s != 'm') + s++; + } + else + fputc(c,f); + } +#endif +} + + +static upx_bool intro(FILE *f) +{ + UNUSED(f); + return 0; +} + + +console_t console_file = +{ + init, + set_fg, + print0, + intro +}; + +#endif /* USE_CONSOLE */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/c_init.cpp b/src/c_init.cpp new file mode 100644 index 0000000000..1b43f2cad0 --- /dev/null +++ b/src/c_init.cpp @@ -0,0 +1,165 @@ +/* c_init.cpp -- console initialization + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +FILE *con_term = NULL; + +#if defined(USE_CONSOLE) + +/************************************************************************* +// +**************************************************************************/ + +static console_t * const me = &console_init; +console_t * con = &console_init; + +int con_mode = CON_INIT; + + +static void try_init(console_t *c, FILE *f) +{ + int k; + + assert(c); + assert(c->init); + k = c->init(f,opt->console,con_mode); + if (k == CON_INIT) + return; +#if 0 + if (con_mode != CON_INIT && opt->console != CON_INIT) + if (k != opt->console) + return; +#endif + if (k > con_mode) + { + con_mode = k; + con = c; + con->init = 0; + if (!con->set_fg) + con->set_fg = console_none.set_fg; + if (!con->print0) + con->print0 = console_none.print0; + if (!con->intro) + con->intro = console_none.intro; + } +} + + +static int do_init(FILE *f) +{ + assert(con_mode == CON_INIT); + + try_init(&console_none,f); + assert(con != me); + assert(con == &console_none); + if (opt->console == CON_NONE || opt->to_stdout) + return con_mode; + try_init(&console_file,f); + if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) + return con_mode; + +#if defined(USE_ANSI) + try_init(&console_ansi_mono,f); + try_init(&console_ansi_color,f); +#endif +#if defined(USE_SCREEN) + try_init(&console_screen,f); +#endif +#if defined(USE_AALIB) + try_init(&console_aalib,f); +#endif + + return con_mode; +} + + +/************************************************************************* +// +**************************************************************************/ + +static int init(FILE *f, int o, int now) +{ + if (con != me) + return con_mode; + assert(o == -1); + assert(now == -1); + UNUSED(o); + UNUSED(now); + return do_init(f); +} + + +static int set_fg(FILE *f, int fg) +{ + if (con == me) + init(f,-1,-1); + assert(con != me); + return con->set_fg(f,fg); +} + + +static upx_bool intro(FILE *f) +{ + if (con == me) + init(f,-1,-1); + assert(con != me); + return con->intro(f); +} + + +console_t console_init = +{ + init, + set_fg, + 0, + intro +}; + + +void con_fprintf(FILE *f, const char *format, ...) +{ + va_list args; + char buf[80*25]; + + va_start(args,format); + upx_vsnprintf(buf,sizeof(buf),format,args); + va_end(args); + + if (con == me) + init(f,-1,-1); + assert(con != me); + con->print0(f,buf); +} + +#endif /* USE_CONSOLE */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/c_none.cpp b/src/c_none.cpp new file mode 100644 index 0000000000..43b615879f --- /dev/null +++ b/src/c_none.cpp @@ -0,0 +1,81 @@ +/* c_none.cpp -- dummy console output + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(USE_CONSOLE) + +/************************************************************************* +// +**************************************************************************/ + +static int init(FILE *f, int o, int now) +{ + UNUSED(f); + UNUSED(o); + UNUSED(now); + return CON_NONE; +} + + +static int set_fg(FILE *f, int fg) +{ + UNUSED(f); + UNUSED(fg); + return -1; +} + + +static void print0(FILE *f, const char *s) +{ + UNUSED(f); + UNUSED(s); +} + + +static upx_bool intro(FILE *f) +{ + UNUSED(f); + return 0; +} + + +console_t console_none = +{ + init, + set_fg, + print0, + intro +}; + +#endif /* USE_CONSOLE */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/c_screen.cpp b/src/c_screen.cpp new file mode 100644 index 0000000000..afdd2fa981 --- /dev/null +++ b/src/c_screen.cpp @@ -0,0 +1,286 @@ +/* c_screen.cpp -- screen driver console output + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(USE_SCREEN) + +#include "screen.h" + +#define mask_fg 0x0f +#define mask_bg 0xf0 + + +/************************************************************************* +// +**************************************************************************/ + +static screen_t *do_construct(screen_t *s, int fd) +{ + if (!s) + return NULL; + if (s->init(s,fd) != 0) + { + s->destroy(s); + return NULL; + } + return s; +} + + +static screen_t *screen = NULL; + +static void do_destroy(void) +{ + if (screen) + { + screen->destroy(screen); + screen = NULL; + } +} + + +static int mode = -1; +static int init_fg = -1; +static int init_bg = -1; +static int cur_fg = -1; +static int cur_bg = -1; + + +static int init(FILE *f, int o, int now) +{ + int fd = fileno(f); + int n = CON_INIT; + + UNUSED(now); + assert(screen == NULL); + atexit(do_destroy); +#if defined(__DJGPP__) + if (!screen) + screen = do_construct(screen_djgpp2_construct(),fd); +#endif +#if defined(__MFX_WIN32) + if (!screen) + screen = do_construct(screen_win32_construct(),fd); +#endif +#if defined(USE_SCREEN_VCSA) + if (!screen) + screen = do_construct(screen_vcsa_construct(),fd); +#endif +#if defined(USE_SCREEN_CURSES) + if (!screen && o == CON_SCREEN) + screen = do_construct(screen_curses_construct(),fd); +#endif + if (!screen) + return CON_INIT; + mode = screen->getMode(screen); + init_fg = cur_fg = screen->getFg(screen); + init_bg = cur_bg = screen->getBg(screen); + if (screen->isMono(screen)) + cur_fg = -1; + if (screen->getCols(screen) < 80 || screen->getCols(screen) > 256) + return CON_INIT; + if (screen->getRows(screen) < 24) + return CON_INIT; + if (cur_fg == (cur_bg >> 4)) + return CON_INIT; + if (cur_bg != BG_BLACK) + if (!screen->isMono(screen)) + { + /* return CON_ANSI_MONO; */ /* we could emulate ANSI mono */ + return CON_INIT; + } + + if (o == CON_SCREEN) + n = CON_SCREEN; + if (o == CON_INIT) /* use by default */ + n = CON_SCREEN; + if (o == CON_ANSI_COLOR) /* can emulate ANSI color */ + n = CON_ANSI_COLOR; + if (o == CON_ANSI_MONO) /* can emulate ANSI mono */ + n = CON_ANSI_MONO; + + if (screen->atExit) + atexit(screen->atExit); + return n; +} + + +static int set_fg(FILE *f, int fg) +{ + const int last_fg = cur_fg; + int f1 = fg & mask_fg; + int f2 = init_fg & mask_fg; + + UNUSED(f); + cur_fg = fg; + if (screen->isMono(screen)) + { + const int b = (init_bg & mask_bg) >> 4; + if (fg == -1) /* restore startup fg */ + f1 = f2; + else if (b == 0) + f1 = (f2 <= 8) ? 15 : 8; + else if (b <= 8) + f1 = (f2 == 0) ? 15 : 0; + else + f1 = (f2 == 0) ? 8 : 0; + } + else if (con_mode == CON_ANSI_MONO && f1 != f2) + { + f1 = f2 ^ 0x08; + } + + screen->setFg(screen,f1 & mask_fg); + return last_fg; +} + + +static void print0(FILE *f, const char *ss) +{ + int cx, cy; + int c_cx, c_cy; + char p[256+1]; + int pi = 0, px = -1, py = -1; + const int sx = screen->getCols(screen); + const int sy = screen->getRows(screen); + int pass; + + // Note: + // We use 2 passes to avoid unnecessary system calls because + // scrollScreen() under Win32 is *extremely* slow. + UNUSED(f); + screen->getCursor(screen,&cx,&cy); + c_cx = cx; c_cy = cy; + for (pass = 0; pass < 2; pass++) + { + const char *s = ss; + int scroll_y = 0; + while (*s) + { + for ( ; *s; s++) + { + if (*s == '\n') + { + c_cy++; + c_cx = 0; + } + else if (*s == '\r') + { + c_cx = 0; +#if 1 + if (pass > 0 && c_cy < sy) + screen->clearLine(screen,c_cy); +#endif + } + else + break; + } + if (c_cx >= sx) + { + c_cy++; + c_cx = 0; + } + if (pass > 0 && pi > 0 && py != c_cy) + { + screen->putString(screen,p,px,py); + pi = 0; + } + if (c_cy >= sy) + { + int l = c_cy - sy + 1; + if (pass > 0) + c_cy -= screen->scrollUp(screen,l); + else + { + scroll_y += l; + c_cy -= l; + } + if (c_cy < 0) + c_cy = 0; + c_cx = 0; + } + if (*s) + { + if (pass > 0) + { + if (pi == 0) + px = c_cx, py = c_cy; + p[pi++] = *s; + p[pi] = 0; + } + c_cx++; + s++; + } + } + if (pass == 0) + { + c_cx = cx; + if (scroll_y > 0) + { + c_cy -= screen->scrollUp(screen,scroll_y); + if (c_cy < 0) + c_cy = 0; + } + else + c_cy = cy; + } + } + if (pi > 0) + screen->putString(screen,p,px,py); + screen->setCursor(screen,c_cx,c_cy); + screen->refresh(screen); +} + + +static upx_bool intro(FILE *f) +{ + UNUSED(f); +#if defined(USE_FRAMES) + if (screen->intro) + return screen->intro(screen,screen_show_frames); +#endif + return 0; +} + + +console_t console_screen = +{ + init, + set_fg, + print0, + intro +}; + + +#endif /* USE_SCREEN */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/compress.cpp b/src/compress.cpp new file mode 100644 index 0000000000..ed2964e9e3 --- /dev/null +++ b/src/compress.cpp @@ -0,0 +1,269 @@ +/* compress.cpp -- interface to the compression library + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(WITH_UCL) && !defined(WITH_NRV) +# define nrv2b_99_compress_internal ucl_nrv2b_99_compress +# define nrv2d_99_compress_internal ucl_nrv2d_99_compress +# if 0 && defined(__i386__) +# include +# define nrv2b_decompress_safe_8 ucl_nrv2b_decompress_asm_safe_8 +# define nrv2b_decompress_safe_le16 ucl_nrv2b_decompress_asm_safe_le16 +# define nrv2b_decompress_safe_le32 ucl_nrv2b_decompress_asm_safe_le32 +# define nrv2d_decompress_safe_8 ucl_nrv2d_decompress_asm_safe_8 +# define nrv2d_decompress_safe_le16 ucl_nrv2d_decompress_asm_safe_le16 +# define nrv2d_decompress_safe_le32 ucl_nrv2d_decompress_asm_safe_le32 +# else +# define nrv2b_decompress_safe_8 ucl_nrv2b_decompress_safe_8 +# define nrv2b_decompress_safe_le16 ucl_nrv2b_decompress_safe_le16 +# define nrv2b_decompress_safe_le32 ucl_nrv2b_decompress_safe_le32 +# define nrv2d_decompress_safe_8 ucl_nrv2d_decompress_safe_8 +# define nrv2d_decompress_safe_le16 ucl_nrv2d_decompress_safe_le16 +# define nrv2d_decompress_safe_le32 ucl_nrv2d_decompress_safe_le32 +# endif +#endif + +#if defined(WITH_NRV) +# include +# include +# if !defined(NRV_VERSION) || (NRV_VERSION < 0x007300L) +# error +# endif +# if 1 && defined(__i386__) +# define nrv2b_decompress_safe_8 nrv2b_decompress_asm_safe_8 +# define nrv2b_decompress_safe_le16 nrv2b_decompress_asm_safe_le16 +# define nrv2d_decompress_safe_8 nrv2d_decompress_asm_safe_8 +# define nrv2d_decompress_safe_le16 nrv2d_decompress_asm_safe_le16 +# if (NRV_VERSION < 0x008000L) +# define nrv2b_decompress_safe_le32 nrv2b_decompress_asm_safe +# define nrv2d_decompress_safe_le32 nrv2d_decompress_asm_safe +# else +# define nrv2b_decompress_safe_le32 nrv2b_decompress_asm_safe_le32 +# define nrv2d_decompress_safe_le32 nrv2d_decompress_asm_safe_le32 +# endif +# endif + NRV_EXTERN_CDECL(int) nrv2b_99_compress_internal(...); + NRV_EXTERN_CDECL(int) nrv2d_99_compress_internal(...); + NRV_EXTERN_CDECL(int) nrv2b_decompress_safe_8(...); + NRV_EXTERN_CDECL(int) nrv2b_decompress_safe_le16(...); + NRV_EXTERN_CDECL(int) nrv2b_decompress_safe_le32(...); + NRV_EXTERN_CDECL(int) nrv2d_decompress_safe_8(...); + NRV_EXTERN_CDECL(int) nrv2d_decompress_safe_le16(...); + NRV_EXTERN_CDECL(int) nrv2d_decompress_safe_le32(...); +#endif + +#if 0 && defined(WITH_ZLIB) && defined(M_ZLIB) +# include +#endif + + +/************************************************************************* +// +**************************************************************************/ + +int upx_compress ( const upx_byte *src, upx_uint src_len, + upx_byte *dst, upx_uint *dst_len, + upx_progress_callback_t *cb, + int method, int level, + const struct upx_compress_config_t *conf_parm, + upx_uintp result) +{ + struct upx_compress_config_t conf; + upx_uint result_buffer[16]; + int r = UPX_E_ERROR; + + assert(level > 0); + memset(&conf, 0xff, sizeof(conf)); + if (conf_parm) + conf = *conf_parm; // struct copy + if (!result) + result = result_buffer; + + // assume no info available - fill in worst case results + //result[0] = 1; // min_offset_found - NOT USED + result[1] = src_len - 1; // max_offset_found + //result[2] = 2; // min_match_found - NOT USED + result[3] = src_len - 1; // max_match_found + //result[4] = 1; // min_run_found - NOT USED + result[5] = src_len; // max_run_found + result[6] = 1; // first_offset_found + //result[7] = 999999; // same_match_offsets_found - NOT USED + +#if 0 && defined(WITH_ZLIB) && defined(M_ZLIB) + if (method == M_ZLIB) + { + uLong destLen = src_len + src_len / 8 + 256; + r = compress2(dst, &destLen, src, src_len, UPX_MIN(level, 9)); + *dst_len = destLen; + if (r == Z_MEM_ERROR) + return UPX_E_OUT_OF_MEMORY; + if (r != Z_OK) + return UPX_E_ERROR; + return UPX_E_OK; + } +#endif + + // prepare bit-buffer settings + conf.bb_endian = 0; + conf.bb_size = 0; + if (method == M_NRV2B_LE32 || method == M_NRV2D_LE32) + conf.bb_size = 32; + else if (method == M_NRV2B_8 || method == M_NRV2D_8) + conf.bb_size = 8; + else if (method == M_NRV2B_LE16 || method == M_NRV2D_LE16) + conf.bb_size = 16; + else + throwInternalError("unknown compression method"); + +#if 1 && defined(WITH_NRV) + if (level == 1 && conf.bb_size == 32 && + conf.max_offset == UPX_UINT_MAX && conf.max_match == UPX_UINT_MAX) + { + if (method == M_NRV2B_LE32) + { + upx_byte wrkmem[NRV2B_1_16_MEM_COMPRESS]; +#if defined(__CHECKER__) || defined(__PURIFY__) + memset(wrkmem,0,NRV2B_1_16_MEM_COMPRESS); +#endif + r = nrv2b_1_16_compress(src, src_len, dst, dst_len, wrkmem); + } + else if (method == M_NRV2D_LE32) + { + upx_byte wrkmem[NRV2D_1_16_MEM_COMPRESS]; +#if defined(__CHECKER__) || defined(__PURIFY__) + memset(wrkmem,0,NRV2D_1_16_MEM_COMPRESS); +#endif + r = nrv2d_1_16_compress(src, src_len, dst, dst_len, wrkmem); + } + else + throwInternalError("unknown compression method"); + return r; + } +#endif + + // optimize compression parms + if (level <= 3 && conf.max_offset == UPX_UINT_MAX) + conf.max_offset = 8*1024-1; + else if (level == 4 && conf.max_offset == UPX_UINT_MAX) + conf.max_offset = 32*1024-1; +#if defined(WITH_NRV) + else if (level <= 7 && conf.max_offset == UPX_UINT_MAX) + conf.max_offset = 1024*1024-1; + else if (level <= 8 && conf.max_offset == UPX_UINT_MAX) + conf.max_offset = 2048*1024-1; + else if (level <= 10 && conf.max_offset == UPX_UINT_MAX) + conf.max_offset = 4096*1024-1; +#endif + + if M_IS_NRV2B(method) + r = nrv2b_99_compress_internal(src, src_len, dst, dst_len, + cb, level, &conf, result); + else if M_IS_NRV2D(method) + r = nrv2d_99_compress_internal(src, src_len, dst, dst_len, + cb, level, &conf, result); + else + throwInternalError("unknown compression method"); + + return r; +} + + +/************************************************************************* +// +**************************************************************************/ + +int upx_decompress ( const upx_byte *src, upx_uint src_len, + upx_byte *dst, upx_uint *dst_len, + int method ) +{ + int r = UPX_E_ERROR; + +#if 0 && defined(WITH_ZLIB) && defined(M_ZLIB) + if (method == M_ZLIB) + { + uLong destLen = *dst_len; + r = uncompress(dst, &destLen, src, src_len); + *dst_len = destLen; + if (r == Z_MEM_ERROR) + return UPX_E_OUT_OF_MEMORY; + if (r != Z_OK) + return UPX_E_ERROR; + return UPX_E_OK; + } +#endif + + if (method == M_NRV2B_LE32) + r = nrv2b_decompress_safe_le32(src,src_len,dst,dst_len,NULL); + else if (method == M_NRV2B_8) + r = nrv2b_decompress_safe_8(src,src_len,dst,dst_len,NULL); + else if (method == M_NRV2B_LE16) + r = nrv2b_decompress_safe_le16(src,src_len,dst,dst_len,NULL); + else if (method == M_NRV2D_LE32) + r = nrv2d_decompress_safe_le32(src,src_len,dst,dst_len,NULL); + else if (method == M_NRV2D_8) + r = nrv2d_decompress_safe_8(src,src_len,dst,dst_len,NULL); + else if (method == M_NRV2D_LE16) + r = nrv2d_decompress_safe_le16(src,src_len,dst,dst_len,NULL); + else + throwInternalError("unknown decompression method"); + return r; +} + + +/************************************************************************* +// +**************************************************************************/ + +int upx_test_overlap ( const upx_byte *buf, upx_uint src_off, + upx_uint src_len, upx_uint *dst_len, + int method ) +{ + int r = UPX_E_ERROR; + + if (method == M_NRV2B_LE32) + r = ucl_nrv2b_test_overlap_le32(buf,src_off,src_len,dst_len,NULL); + else if (method == M_NRV2B_8) + r = ucl_nrv2b_test_overlap_8(buf,src_off,src_len,dst_len,NULL); + else if (method == M_NRV2B_LE16) + r = ucl_nrv2b_test_overlap_le16(buf,src_off,src_len,dst_len,NULL); + else if (method == M_NRV2D_LE32) + r = ucl_nrv2d_test_overlap_le32(buf,src_off,src_len,dst_len,NULL); + else if (method == M_NRV2D_8) + r = ucl_nrv2d_test_overlap_8(buf,src_off,src_len,dst_len,NULL); + else if (method == M_NRV2D_LE16) + r = ucl_nrv2d_test_overlap_le16(buf,src_off,src_len,dst_len,NULL); + else + throwInternalError("unknown decompression method"); + return r; +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/conf.h b/src/conf.h new file mode 100644 index 0000000000..f45a6d0a0e --- /dev/null +++ b/src/conf.h @@ -0,0 +1,613 @@ +/* conf.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_CONF_H +#define __UPX_CONF_H + +#if 0 && defined(__EMX__) +# include +#endif + +#if defined(UPX_CONFIG_H) +# include UPX_CONFIG_H +#endif +#include + +#include "version.h" +#include "tailor.h" + +#if !defined(__i386__) +# if defined(__386__) || defined(_M_IX86) +# define __i386__ 1 +# endif +#endif +#if defined(__linux__) && !defined(__unix__) +# define __unix__ 1 +#endif + +// just in case +#undef dos +#undef linux +#undef tos +#undef unix + + +#if defined(WITH_UCL) +# include +# include +# if !defined(UPX_UINT_MAX) +# define UPX_UINT_MAX UCL_UINT_MAX +# define upx_uint ucl_uint +# define upx_voidp ucl_voidp +# define upx_uintp ucl_uintp +# define upx_byte ucl_byte +# define upx_bytep ucl_bytep +# define upx_bool ucl_bool +# define upx_progress_callback_t ucl_progress_callback_t +# define upx_adler32 ucl_adler32 +# define UPX_E_OK UCL_E_OK +# define UPX_E_ERROR UCL_E_ERROR +# define UPX_E_OUT_OF_MEMORY UCL_E_OUT_OF_MEMORY +# define __UPX_ENTRY __UCL_ENTRY +# endif +#endif +#if defined(WITH_NRV) +# include +# if !defined(UPX_UINT_MAX) +# define UPX_UINT_MAX NRV_UINT_MAX +# define upx_uint nrv_uint +# define upx_voidp nrv_voidp +# define upx_uintp nrv_uintp +# define upx_byte nrv_byte +# define upx_bytep nrv_bytep +# define upx_bool nrv_bool +# define upx_progress_callback_t nrv_progress_callback_t +# define upx_adler32 nrv_adler32 +# define UPX_E_OK NRV_E_OK +# define UPX_E_ERROR NRV_E_ERROR +# define UPX_E_OUT_OF_MEMORY NRV_E_OUT_OF_MEMORY +# define __UPX_ENTRY __NRV_ENTRY +# endif +#endif +#ifndef WITH_ZLIB +# define WITH_ZLIB 1 +#endif +#if !defined(UPX_UINT_MAX) || (UINT_MAX < 0xffffffffL) +# error "you lose" +#endif +#if !defined(WITH_UCL) +# error "you lose" +#endif +#if !defined(UCL_VERSION) || (UCL_VERSION < 0x009100L) +# error "please upgrade your UCL installation" +#endif + + +/************************************************************************* +// system includes +**************************************************************************/ + +#if !defined(NO_SYS_TYPES_H) +# include +#endif + +#define NDEBUG +#undef NDEBUG +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(NO_FCNTL_H) +# include +#endif +#if !defined(NO_SYS_STAT_H) +# include +#endif +#if defined(HAVE_IO_H) && !defined(NO_IO_H) +# include +#endif +#if defined(HAVE_DOS_H) && !defined(NO_DOS_H) +# include +#endif +#if defined(HAVE_MALLOC_H) && !defined(NO_MALLOC_H) +# include +#endif +#if defined(HAVE_ALLOCA_H) && !defined(NO_ALLOCA_H) +# include +#endif +#if defined(HAVE_SIGNAL_H) +# include +#endif +#if defined(HAVE_UNISTD_H) +# include +#endif +#if defined(TIME_WITH_SYS_TIME) +# include +# include +#else +# include +#endif +#if defined(HAVE_UTIME_H) +# include +#elif defined(HAVE_SYS_UTIME_H) +# include +#endif +#if defined(HAVE_SHARE_H) +# include +#endif + + +// malloc debuggers +#if defined(WITH_DMALLOC) +# define DMALLOC_FUNC_CHECK +# include +#elif defined(WITH_GC) +# define GC_DEBUG +# include +# undef malloc +# undef realloc +# undef free +# define malloc GC_MALLOC +# define realloc GC_REALLOC +# define free GC_FREE +#elif defined(WITH_MSS) +# define MSS +# include +#endif + + +/************************************************************************* +// portab +**************************************************************************/ + +#if defined(NO_BOOL) +typedef int bool; +enum { false, true }; +#endif + +#if !defined(PATH_MAX) +# define PATH_MAX 512 +#elif (PATH_MAX < 512) +# undef PATH_MAX +# define PATH_MAX 512 +#endif + + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif +#ifndef SIGTYPEENTRY +# define SIGTYPEENTRY +#endif +typedef RETSIGTYPE (SIGTYPEENTRY *sig_type)(int); + +#undef MODE_T +#if defined(HAVE_MODE_T) +# define MODE_T mode_t +#else +# define MODE_T int +#endif + +#if !defined(HAVE_STRCHR) +# if defined(HAVE_INDEX) +# define strchr index +# endif +#endif +#if !defined(HAVE_STRCASECMP) +# if defined(HAVE_STRICMP) +# define strcasecmp stricmp +# else +# define strcasecmp strcmp +# endif +#endif +#if !defined(HAVE_STRNCASECMP) +# if defined(HAVE_STRNICMP) +# define strncasecmp strnicmp +# else +# define strncasecmp strncmp +# endif +#endif + + +#ifndef STDIN_FILENO +# define STDIN_FILENO (fileno(stdin)) +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO (fileno(stdout)) +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO (fileno(stderr)) +#endif + +#if !defined(S_IFMT) && defined(_S_IFMT) +# define S_IFMT _S_IFMT +#endif +#if !defined(S_IFREG) && defined(_S_IFREG) +# define S_IFREG _S_IFREG +#endif +#if !defined(S_IFDIR) && defined(_S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif +#if !defined(S_IFCHR) && defined(_S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +#if !defined(S_ISREG) +# if defined(S_IFMT) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# else +# error S_ISREG +# endif +#endif +#if !defined(S_ISDIR) +# if defined(S_IFMT) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# else +# error S_ISDIR +# endif +#endif +#if !defined(S_ISCHR) +# if defined(S_IFMT) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +# endif +#endif + +// avoid warnings about shadowing that obsolete index() function +#define index upx_index + + +/************************************************************************* +// file io +**************************************************************************/ + +#if defined(HAVE_SETMODE) +# if !defined(O_BINARY) +# error "setmode without O_BINARY" +# endif +# define USE_SETMODE 1 +#endif + +#if !defined(O_BINARY) +# define O_BINARY 0 +#endif + +#if defined(__DJGPP__) +# undef sopen +# undef USE_SETMODE +#endif + + +/************************************************************************* +// memory util +**************************************************************************/ + +#undef FREE +#define FREE(ptr) if (ptr) { free(ptr); ptr = NULL; } + +#undef UNUSED +#if defined(__BORLANDC__) +#define UNUSED(parm) ((void)(parm)) +#else +#define UNUSED(parm) (parm = parm) +#endif + +#define HIGH(array) ((unsigned) (sizeof(array)/sizeof((array)[0]))) + +#define ALIGN_DOWN(a,b) (((a) / (b)) * (b)) +#define ALIGN_UP(a,b) ALIGN_DOWN((a) + ((b) - 1), b) + +#define UPX_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define UPX_MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define UPX_MAX3(a,b,c) ((a) >= (b) ? UPX_MAX(a,c) : UPX_MAX(b,c)) +#define UPX_MIN3(a,b,c) ((a) <= (b) ? UPX_MIN(a,c) : UPX_MIN(b,c)) + + +#if 0 && defined(__cplusplus) +// global operators - debug +inline void *operator new(size_t l) +{ + void *p = malloc(l); + printf("new %6ld %p\n",(long)l,p); + fflush(stdout); + return p; +} +inline void *operator new[](size_t l) +{ + void *p = malloc(l); + printf("new %6ld %p\n",(long)l,p); + fflush(stdout); + return p; +} +inline void operator delete(void *p) +{ + printf("delete %p\n",p); + fflush(stdout); + if (p) free(p); +} +inline void operator delete[](void *p) +{ + printf("delete %p\n",p); + fflush(stdout); + if (p) free(p); +} +#endif + + +// A autoheap_array allocates memory on the heap, but automatically +// gets destructed when leaving scope or on exceptions. +// "var" is declared as a read-only reference to a pointer +// and behaves exactly like an array "var[]". +#if 0 +# define autoheap_array(type, var, size) \ + assert((int)(size) > 0); \ + vector var ## _autoheap_vec((size)); \ + type * const & var = & var ## _autoheap_vec[0] +#else +# define autoheap_array(type, var, size) \ + assert((int)(size) > 0); \ + MemBuffer var ## _autoheap_buf((size)*(sizeof(type))); \ + type * const & var = (type *) (unsigned char *) var ## _autoheap_buf +#endif + + +/************************************************************************* +// +**************************************************************************/ + +/* exit codes of this program: 0 ok, 1 error, 2 warning */ +#define EXIT_OK 0 +#define EXIT_ERROR 1 +#define EXIT_WARN 2 + +#define EXIT_USAGE 1 +#define EXIT_FILE_READ 1 +#define EXIT_FILE_WRITE 1 +#define EXIT_MEMORY 1 +#define EXIT_CHECKSUM 1 +#define EXIT_INIT 1 +#define EXIT_INTERNAL 1 + + +// compression methods - DO NOT CHANGE +#define M_NRV2B_LE32 2 +#define M_NRV2B_8 3 +#define M_NRV2B_LE16 4 +#define M_NRV2D_LE32 5 +#define M_NRV2D_8 6 +#define M_NRV2D_LE16 7 + +#define M_IS_NRV2B(x) ((x) >= M_NRV2B_LE32 && (x) <= M_NRV2B_LE16) +#define M_IS_NRV2D(x) ((x) >= M_NRV2D_LE32 && (x) <= M_NRV2D_LE16) + + +/************************************************************************* +// globals +**************************************************************************/ + +#include "unupx.h" + + +#if defined(__cplusplus) + +#include "stdcxx.h" +#include "except.h" +#include "bele.h" +#include "util.h" +#include "console.h" + +// options +enum { + CMD_NONE, + CMD_COMPRESS, CMD_DECOMPRESS, CMD_TEST, CMD_LIST, CMD_FILEINFO, + CMD_HELP, CMD_LICENSE, CMD_VERSION +}; + +struct options_t { + int cmd; + int method; + int level; // compression level 1..10 + int mem_level; // memory level 1..9 + int filter; // preferred filter from Packer::getFilters() + bool all_filters; // try all filters ? + + int console; + int debug; + int force; + int info_mode; + bool ignorewarn; + int backup; + bool no_env; + bool no_progress; + const char *output_name; + const char *script_name; + int small; + int verbose; + bool to_stdout; + + // overlay handling + enum { + SKIP_OVERLAY = 0, + COPY_OVERLAY = 1, + STRIP_OVERLAY = 2 + }; + int overlay; + + // compression runtime parameters - see struct ucl_compress_config_t + struct { + upx_uint max_offset; + upx_uint max_match; + int s_level; + int h_level; + int p_level; + int c_flags; + upx_uint m_size; + } crp; + + // CPU + enum { + CPU_DEFAULT = 0, + CPU_8086 = 1, + CPU_286 = 2, + CPU_386 = 3, + CPU_486 = 4, + CPU_586 = 5, + CPU_686 = 6 + }; + int cpu; + + // options for various executable formats + struct { + bool force_stub; + bool no_reloc; + } dos; + struct { + bool coff; + } djgpp2; + struct { + bool split_segments; + } tos; + struct { + unsigned blocksize; + } unix; + struct { + bool le; + } wcle; + struct { + bool compress_exports; + int compress_icons; + bool compress_resources; + int strip_relocs; + } w32pe; +}; + +extern struct options_t * volatile opt; + + +// main.cpp +extern const char *progname; +bool set_ec(int ec); +#if defined(__GNUC__) +void e_exit(int ec) __attribute__((noreturn)); +#else +void e_exit(int ec); +#endif + + +// msg.cpp +void printSetNl(int need_nl); +void printClearLine(FILE *f = NULL); +void printErr(const char *iname, const Throwable *e); +#if defined(__GNUC__) +void printErr(const char *iname, const char *format, ...) + __attribute__((format(printf,2,3))); +void printWarn(const char *iname, const char *format, ...) + __attribute__((format(printf,2,3))); +#else +void printErr(const char *iname, const char *format, ...); +void printWarn(const char *iname, const char *format, ...); +#endif + +#if defined(__GNUC__) +void infoWarning(const char *format, ...) + __attribute__((format(printf,1,2))); +void infoHeader(const char *format, ...) + __attribute__((format(printf,1,2))); +void info(const char *format, ...) + __attribute__((format(printf,1,2))); +#else +void infoWarning(const char *format, ...); +void infoHeader(const char *format, ...); +void info(const char *format, ...); +#endif +void infoHeader(); +void infoWriting(const char *what, long size); + + +// work.cpp +void do_one_file(const char *iname, char *oname); +void do_files(int i, int argc, char *argv[]); + + +// help.cpp +void show_head(void); +void show_help(int x = 0); +void show_license(void); +void show_usage(void); +void show_version(int); + + +// compress.cpp +#if defined(WITH_UCL) +#define upx_compress_config_t ucl_compress_config_t +#elif defined(WITH_NRV) +struct nrv_compress_config_t; +struct nrv_compress_config_t +{ + int bb_endian; + int bb_size; + nrv_uint max_offset; + nrv_uint max_match; + int s_level; + int h_level; + int p_level; + int c_flags; + nrv_uint m_size; +}; +#define upx_compress_config_t nrv_compress_config_t +#endif + +int upx_compress ( const upx_byte *src, upx_uint src_len, + upx_byte *dst, upx_uint *dst_len, + upx_progress_callback_t *cb, + int method, int level, + const struct upx_compress_config_t *conf, + upx_uintp result); +int upx_decompress ( const upx_byte *src, upx_uint src_len, + upx_byte *dst, upx_uint *dst_len, + int method ); +int upx_test_overlap ( const upx_byte *buf, upx_uint src_off, + upx_uint src_len, upx_uint *dst_len, + int method ); + +#endif /* __cplusplus */ + + +#define SCRIPT_MAX 32 + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/config_h/linux.h b/src/config_h/linux.h new file mode 100644 index 0000000000..354b205987 --- /dev/null +++ b/src/config_h/linux.h @@ -0,0 +1,240 @@ +/* pseudo for Linux */ + +#ifndef __UPX_CONFIG_H +#define __UPX_CONFIG_H + +/* $TOP$ */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your memcmp is broken. */ +/* #undef NO_MEMCMP */ + +/* Define to `long' if doesn't define. */ +/* #undef ptrdiff_t */ + +/* The number of bytes in a ptrdiff_t. */ +#define SIZEOF_PTRDIFF_T 4 + +/* The number of bytes in a size_t. */ +#define SIZEOF_SIZE_T 4 + +/* Define when using the dmalloc package. */ +/* #undef WITH_DMALLOC */ + +/* Define if you have the access function. */ +#define HAVE_ACCESS 1 + +/* Define if you have the atoi function. */ +#define HAVE_ATOI 1 + +/* Define if you have the chmod function. */ +#define HAVE_CHMOD 1 + +/* Define if you have the chown function. */ +#define HAVE_CHOWN 1 + +/* Define if you have the ctime function. */ +#define HAVE_CTIME 1 + +/* Define if you have the difftime function. */ +#define HAVE_DIFFTIME 1 + +/* Define if you have the fchmod function. */ +#define HAVE_FCHMOD 1 + +/* Define if you have the fileno function. */ +#define HAVE_FILENO 1 + +/* Define if you have the fstat function. */ +#define HAVE_FSTAT 1 + +/* Define if you have the XXX function. */ +#define HAVE_GETPID 1 + +/* Define if you have the XXX function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the getumask function. */ +/* #undef HAVE_GETUMASK */ + +/* Define if you have the gmtime function. */ +#define HAVE_GMTIME 1 + +/* Define if you have the index function. */ +#define HAVE_INDEX 1 + +/* Define if you have the isatty function. */ +#define HAVE_ISATTY 1 + +/* Define if you have the lstat function. */ +#define HAVE_LSTAT 1 + +/* Define if you have the localtime function. */ +#define HAVE_LOCALTIME 1 + +/* Define if you have the memcmp function. */ +#define HAVE_MEMCMP 1 + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if you have the memmove function. */ +#define HAVE_MEMMOVE 1 + +/* Define if you have the memset function. */ +#define HAVE_MEMSET 1 + +/* Define if you have the mktime function. */ +#define HAVE_MKTIME 1 + +/* Define if you have the setmode function. */ +/* #undef HAVE_SETMODE */ + +/* Define if you have the stat function. */ +#define HAVE_STAT 1 + +/* Define if you have the strcasecmp function. */ +#define HAVE_STRCASECMP 1 + +/* Define if you have the strchr function. */ +#define HAVE_STRCHR 1 + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define if you have the strftime function. */ +#define HAVE_STRFTIME 1 + +/* Define if you have the stricmp function. */ +/* #undef HAVE_STRICMP */ + +/* Define if you have the strncasecmp function. */ +#define HAVE_STRNCASECMP 1 + +/* Define if you have the strnicmp function. */ +/* #undef HAVE_STRNICMP */ + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the tzset function. */ +#define HAVE_TZSET 1 + +/* Define if you have the umask function. */ +#define HAVE_UMASK 1 + +/* Define if you have the utime function. */ +#define HAVE_UTIME 1 + +/* Define if you have the vsnprintf function. */ +#define HAVE_VSNPRINTF 1 + +/* Define if you have the header file. */ +#define HAVE_ASSERT_H 1 + +/* Define if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_CURSES_H 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_KD_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_KDEV_T_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_MAJOR_H 1 + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the header file. */ +#define HAVE_NCURSES_H 1 + +/* Define if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_UTIME_H */ + +/* Define if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* $BOTTOM$ */ + +#if defined(HAVE_GMTIME) && !defined(TIME_WITH_SYS_TIME) +# undef /**/ HAVE_GMTIME +#endif + +#if defined(HAVE_LOCALTIME) && !defined(TIME_WITH_SYS_TIME) +# undef /**/ HAVE_LOCALTIME +#endif + +#if defined(HAVE_STRFTIME) && !defined(TIME_WITH_SYS_TIME) +# undef /**/ HAVE_STRFTIME +#endif + +#if defined(HAVE_SYS_RESOURCE_H) && !defined(TIME_WITH_SYS_TIME) +# undef /**/ HAVE_SYS_RESOURCE_H +#endif + +#if defined(HAVE_SYS_TIMES_H) && !defined(TIME_WITH_SYS_TIME) +# undef /**/ HAVE_SYS_TIMES_H +#endif + +#if (SIZEOF_PTRDIFF_T <= 0) +# undef /**/ SIZEOF_PTRDIFF_T +#endif + +#if (SIZEOF_SIZE_T <= 0) +# undef /**/ SIZEOF_SIZE_T +#endif + +#endif /* already included */ + +/* +vi:ts=4 +*/ diff --git a/src/console.h b/src/console.h new file mode 100644 index 0000000000..ac4fcfda1e --- /dev/null +++ b/src/console.h @@ -0,0 +1,186 @@ +/* console.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + + +/************************************************************************* +// +**************************************************************************/ + +#undef USE_CONSOLE +#undef USE_ANSI +#undef USE_SCREEN +#undef USE_SCREEN_VCSA +#undef USE_SCREEN_CURSES +#undef USE_FRAMES + +#if 1 && defined(__linux__) +# define USE_SCREEN +# define USE_SCREEN_VCSA +# if !defined(HAVE_LINUX_KD_H) +# undef USE_SCREEN +# undef USE_SCREEN_VCSA +# endif +# if !defined(HAVE_LINUX_KDEV_T_H) || !defined(HAVE_LINUX_MAJOR_H) +# undef USE_SCREEN +# undef USE_SCREEN_VCSA +# endif +#endif + +#if 1 && defined(__DJGPP__) +# define USE_SCREEN +#endif + +#if 1 && defined(__MFX_WIN32) +# define USE_SCREEN +#endif + + +#if 0 || defined(NO_ANSI) +# undef USE_ANSI +#endif +#if 0 || defined(NO_SCREEN) +# undef USE_SCREEN +#endif +#if 0 || defined(NO_FRAMES) || !defined(USE_SCREEN) +# undef USE_FRAMES +#endif +#if 1 +# undef USE_FRAMES +#endif + + +#if 0 || defined(USE_ANSI) || defined(USE_SCREEN) +# define USE_CONSOLE +#endif + +#if 0 || defined(NO_CONSOLE) || !defined(USE_CONSOLE) +# undef USE_CONSOLE +# undef USE_ANSI +# undef USE_SCREEN +# undef USE_SCREEN_VCSA +# undef USE_SCREEN_CURSES +# undef USE_FRAMES +#endif + + +/************************************************************************* +// +**************************************************************************/ + +enum { + CON_INIT, + CON_NONE, + CON_FILE, + CON_ANSI_MONO, + CON_ANSI_COLOR, + CON_SCREEN, + CON_UNUSED +}; + + +#if defined(USE_CONSOLE) + +typedef struct +{ + int (*init)(FILE *f, int, int); + int (*set_fg)(FILE *f, int fg); + void (*print0)(FILE *f, const char *s); + upx_bool (*intro)(FILE *f); +} +console_t; + + +#if defined(__GNUC__) +void con_fprintf(FILE *f, const char *format, ...) + __attribute__((format(printf,2,3))); +#else +void con_fprintf(FILE *f, const char *format, ...); +#endif + + +#define FG_BLACK 0x00 +#define FG_BLUE 0x01 +#define FG_GREEN 0x02 +#define FG_CYAN 0x03 +#define FG_RED 0x04 +#define FG_VIOLET 0x05 +#define FG_ORANGE 0x06 +#define FG_LTGRAY 0x07 +#define FG_DKGRAY 0x08 +#define FG_BRTBLUE 0x09 +#define FG_BRTGREEN 0x0a +#define FG_BRTCYAN 0x0b +#define FG_BRTRED 0x0c +#define FG_BRTVIOLET 0x0d +#define FG_YELLOW 0x0e +#define FG_WHITE 0x0f + +#define BG_BLACK 0x00 +#define BG_BLUE 0x10 +#define BG_GREEN 0x20 +#define BG_CYAN 0x30 +#define BG_RED 0x40 +#define BG_VIOLET 0x50 +#define BG_ORANGE 0x60 +#define BG_WHITE 0x70 + +#endif /* USE_CONSOLE */ + + +/************************************************************************* +// +**************************************************************************/ + +extern FILE *con_term; + +#if defined(USE_CONSOLE) + +extern int con_mode; +extern console_t *con; + +extern console_t console_init; +extern console_t console_none; +extern console_t console_file; +extern console_t console_ansi_mono; +extern console_t console_ansi_color; +extern console_t console_screen; + + +#define con_fg(f,x) con->set_fg(f,x) + +#else + +#define con_fg(f,x) 0 +#define con_fprintf fprintf + +#endif /* USE_CONSOLE */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/except.cpp b/src/except.cpp new file mode 100644 index 0000000000..417a11578f --- /dev/null +++ b/src/except.cpp @@ -0,0 +1,138 @@ +/* except.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + + +/************************************************************************* +// compression +**************************************************************************/ + +void throwCantPack(const char *msg) +{ + // UGLY, but makes things easier + if (opt->cmd == CMD_COMPRESS) + throw CantPackException(msg); + else if (opt->cmd == CMD_FILEINFO) + throw CantPackException(msg); + else + throw CantUnpackException(msg); +} + +void throwFilterException() +{ + throwCantPack("filter problem"); +} + +void throwUnknownExecutableFormat(const char *msg, bool warn) +{ + throw UnknownExecutableFormatException(msg, warn); +} + +void throwNotCompressible(const char *msg) +{ + throw NotCompressibleException(msg); +} + +void throwAlreadyPacked(const char *msg) +{ + throw AlreadyPackedException(msg); +} + + +/************************************************************************* +// decompression +**************************************************************************/ + +void throwCantUnpack(const char *msg) +{ + // UGLY, but makes things easier + throwCantPack(msg); +} + +void throwNotPacked(const char *msg) +{ + if (msg == NULL) + msg = "not packed by UPX"; + throw NotPackedException(msg); +} + +void throwChecksumError() +{ + throw Exception("checksum error"); +} + +void throwCompressedDataViolation() +{ + throw Exception("compressed data violation"); +} + + +/************************************************************************* +// other +**************************************************************************/ + +void throwInternalError(const char *msg) +{ + throw InternalError(msg); +} + +void throwBadLoader() +{ + throwInternalError("bad loader"); +} + + +void throwIOException(const char *msg, int e) +{ + throw IOException(msg,e); +} + + +/************************************************************************* +// +**************************************************************************/ + +const char *prettyName(const char *n) +{ + while (*n >= '0' && *n <= '9') // gcc / egcs + n++; + if (strlen(n) > 6 && memcmp(n, "class ", 6) == 0) // Visual C++ + n += 6; + return n; +} + +const char *prettyName(const type_info &ti) +{ + return prettyName(ti.name()); +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/except.h b/src/except.h new file mode 100644 index 0000000000..34479b2f73 --- /dev/null +++ b/src/except.h @@ -0,0 +1,226 @@ +/* except.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_EXCEPT_H +#define __UPX_EXCEPT_H + +#ifdef __cplusplus + +const char *prettyName(const char *n); +const char *prettyName(const type_info &ti); + + +/************************************************************************* +// exceptions +**************************************************************************/ + +class Throwable : public exception +{ +protected: + Throwable(const char *m = 0, int e = 0, bool w = false) + : msg(m), err(e), is_warning(w) { } +public: + virtual ~Throwable() { } + const char *getMsg() const { return msg; } + int getErrno() const { return err; } + bool isWarning() const { return is_warning; } +private: + //Throwable(const Throwable &); +private: +// void * operator new(size_t); // ... + const char *msg; + int err; +protected: + bool is_warning; // can be set by subclasses +}; + + +// Exceptions can/should be caught +class Exception : public Throwable +{ + typedef Throwable super; +public: + Exception(const char *m = 0, int e = 0, bool w = false) : super(m,e,w) { } +}; + + +// Errors should not be caught (or re-thrown) +class Error : public Throwable +{ + typedef Throwable super; +public: + Error(const char *m = 0, int e = 0) : super(m,e) { } +}; + + +/************************************************************************* +// system exception +**************************************************************************/ + +class OutOfMemoryException : public Exception +{ + typedef Exception super; +public: + OutOfMemoryException(const char *m = 0, int e = 0) : super(m,e) { } +}; + + +class IOException : public Exception +{ + typedef Exception super; +public: + IOException(const char *m = 0, int e = 0) : super(m,e) { } +}; + + +class EOFException : public IOException +{ + typedef IOException super; +public: + EOFException(const char *m = 0, int e = 0) : super(m,e) { } +}; + + +class FileNotFoundException : public IOException +{ + typedef IOException super; +public: + FileNotFoundException(const char *m = 0, int e = 0) : super(m,e) { } +}; + + +class FileAlreadyExistsException : public IOException +{ + typedef IOException super; +public: + FileAlreadyExistsException(const char *m = 0, int e = 0) : super(m,e) { } +}; + + +/************************************************************************* +// application execptions +**************************************************************************/ + +class OverlayException : public Exception +{ + typedef Exception super; +public: + OverlayException(const char *m = 0, bool w = false) : super(m,0,w) { } +}; + +class CantPackException : public Exception +{ + typedef Exception super; +public: + CantPackException(const char *m = 0, bool w = false) : super(m,0,w) { } +}; + +class UnknownExecutableFormatException : public CantPackException +{ + typedef CantPackException super; +public: + UnknownExecutableFormatException(const char *m = 0, bool w = false) : super(m,w) { } +}; + +class AlreadyPackedException : public CantPackException +{ + typedef CantPackException super; +public: + AlreadyPackedException(const char *m = 0) : super(m) { is_warning = true; } +}; + +class NotCompressibleException : public CantPackException +{ + typedef CantPackException super; +public: + NotCompressibleException(const char *m = 0) : super(m) { } +}; + + +class CantUnpackException : public Exception +{ + typedef Exception super; +public: + CantUnpackException(const char *m = 0, bool w = false) : super(m,0,w) { } +}; + +class NotPackedException : public CantUnpackException +{ + typedef CantUnpackException super; +public: + NotPackedException(const char *m = 0) : super(m,true) { } +}; + + +/************************************************************************* +// errors +**************************************************************************/ + +class InternalError : public Error +{ + typedef Error super; +public: + InternalError(const char *m = 0) : super(m,0) { } +}; + + +/************************************************************************* +// util +**************************************************************************/ + +#if 0 && defined(__GNUC__) +// (noreturn) is probably not the correct semantics +#define NORET __attribute__((noreturn)) +#else +#define NORET +#endif + +void throwCantPack(const char *msg) NORET; +void throwUnknownExecutableFormat(const char *msg = 0, bool warn = false) NORET; +void throwNotCompressible(const char *msg = 0) NORET; +void throwAlreadyPacked(const char *msg = 0) NORET; +void throwCantUnpack(const char *msg) NORET; +void throwNotPacked(const char *msg = 0) NORET; +void throwFilterException() NORET; +void throwBadLoader() NORET; +void throwChecksumError() NORET; +void throwCompressedDataViolation() NORET; +void throwInternalError(const char *msg) NORET; +void throwIOException(const char *msg = 0, int e = 0) NORET; + +#undef NORET + + +#endif /* __cplusplus */ + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/fcto_ml.ch b/src/fcto_ml.ch new file mode 100644 index 0000000000..b0f1381503 --- /dev/null +++ b/src/fcto_ml.ch @@ -0,0 +1,105 @@ +/* fctl_ml.ch -- filter CTO implementation by ML1050 + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + + +/************************************************************************* +// these are not implemented here +**************************************************************************/ + +// filter: e8, e9, e8e9 +#define f_cto32_e8 NULL +#define f_cto32_e9 NULL +#define f_cto32_e8e9 NULL + +// unfilter: e8, e9, e8e9 +#define u_cto32_e8 NULL +#define u_cto32_e9 NULL +#define u_cto32_e8e9 NULL + +// scan: e8, e9, e8e9 +#define s_cto32_e8 NULL +#define s_cto32_e9 NULL +#define s_cto32_e8e9 NULL + +// filter: e8, e9, e8e9 with bswap be->le +#define f_cto32_e8_bswap_be NULL +#define f_cto32_e9_bswap_be NULL +#define f_cto32_e8e9_bswap_be NULL + +// unfilter: e8, e9, e8e9 with bswap be->le +#define u_cto32_e8_bswap_be NULL +#define u_cto32_e9_bswap_be NULL +#define u_cto32_e8e9_bswap_be NULL + +// scan: e8, e9, e8e9 with bswap be->le +#define s_cto32_e8_bswap_be NULL +#define s_cto32_e9_bswap_be NULL +#define s_cto32_e8e9_bswap_be NULL + + +/************************************************************************* +// +**************************************************************************/ + +#define COND(b,x) (b[x] == 0xe8) +#define F f_cto32_e8_bswap_le +#define U u_cto32_e8_bswap_le +#include "fcto_ml2.ch" +#undef U +#undef F +#define F s_cto32_e8_bswap_le +#include "fcto_ml2.ch" +#undef F +#undef COND + +#define COND(b,x) (b[x] == 0xe9) +#define F f_cto32_e9_bswap_le +#define U u_cto32_e9_bswap_le +#include "fcto_ml2.ch" +#undef U +#undef F +#define F s_cto32_e9_bswap_le +#include "fcto_ml2.ch" +#undef F +#undef COND + +#define COND(b,x) (b[x] == 0xe8 || b[x] == 0xe9) +#define F f_cto32_e8e9_bswap_le +#define U u_cto32_e8e9_bswap_le +#include "fcto_ml2.ch" +#undef U +#undef F +#define F s_cto32_e8e9_bswap_le +#include "fcto_ml2.ch" +#undef F +#undef COND + + +/* +vi:ts=4:et +*/ + diff --git a/src/fcto_ml2.ch b/src/fcto_ml2.ch new file mode 100644 index 0000000000..cce46928f2 --- /dev/null +++ b/src/fcto_ml2.ch @@ -0,0 +1,191 @@ +/* fctl_ml2.ch -- filter CTO implementation by ML1050 + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + + +/************************************************************************* +// +**************************************************************************/ + +static int F(filter_t *f) +{ +#ifdef U + // filter + upx_byte *b = f->buf; + const unsigned addvalue = f->addvalue; +#else + // scan + const upx_byte *b = f->buf; +#endif + const unsigned size = f->buf_len; + + unsigned ic, jc, kc; + unsigned cto; + unsigned char cto8; + unsigned calls = 0, noncalls = 0, noncalls2 = 0; + unsigned lastnoncall = size, lastcall = 0; + + // find a 16MB large empty address space + if (f->forced_cto >= 0 && f->forced_cto <= 255) + cto8 = (unsigned char) f->forced_cto; + else + { + unsigned char buf[256]; + memset(buf,0,256); + +#if 1 + for (ic = 0; ic < size - 5; ic++) + if (COND(b,ic) && get_le32(b+ic+1)+ic+1 >= size) + { + buf[b[ic+1]] |= 1; + } +#else + { + int i = size - 6; + do { + if (COND(b,i) && get_le32(b+i+1)+i+1 >= size) + buf[b[i+1]] |= 1; + } while (--i >= 0); + } +#endif + + ic = 256; + if (f->preferred_ctos) + { + for (const int *pc = f->preferred_ctos; *pc >= 0; pc++) + { + if (buf[*pc & 255] == 0) + { + ic = *pc & 255; + break; + } + } + } +#if 0 + // just a test to see if certain ctos would improve compression + if (ic >= 256) + for (ic = 0; ic < 256; ic += 16) + if (buf[ic] == 0) + break; +#endif + if (ic >= 256) + for (ic = 0; ic < 256; ic++) + if (buf[ic] == 0) + break; + if (ic >= 256) + //throwCantPack("call trick problem"); + return -1; + cto8 = (unsigned char) ic; + } + cto = (unsigned)cto8 << 24; + + for (ic = 0; ic < size - 5; ic++) + { + if (!COND(b,ic)) + continue; + jc = get_le32(b+ic+1)+ic+1; + // try to detect 'real' calls only + if (jc < size) + { +#ifdef U + set_be32(b+ic+1,jc+addvalue+cto); +#endif + if (ic - lastnoncall < 5) + { + // check the last 4 bytes before this call + for (kc = 4; kc; kc--) + if (COND(b,ic-kc) && b[ic-kc+1] == cto8) + break; + if (kc) + { +#ifdef U + // restore original + set_le32(b+ic+1,jc-ic-1); +#endif + if (b[ic+1] == cto8) + return 1; // fail - buffer not restored + lastnoncall = ic; + noncalls2++; + continue; + } + } + calls++; + ic += 4; + lastcall = ic+1; + } + else + { + assert(b[ic+1] != cto8); // this should not happen + lastnoncall = ic; + noncalls++; + } + } + + f->cto = cto8; + f->calls = calls; + f->noncalls = noncalls; + f->lastcall = lastcall; + +#ifdef TESTING + printf("\ncalls=%d noncalls=%d noncalls2=%d text_size=%x calltrickoffset=%x\n",calls,noncalls,noncalls2,size,cto); +#endif + return 0; +} + + +#ifdef U +static int U(filter_t *f) +{ + upx_byte *b = f->buf; + const unsigned size5 = f->buf_len - 5; + const unsigned addvalue = f->addvalue; + const unsigned cto = f->cto << 24; + + unsigned ic, jc; + + for (ic = 0; ic < size5; ic++) + if (COND(b,ic)) + { + jc = get_be32(b+ic+1); + if (b[ic+1] == f->cto) + { + set_le32(b+ic+1,jc-ic-1-addvalue-cto); + f->calls++; + ic += 4; + f->lastcall = ic+1; + } + else + f->noncalls++; + } + return 0; +} +#endif + + +/* +vi:ts=4:et +*/ + diff --git a/src/file.cpp b/src/file.cpp new file mode 100644 index 0000000000..9d00eea25e --- /dev/null +++ b/src/file.cpp @@ -0,0 +1,379 @@ +/* file.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" + + +/************************************************************************* +// +**************************************************************************/ + +void File::chmod(const char *name, int mode) +{ +#if defined(HAVE_CHMOD) + if (::chmod(name,mode) != 0) + throwIOException(name,errno); +#endif +} + + +void File::rename(const char *old_, const char *new_) +{ +#if 1 && defined(__DJGPP__) + if (::_rename(old_,new_) != 0) +#else + if (::rename(old_,new_) != 0) +#endif + throwIOException("rename error",errno); +} + + +void File::unlink(const char *name) +{ + if (::unlink(name) != 0) + throwIOException(name,errno); +} + + +/************************************************************************* +// +**************************************************************************/ + +FileBase::FileBase() : + _fd(-1), _flags(0), _shflags(0), _mode(0), _name(0) +{ + memset(&st,0,sizeof(st)); +} + + +FileBase::~FileBase() +{ +#if 0 && defined(__GNUC__) // debug + if (isOpen()) + fprintf(stderr,"%s: %s\n", _name, __PRETTY_FUNCTION__); +#endif + + // FIXME: we should use close() during exception unwinding but + // closex() otherwise + closex(); +} + + +bool FileBase::close() +{ + bool ok = true; + if (isOpen() && _fd != STDIN_FILENO && _fd != STDOUT_FILENO && _fd != STDERR_FILENO) + if (::close(_fd) == -1) + ok = false; + _fd = -1; + _flags = 0; + _mode = 0; + _name = 0; + return ok; +} + + +void FileBase::closex() +{ + if (!close()) + throwIOException("close failed",errno); +} + + +int FileBase::read(void *buf, int len) +{ + int l; + if (!isOpen() || len < 0) + throwIOException("bad read"); + if (len == 0) + return 0; + for (;;) + { +#if 1 && defined(__DJGPP__) + l = ::_read(_fd,buf,len); +#else + l = ::read(_fd,buf,len); +#endif + if (l < 0) + { +#if defined(EINTR) + if (errno == EINTR) + continue; +#endif + throwIOException("read error",errno); + } + break; + } + return l; +} + + +int FileBase::readx(void *buf, int len) +{ + int l = this->read(buf,len); + if (l != len) + throw EOFException(); + return l; +} + + +void FileBase::write(const void *buf, int len) +{ + int l; + if (!isOpen() || len < 0) + throwIOException("bad write"); + if (len == 0) + return; + for (;;) + { +#if 1 && defined(__DJGPP__) + l = ::_write(_fd,buf,len); +#else + l = ::write(_fd,buf,len); +#endif +#if defined(EINTR) + if (l < 0 && errno == EINTR) + continue; +#endif + if (l != len) + throwIOException("write error",errno); + break; + } +} + + +void FileBase::seek(off_t off, int whence) +{ + if (!isOpen()) + throwIOException("bad seek"); + if (::lseek(_fd,off,whence) < 0) + throwIOException("seek error",errno); +} + + +off_t FileBase::tell() +{ + if (!isOpen()) + throwIOException("bad tell"); + off_t l = ::lseek(_fd,0,SEEK_CUR); + if (l < 0) + throwIOException("tell error",errno); + return l; +} + + +/************************************************************************* +// +**************************************************************************/ + +InputFile::InputFile() +{ +} + + +InputFile::~InputFile() +{ +} + + +void InputFile::sopen(const char *name, int flags, int shflags) +{ + close(); + _name = name; + _flags = flags; + _shflags = shflags; + _mode = 0; + if (shflags < 0) + _fd = ::open(_name,_flags); + else +#if defined(__DJGPP__) + _fd = ::open(_name,_flags | _shflags); +#elif defined(SH_DENYRW) + _fd = ::sopen(_name,_flags,_shflags); +#else + assert(0); +#endif + if (!isOpen()) + { + if (errno == ENOENT) + throw FileNotFoundException(_name,errno); + else if (errno == EEXIST) + throw FileAlreadyExistsException(_name,errno); + else + throwIOException(_name,errno); + } +} + + +int InputFile::read(void * buf, int len) +{ + return super::read(buf,len); +} + + +int InputFile::readx(void * buf, int len) +{ + return super::readx(buf,len); +} + + +void InputFile::seek(off_t off, int whence) +{ + super::seek(off,whence); +} + + +off_t InputFile::tell() +{ + return super::tell(); +} + + +/************************************************************************* +// +**************************************************************************/ + +OutputFile::OutputFile() : + bytes_written(0) +{ +} + + +OutputFile::~OutputFile() +{ +} + + +void OutputFile::sopen(const char *name, int flags, int shflags, int mode) +{ + close(); + _name = name; + _flags = flags; + _shflags = shflags; + _mode = mode; + if (shflags < 0) + _fd = ::open(_name,_flags,_mode); + else +#if defined(__DJGPP__) + _fd = ::open(_name,_flags | _shflags, _mode); +#elif defined(SH_DENYRW) + _fd = ::sopen(_name,_flags,_shflags,_mode); +#else + assert(0); +#endif + if (!isOpen()) + { +#if 0 + // don't throw FileNotFound here - confusing + if (errno == ENOENT) + throw FileNotFoundException(_name,errno); + else +#endif + if (errno == EEXIST) + throw FileAlreadyExistsException(_name,errno); + else + throwIOException(_name,errno); + } +} + + +bool OutputFile::openStdout(int flags, bool force) +{ + close(); + if (!force) + { + if (!isafile(STDOUT_FILENO)) + return false; + } + _fd = STDOUT_FILENO; + _name = ""; + _flags = flags; + _shflags = -1; + _mode = 0; + if (flags != 0) + { + assert(flags == O_BINARY); +#if defined(HAVE_SETMODE) && defined(USE_SETMODE) + if (setmode(_fd, O_BINARY) == -1) + throwIOException(_name,errno); +#if defined(__DJGPP__) + __djgpp_set_ctrl_c(1); +#endif +#endif + } + return true; +} + + +void OutputFile::write(const void * buf, int len) +{ + super::write(buf,len); + bytes_written += len; +} + + +void OutputFile::dump(const char *name, const void *buf, int len, int flags) +{ + if (flags < 0) + flags = O_CREAT | O_BINARY | O_TRUNC; + flags |= O_WRONLY; + OutputFile f; + f.open(name, flags, 0666); + f.write(buf, len); + f.close(); +} + + +/************************************************************************* +// +**************************************************************************/ + +MemoryOutputFile::MemoryOutputFile() : + b(NULL), b_size(0), b_pos(0), bytes_written(0) +{ +} + + +void MemoryOutputFile::write(const void * buf, int len) +{ + if (!isOpen() || len < 0) + throwIOException("bad write"); + if (len == 0) + return; + if (b_pos + len > b_size) + throwIOException("write error",ENOSPC); + memcpy(b + b_pos, buf, len); + b_pos += len; + bytes_written += len; +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/file.h b/src/file.h new file mode 100644 index 0000000000..2b228be077 --- /dev/null +++ b/src/file.h @@ -0,0 +1,174 @@ +/* file.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_FILE_H +#define __UPX_FILE_H + + +/************************************************************************* +// +**************************************************************************/ + +class File +{ +public: + static void chmod(const char *name, int mode); + static void rename(const char *old_, const char *new_); + static void unlink(const char *name); +}; + + +class FileBase : public File +{ +protected: + FileBase(); + virtual ~FileBase(); +public: + virtual bool close(); + virtual void closex(); + virtual bool isOpen() const { return _fd >= 0; } + int getFd() const { return _fd; } + const char *getName() const { return _name; } + +protected: + virtual int read(void *buf, int len); + virtual int readx(void *buf, int len); + virtual void write(const void *buf, int len); + virtual void seek(off_t off, int whence); + virtual off_t tell(); + + int _fd; + int _flags; + int _shflags; + int _mode; + const char *_name; +public: + struct stat st; +}; + + +/************************************************************************* +// +**************************************************************************/ + +class InputFile : public FileBase +{ + typedef FileBase super; +public: + InputFile(); + virtual ~InputFile(); + + virtual void sopen(const char *name, int flags, int shflags); + virtual void open(const char *name, int flags) + { + sopen(name, flags, -1); + } + + virtual int read(void * buf, int len); + virtual int readx(void * buf, int len); + + virtual void seek(off_t off, int whence); + virtual off_t tell(); +}; + + +/************************************************************************* +// +**************************************************************************/ + +class OutputFile : public FileBase +{ + typedef FileBase super; +public: + OutputFile(); + virtual ~OutputFile(); + + virtual void sopen(const char *name, int flags, int shflags, int mode); + virtual void open(const char *name, int flags, int mode) + { + sopen(name, flags, -1, mode); + } + virtual bool openStdout(int flags=0, bool force=false); + + virtual void write(const void *buf, int len); + + off_t getBytesWritten() const { return bytes_written; } + + // FIXME - won't work with `--stdout' option + virtual void seek(off_t off, int whence) + { + super::seek(off,whence); + } + virtual void rewrite(const void *buf, int len) + { + write(buf, len); + bytes_written -= len; + } + + // util + static void dump(const char *name, const void *buf, int len, int flags=-1); + +protected: + off_t bytes_written; +}; + + +/************************************************************************* +// +**************************************************************************/ + +class MemoryOutputFile : public FileBase +{ + typedef FileBase super; +public: + MemoryOutputFile(); + virtual ~MemoryOutputFile() { b = NULL; } + + virtual bool close() { b = NULL; return true; } + virtual bool isOpen() const { return b != NULL; } + virtual void open(void *buf, unsigned size) + { b = (upx_bytep) buf; b_size = size; } + + virtual void write(const void *buf, int len); + + off_t getBytesWritten() const { return bytes_written; } + +protected: + upx_bytep b; + unsigned b_size; + unsigned b_pos; + off_t bytes_written; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/filter.cpp b/src/filter.cpp new file mode 100644 index 0000000000..f2cbe8b3e0 --- /dev/null +++ b/src/filter.cpp @@ -0,0 +1,206 @@ +/* filter.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "filter.h" + + +/************************************************************************* +// util +**************************************************************************/ + +static //inline +void initFilter(Filter *f, upx_byte *buf, unsigned buf_len) +{ + f->buf = buf; + f->buf_len = buf_len; + // clear output parameters + f->calls = f->wrongcalls = f->noncalls = f->lastcall = 0; +} + + +/************************************************************************* +// implementation +**************************************************************************/ + +const FilterImp::f_t *FilterImp::getFilter(int id) +{ + static bool done = false; + static unsigned filter_id[256]; + + if (id < 0 || id > 255) + return NULL; + if (!done) + { + memset(filter_id, 0xff, sizeof(filter_id)); + for (int i = 0; i < n_filters; i++) + filter_id[filters[i].id] = i; + done = true; + } + + unsigned index = filter_id[id]; + if (index > 255) + return NULL; + assert(filters[index].id == id); + return &filters[index]; +} + + +/************************************************************************* +// high level API +**************************************************************************/ + +void Filter::init(int id_, unsigned addvalue_) +{ + this->id = id_; + initFilter(this, NULL, 0); + // clear input parameters + this->addvalue = addvalue_; + this->forced_cto = -1; + this->preferred_ctos = NULL; + // clear input/output parameters + this->cto = 0; +} + + +bool Filter::filter(upx_byte *buf_, unsigned buf_len_) +{ + initFilter(this, buf_, buf_len_); + const FilterImp::f_t *ft = FilterImp::getFilter(id); + if (ft == NULL) + throwInternalError("filter-1"); + if (ft->id == 0) + return true; + if (buf_len < ft->min_buf_len) + return false; + if (ft->max_buf_len && buf_len > ft->max_buf_len) + return false; + if (!ft->f) + throwInternalError("filter-2"); + + // setChecksum + if (clevel != 1) + { + this->adler = upx_adler32(0,NULL,0); + this->adler = upx_adler32(this->adler, this->buf, this->buf_len); + } + //printf("filter: %02x %p %d\n", this->id, this->buf, this->buf_len); + int r = (*ft->f)(this); + //printf("filter: %02x %d\n", ft->id, r); + if (r > 0) + throwFilterException(); + if (r == 0) + return true; + return false; +} + + +bool Filter::unfilter(upx_byte *buf_, unsigned buf_len_, bool vc) +{ + initFilter(this, buf_, buf_len_); + const FilterImp::f_t *ft = FilterImp::getFilter(id); + if (ft == NULL) + throwInternalError("unfilter-1"); + if (ft->id == 0) + return true; + if (buf_len < ft->min_buf_len) + return false; + if (ft->max_buf_len && buf_len > ft->max_buf_len) + return false; + if (!ft->u) + throwInternalError("unfilter-2"); + + //printf("unfilter: %02x %p %d\n", this->id, this->buf, this->buf_len); + int r = (*ft->u)(this); + //printf("unfilter: %02x %d\n", ft->id, r); + if (r != 0) + throwInternalError("unfilter-3"); + + // verifyChecksum + if (vc && clevel != 1) + { + unsigned a = upx_adler32(0,NULL,0); + if (this->adler != upx_adler32(a, this->buf, this->buf_len)) + throwInternalError("unfilter-4"); + } + + return true; +} + + +bool Filter::verifyUnfilter() +{ + // Note: + // This verify is just because of complete paranoia that there + // could be a hidden bug in the filter implementation, and + // it should not be necessary at all. + // Maybe we will remove it at some future point. + // + // See also: + // Packer::verifyOverlappingDecompression() + + //printf("verifyUnfilter: %02x %p %d\n", this->id, this->buf, this->buf_len); + if (clevel == 1) + return true; + return unfilter(this->buf, this->buf_len, true); +} + + +bool Filter::scan(const upx_byte *buf_, unsigned buf_len_) +{ + // Note: must use const_cast here. This is fine as the scan + // implementations (f->s) actually don't change the buffer. + upx_byte *b = const_cast(buf_); + initFilter(this, b, buf_len_); + + const FilterImp::f_t *ft = FilterImp::getFilter(id); + if (ft == NULL) + throwInternalError("filter-1"); + if (ft->id == 0) + return true; + if (buf_len < ft->min_buf_len) + return false; + if (ft->max_buf_len && buf_len > ft->max_buf_len) + return false; + if (!ft->s) + throwInternalError("filter-2"); + + //printf("filter: %02x %p %d\n", this->id, this->buf, this->buf_len); + int r = (*ft->s)(this); + //printf("filter: %02x %d\n", ft->id, r); + if (r > 0) + throwFilterException(); + if (r == 0) + return true; + return false; +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/filter.h b/src/filter.h new file mode 100644 index 0000000000..376a55853c --- /dev/null +++ b/src/filter.h @@ -0,0 +1,129 @@ +/* filter.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_FILTER_H +#define __UPX_FILTER_H + +class Filter; +class FilterImp; + + +/************************************************************************* +// A filter is a reversible operation that modifies a given +// block of memory. +// +// A filter can fail and return false. In this case the buffer +// must be unmodified (or otherwise restored). +// +// If a filter fails and somehow cannot restore the block it must +// call throwFilterException() - this will cause the compression +// to fail. +// +// The return value of unfilters can/should be ignored. They throw +// exceptions in case of errors. +// +// The main idea behind filters is to convert relative jumps and calls +// to absolute addresses so that the buffer compresses better. +**************************************************************************/ + +class Filter +{ +public: + Filter(int level) { clevel = level; init(); } + void init(int id=0, unsigned addvalue=0); + + bool filter(upx_byte *buf, unsigned buf_len); + bool unfilter(upx_byte *buf, unsigned buf_len, bool verify_checksum=false); + bool verifyUnfilter(); + bool scan(const upx_byte *buf, unsigned buf_len); + +public: + // Will be set by each call to filter()/unfilter(). + // Read-only afterwards. + upx_byte *buf; + unsigned buf_len; + + // Checksum of the buffer before applying the filter + // or after un-applying the filter. + unsigned adler; + + // Input parameters used by various filters. + unsigned addvalue; + int forced_cto; + const int *preferred_ctos; + + // Input/output parameters used by various filters + unsigned char cto; // call trick offset + + // Output used by various filters. Read only. + unsigned calls; + unsigned noncalls; + unsigned wrongcalls; + unsigned lastcall; + + // Read only. + int id; + +private: + int clevel; // compression level +}; + + +/************************************************************************* +// We don't want a full OO interface here because of +// certain implementation speed reasons. +// +// This class is strictly private to Filter - don't look. +**************************************************************************/ + +class FilterImp +{ + friend class Filter; + +private: + struct f_t { + int id; + unsigned min_buf_len; + unsigned max_buf_len; + int (*f)(Filter *); + int (*u)(Filter *); + int (*s)(Filter *); + }; + static const f_t filters[]; + static const int n_filters; + + static const f_t *getFilter(int id); +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/filteri.cpp b/src/filteri.cpp new file mode 100644 index 0000000000..2d700d5bc6 --- /dev/null +++ b/src/filteri.cpp @@ -0,0 +1,453 @@ +/* filteri.cpp -- filter implementation (low-level) + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "filter.h" + +#define filter_t Filter +#define set_dummy(p, v) ((void)0) + + +/************************************************************************* +// 16-bit calltrick ("naive") +**************************************************************************/ + +#define CT16(f, cond, addvalue, get, set) \ + upx_byte *b = f->buf; \ + upx_byte *b_end = b + f->buf_len - 3; \ + do { \ + if (cond) \ + { \ + b += 1; \ + unsigned a = (unsigned) (b - f->buf); \ + f->lastcall = a; \ + set(b, get(b) + (addvalue)); \ + f->calls++; \ + b += 2 - 1; \ + } \ + } while (++b < b_end); \ + if (f->lastcall) f->lastcall += 2; \ + return 0; + + + +// filter: e8, e9, e8e9 +static int f_ct16_e8(filter_t *f) +{ + CT16(f, (*b == 0xe8), a + f->addvalue, get_le16, set_le16) +} + +static int f_ct16_e9(filter_t *f) +{ + CT16(f, (*b == 0xe9), a + f->addvalue, get_le16, set_le16) +} + +static int f_ct16_e8e9(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), a + f->addvalue, get_le16, set_le16) +} + + +// unfilter: e8, e9, e8e9 +static int u_ct16_e8(filter_t *f) +{ + CT16(f, (*b == 0xe8), 0 - a - f->addvalue, get_le16, set_le16) +} + +static int u_ct16_e9(filter_t *f) +{ + CT16(f, (*b == 0xe9), 0 - a - f->addvalue, get_le16, set_le16) +} + +static int u_ct16_e8e9(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le16, set_le16) +} + + +// scan: e8, e9, e8e9 +static int s_ct16_e8(filter_t *f) +{ + CT16(f, (*b == 0xe8), 0 - a - f->addvalue, get_le16, set_dummy) +} + +static int s_ct16_e9(filter_t *f) +{ + CT16(f, (*b == 0xe9), 0 - a - f->addvalue, get_le16, set_dummy) +} + +static int s_ct16_e8e9(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le16, set_dummy) +} + + +// filter: e8, e9, e8e9 with bswap le->be +static int f_ct16_e8_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe8), a + f->addvalue, get_le16, set_be16) +} + +static int f_ct16_e9_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe9), a + f->addvalue, get_le16, set_be16) +} + +static int f_ct16_e8e9_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), a + f->addvalue, get_le16, set_be16) +} + + +// unfilter: e8, e9, e8e9 with bswap le->be +static int u_ct16_e8_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe8), 0 - a - f->addvalue, get_be16, set_le16) +} + +static int u_ct16_e9_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe9), 0 - a - f->addvalue, get_be16, set_le16) +} + +static int u_ct16_e8e9_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_be16, set_le16) +} + + +// scan: e8, e9, e8e9 with bswap le->be +static int s_ct16_e8_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe8), 0 - a - f->addvalue, get_be16, set_dummy) +} + +static int s_ct16_e9_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe9), 0 - a - f->addvalue, get_be16, set_dummy) +} + +static int s_ct16_e8e9_bswap_le(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_be16, set_dummy) +} + + +// filter: e8, e9, e8e9 with bswap be->le +static int f_ct16_e8_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe8), a + f->addvalue, get_be16, set_le16) +} + +static int f_ct16_e9_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe9), a + f->addvalue, get_be16, set_le16) +} + +static int f_ct16_e8e9_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), a + f->addvalue, get_be16, set_le16) +} + + +// unfilter: e8, e9, e8e9 with bswap be->le +static int u_ct16_e8_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe8), 0 - a - f->addvalue, get_le16, set_be16) +} + +static int u_ct16_e9_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe9), 0 - a - f->addvalue, get_le16, set_be16) +} + +static int u_ct16_e8e9_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le16, set_be16) +} + + +// scan: e8, e9, e8e9 with bswap be->le +static int s_ct16_e8_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe8), 0 - a - f->addvalue, get_le16, set_dummy) +} + +static int s_ct16_e9_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe9), 0 - a - f->addvalue, get_le16, set_dummy) +} + +static int s_ct16_e8e9_bswap_be(filter_t *f) +{ + CT16(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le16, set_dummy) +} + + +#undef CT16 + + +/************************************************************************* +// 32-bit calltrick ("naive") +**************************************************************************/ + +#define CT32(f, cond, addvalue, get, set) \ + upx_byte *b = f->buf; \ + upx_byte *b_end = b + f->buf_len - 5; \ + do { \ + if (cond) \ + { \ + b += 1; \ + unsigned a = (unsigned) (b - f->buf); \ + f->lastcall = a; \ + set(b, get(b) + (addvalue)); \ + f->calls++; \ + b += 4 - 1; \ + } \ + } while (++b < b_end); \ + if (f->lastcall) f->lastcall += 4; \ + return 0; + + +// filter: e8, e9, e8e9 +static int f_ct32_e8(filter_t *f) +{ + CT32(f, (*b == 0xe8), a + f->addvalue, get_le32, set_le32) +} + +static int f_ct32_e9(filter_t *f) +{ + CT32(f, (*b == 0xe9), a + f->addvalue, get_le32, set_le32) +} + +static int f_ct32_e8e9(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), a + f->addvalue, get_le32, set_le32) +} + + +// unfilter: e8, e9, e8e9 +static int u_ct32_e8(filter_t *f) +{ + CT32(f, (*b == 0xe8), 0 - a - f->addvalue, get_le32, set_le32) +} + +static int u_ct32_e9(filter_t *f) +{ + CT32(f, (*b == 0xe9), 0 - a - f->addvalue, get_le32, set_le32) +} + +static int u_ct32_e8e9(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le32, set_le32) +} + + +// scan: e8, e9, e8e9 +static int s_ct32_e8(filter_t *f) +{ + CT32(f, (*b == 0xe8), 0 - a - f->addvalue, get_le32, set_dummy) +} + +static int s_ct32_e9(filter_t *f) +{ + CT32(f, (*b == 0xe9), 0 - a - f->addvalue, get_le32, set_dummy) +} + +static int s_ct32_e8e9(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le32, set_dummy) +} + + +// filter: e8, e9, e8e9 with bswap le->be +static int f_ct32_e8_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe8), a + f->addvalue, get_le32, set_be32) +} + +static int f_ct32_e9_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe9), a + f->addvalue, get_le32, set_be32) +} + +static int f_ct32_e8e9_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), a + f->addvalue, get_le32, set_be32) +} + + +// unfilter: e8, e9, e8e9 with bswap le->be +static int u_ct32_e8_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe8), 0 - a - f->addvalue, get_be32, set_le32) +} + +static int u_ct32_e9_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe9), 0 - a - f->addvalue, get_be32, set_le32) +} + +static int u_ct32_e8e9_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_be32, set_le32) +} + + +// scan: e8, e9, e8e9 with bswap le->be +static int s_ct32_e8_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe8), 0 - a - f->addvalue, get_be32, set_dummy) +} + +static int s_ct32_e9_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe9), 0 - a - f->addvalue, get_be32, set_dummy) +} + +static int s_ct32_e8e9_bswap_le(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_be32, set_dummy) +} + + +// filter: e8, e9, e8e9 with bswap be->le +static int f_ct32_e8_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe8), a + f->addvalue, get_be32, set_le32) +} + +static int f_ct32_e9_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe9), a + f->addvalue, get_be32, set_le32) +} + +static int f_ct32_e8e9_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), a + f->addvalue, get_be32, set_le32) +} + + +// unfilter: e8, e9, e8e9 with bswap be->le +static int u_ct32_e8_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe8), 0 - a - f->addvalue, get_le32, set_be32) +} + +static int u_ct32_e9_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe9), 0 - a - f->addvalue, get_le32, set_be32) +} + +static int u_ct32_e8e9_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le32, set_be32) +} + + +// scan: e8, e9, e8e9 with bswap be->le +static int s_ct32_e8_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe8), 0 - a - f->addvalue, get_le32, set_dummy) +} + +static int s_ct32_e9_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe9), 0 - a - f->addvalue, get_le32, set_dummy) +} + +static int s_ct32_e8e9_bswap_be(filter_t *f) +{ + CT32(f, (*b == 0xe8 || *b == 0xe9), 0 - a - f->addvalue, get_le32, set_dummy) +} + + +#undef CT32 + + +/************************************************************************* +// 32-bit calltrick with cto ("clever") +// +// This version is more sophisticated because it only +// tries to change actual calls and/or jumps. +**************************************************************************/ + +#if 1 +// use Laszlo's implementation +#include "fcto_ml.ch" +#else +// use Marco's implementation +#include "fcto_mfx.ch" +#endif + + +/************************************************************************* +// database for class Filter +**************************************************************************/ + +const FilterImp::f_t FilterImp::filters[] = { + // no filter + { 0x00, 0, 0, NULL, NULL, NULL }, + // 16-bit calltrick + { 0x01, 4, 0, f_ct16_e8, u_ct16_e8, s_ct16_e8,}, + { 0x02, 4, 0, f_ct16_e9, u_ct16_e9, s_ct16_e9 }, + { 0x03, 4, 0, f_ct16_e8e9, u_ct16_e8e9, s_ct16_e8e9 }, + { 0x04, 4, 0, f_ct16_e8_bswap_le, u_ct16_e8_bswap_le, s_ct16_e8_bswap_le }, + { 0x05, 4, 0, f_ct16_e9_bswap_le, u_ct16_e9_bswap_le, s_ct16_e9_bswap_le }, + { 0x06, 4, 0, f_ct16_e8e9_bswap_le, u_ct16_e8e9_bswap_le, s_ct16_e8e9_bswap_le }, + { 0x07, 4, 0, f_ct16_e8_bswap_be, u_ct16_e8_bswap_be, s_ct16_e8_bswap_be }, + { 0x08, 4, 0, f_ct16_e9_bswap_be, u_ct16_e9_bswap_be, s_ct16_e9_bswap_be }, + { 0x09, 4, 0, f_ct16_e8e9_bswap_be, u_ct16_e8e9_bswap_be, s_ct16_e8e9_bswap_be }, + // 32-bit calltrick + { 0x11, 6, 0, f_ct32_e8, u_ct32_e8, s_ct32_e8 }, + { 0x12, 6, 0, f_ct32_e9, u_ct32_e9, s_ct32_e9 }, + { 0x13, 6, 0, f_ct32_e8e9, u_ct32_e8e9, s_ct32_e8e9 }, + { 0x14, 6, 0, f_ct32_e8_bswap_le, u_ct32_e8_bswap_le, s_ct32_e8_bswap_le }, + { 0x15, 6, 0, f_ct32_e9_bswap_le, u_ct32_e9_bswap_le, s_ct32_e9_bswap_le }, + { 0x16, 6, 0, f_ct32_e8e9_bswap_le, u_ct32_e8e9_bswap_le, s_ct32_e8e9_bswap_le }, + { 0x17, 6, 0, f_ct32_e8_bswap_be, u_ct32_e8_bswap_be, s_ct32_e8_bswap_be }, + { 0x18, 6, 0, f_ct32_e9_bswap_be, u_ct32_e9_bswap_be, s_ct32_e9_bswap_be }, + { 0x19, 6, 0, f_ct32_e8e9_bswap_be, u_ct32_e8e9_bswap_be, s_ct32_e8e9_bswap_be }, + // 32-bit cto calltrick + { 0x21, 6, 0x00ffffff, f_cto32_e8, u_cto32_e8, s_cto32_e8 }, + { 0x22, 6, 0x00ffffff, f_cto32_e9, u_cto32_e9, s_cto32_e9 }, + { 0x23, 6, 0x00ffffff, f_cto32_e8e9, u_cto32_e8e9, s_cto32_e8e9 }, + { 0x24, 6, 0x00ffffff, f_cto32_e8_bswap_le, u_cto32_e8_bswap_le, s_cto32_e8_bswap_le }, + { 0x25, 6, 0x00ffffff, f_cto32_e9_bswap_le, u_cto32_e9_bswap_le, s_cto32_e9_bswap_le }, + { 0x26, 6, 0x00ffffff, f_cto32_e8e9_bswap_le, u_cto32_e8e9_bswap_le, s_cto32_e8e9_bswap_le }, + { 0x27, 6, 0x00ffffff, f_cto32_e8_bswap_be, u_cto32_e8_bswap_be, s_cto32_e8_bswap_be }, + { 0x28, 6, 0x00ffffff, f_cto32_e9_bswap_be, u_cto32_e9_bswap_be, s_cto32_e9_bswap_be }, + { 0x29, 6, 0x00ffffff, f_cto32_e8e9_bswap_be, u_cto32_e8e9_bswap_be, s_cto32_e8e9_bswap_be }, +}; + +const int FilterImp::n_filters = HIGH(filters); + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/help.cpp b/src/help.cpp new file mode 100644 index 0000000000..f18685a471 --- /dev/null +++ b/src/help.cpp @@ -0,0 +1,278 @@ +/* help.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + + +/************************************************************************* +// +**************************************************************************/ + +static bool head_done = 0; + +void show_head(void) +{ + FILE *f = con_term; + int fg; + + if (head_done) + return; + head_done = 1; + + fg = con_fg(f,FG_GREEN); + con_fprintf(f, + " Ultimate Packer for eXecutables\n" + " Copyright (C) 1996, 1997, 1998, 1999, 2000\n" + "UPX v%-12sMarkus F.X.J. Oberhumer & Laszlo Molnar%21s\n\n", + UPX_VERSION_STRING, UPX_VERSION_DATE); + fg = con_fg(f,fg); +} + + +/************************************************************************* +// +**************************************************************************/ + +void show_usage(void) +{ + FILE *f = con_term; + + con_fprintf(f,"Usage: %s [-123456788dlsthVL] [-qvfk] [-o file] %sfile..\n", progname, +#if defined(__DJGPP__) || defined(__EMX__) + "[@]"); +#else + ""); +#endif +} + + +/************************************************************************* +// +**************************************************************************/ + +void show_help(int x) +{ + FILE *f = con_term; + int fg; + + show_head(); + show_usage(); + + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"\nCommands:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " -1 compress faster -9 compress better\n" + "%s" + " -d decompress -l list compressed file\n" + " -t test compressed file -V display version number\n" + " -h give %s help -L display software license\n%s", + x == 0 ? "" : " --best compress best (can be very slow for big files)\n", + x == 0 ? "more" : "this", x == 0 ? "" : "\n"); + + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options:\n"); + fg = con_fg(f,fg); + + con_fprintf(f, + " -q be quiet -v be verbose\n" + //" -oFILE write output to `FILE' -c write output to stdout\n" + " -oFILE write output to `FILE'\n" + //" -f force overwrite of output files and compression of suspicious files\n" + " -f force compression of suspicious files\n" + "%s%s" + , (x == 0) ? " -k keep backup files\n" : "" +#if 1 + , (x > 0) ? " --no-color, --mono, --color, --no-progress change look\n" : "" +#else + , "" +#endif + ); + + if (x > 0) + { + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"\nBackup options:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " -k, --backup keep backup files\n" + " --no-backup no backup files [default]\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Overlay options:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --overlay=skip don't compress a file with an overlay\n" + " --overlay=copy copy any extra data attached to the file [default]\n" + " --overlay=strip strip any extra data attached to the file [dangerous]\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options for dos/exe:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --8086 make compressed exe work on any 8086\n" + " --no-reloc put no relocations in to the exe header\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options for dos/com:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --8086 make compressed com work on any 8086\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options for dos/sys:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --8086 make compressed sys work on any 8086\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options for djgpp2/coff:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --coff produce COFF output [default: EXE]\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options for watcom/le:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --le produce LE output [default: EXE]\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options for win32/pe & rtm32/pe:\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " --compress-exports=0 do not compress the export section\n" + " --compress-exports=1 compress the export section [default]\n" + " --compress-icons=0 do not compress any icons\n" + " --compress-icons=1 compress all but the first icon\n" + " --compress-icons=2 compress all but the first icon directory [default]\n" + " --compress-resources=0 do not compress any resources\n" + " --strip-relocs=0 do not strip relocations\n" + " --strip-relocs=1 strip relocations [default]\n" + "\n"); + fg = con_fg(f,FG_YELLOW); + con_fprintf(f,"Options for linux/i386\n"); + fg = con_fg(f,fg); + con_fprintf(f, + " -s use /usr/local/lib/upx[bd] as decompressor\n" + " -s=path/upxX use path/upxX as decompressor\n" + "\n"); + } + + con_fprintf(f, + " file.. executables to (de)compress\n" + "\n" + "This version supports: dos/exe, dos/com, dos/sys, djgpp2/coff, watcom/le,\n" + " win32/pe, rtm32/pe, tmt/adam, atari/tos\n" + " linux/elf386, linux/sh386, linux/386\n" + "%s", + "\nUPX comes with ABSOLUTELY NO WARRANTY; for details visit http://upx.tsx.org\n" + //"\nUPX comes with ABSOLUTELY NO WARRANTY; for details type `upx -L'.\n" + ""); + + +#if defined(DEBUG) || defined(TESTING) + fg = con_fg(f,FG_RED); + con_fprintf(f,"\nWARNING: this version is compiled with" +#if defined(DEBUG) + " -DDEBUG" +#endif +#if defined(TESTING) + " -DTESTING" +#endif + "\n"); + fg = con_fg(f,fg); +#endif +} + + +/************************************************************************* +// +**************************************************************************/ + +void show_license(void) +{ + FILE *f = con_term; + + show_head(); + +con_fprintf(f, + " This program may be used freely, and you are welcome to\n" + " redistribute it under certain conditions.\n" + "\n" + " This program is distributed in the hope that it will be useful,\n" + " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + " UPX License Agreement for more details.\n" + "\n" + " You should have received a copy of the UPX License Agreement\n" + " along with this program; see the file LICENSE.\n" + " If not, visit one of the following pages:\n" + "\n" + ); + int fg = con_fg(f,FG_CYAN); +con_fprintf(f, + " http://upx.tsx.org\n" + " http://wildsau.idv.uni-linz.ac.at/mfx/upx.html\n" + " http://www.nexus.hu/upx\n" + ); + (void)con_fg(f,FG_ORANGE); +con_fprintf(f, + "\n" + " Markus F.X.J. Oberhumer Laszlo Molnar\n" + " markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu\n" + ); + fg = con_fg(f,fg); +} + + +/************************************************************************* +// +**************************************************************************/ + +void show_version(int x) +{ + FILE *f = stdout; + UNUSED(x); + + fprintf(f,"upx %s\n",UPX_VERSION_STRING); +#if defined(WITH_UCL) + fprintf(f,"UCL data compression library %s\n", ucl_version_string()); +#elif defined(WITH_NRV) + fprintf(f,"NRV data compression library %s\n", nrv_version_string()); +#endif + fprintf(f,"Copyright (C) 1996,1997,1998,1999,2000 Markus Franz Xaver Johannes Oberhumer\n"); + fprintf(f,"Copyright (C) 1996,1997,1998,1999,2000 Laszlo Molnar\n"); + fprintf(f,"Copyright (C) 2000 John F. Reiser\n"); + fprintf(f,"UPX comes with ABSOLUTELY NO WARRANTY; for details type `%s -L'.\n", progname); +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/lefile.cpp b/src/lefile.cpp new file mode 100644 index 0000000000..e2e7e6bf14 --- /dev/null +++ b/src/lefile.cpp @@ -0,0 +1,349 @@ +/* lefile.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "mem.h" +#include "lefile.h" + + +LeFile::LeFile(InputFile *f) : fif(f) +{ + memset(&ih,0,sizeof ih); + memset(&oh,0,sizeof oh); + iobject_table = oobject_table = NULL; + ifpage_table = ofpage_table = NULL; + ipm_entries = opm_entries = NULL; + ires_names = ores_names = NULL; + ifixups = ofixups = NULL; + inonres_names = ononres_names = NULL; + ientries = oentries = NULL; + le_offset = exe_offset = 0; +} + + +LeFile::~LeFile() +{ + delete [] iobject_table; + delete [] oobject_table; + delete [] ifpage_table; + delete [] ofpage_table; + delete [] ipm_entries; + delete [] opm_entries; + delete [] ires_names; + delete [] ores_names; + delete [] ifixups; + delete [] ofixups; + delete [] inonres_names; + delete [] ononres_names; + delete [] ientries; + delete [] oentries; +} + + +#define objects ih.object_table_entries +#define pages ih.memory_pages +#define mps ih.memory_page_size + + +void LeFile::readObjectTable() +{ + iobject_table = new le_object_table_entry_t[soobject_table = objects]; + fif->seek(le_offset + ih.object_table_offset,SEEK_SET); + fif->readx(iobject_table,sizeof(*iobject_table)*objects); +} + + +void LeFile::writeObjectTable() +{ + if (fof && oobject_table) + fof->write(oobject_table,sizeof(*iobject_table)*soobject_table); +} + + +void LeFile::readPageMap() +{ + ipm_entries = new le_pagemap_entry_t[sopm_entries = pages]; + fif->seek(le_offset + ih.object_pagemap_offset,SEEK_SET); + fif->readx(ipm_entries,sizeof(*ipm_entries)*pages); + + for (unsigned ic = 0; ic < pages; ic++) + if ((ipm_entries[ic].type & 0xC0) != 0 && (ipm_entries[ic].type & 0xC0) != 0xC0) + throwCantPack("unexpected value in page map table"); +} + + +void LeFile::writePageMap() +{ + if (fof && opm_entries) + fof->write(opm_entries,sizeof(*ipm_entries)*sopm_entries); +} + + +void LeFile::readResidentNames() +{ + sores_names = ih.entry_table_offset - ih.resident_names_offset; + ires_names = new upx_byte[sores_names]; + fif->seek(le_offset+ih.resident_names_offset,SEEK_SET); + fif->readx(ires_names,sores_names); +} + + +void LeFile::writeResidentNames() +{ + if (fof && ores_names) + fof->write(ores_names,sores_names); +} + + +void LeFile::readEntryTable() +{ + soentries = ih.fixup_page_table_offset - ih.entry_table_offset; + fif->seek(le_offset + ih.entry_table_offset,SEEK_SET); + ientries = new upx_byte[soentries]; + fif->readx(ientries,soentries); +} + + +void LeFile::writeEntryTable() +{ + if (fof && oentries) + fof->write(oentries,soentries); +} + + +void LeFile::readFixupPageTable() +{ + ifpage_table = new unsigned[sofpage_table = 1+pages]; + fif->seek(le_offset + ih.fixup_page_table_offset,SEEK_SET); + fif->readx(ifpage_table,4*sofpage_table); +} + + +void LeFile::writeFixupPageTable() +{ + if (fof && ofpage_table) + fof->write(ofpage_table,4*sofpage_table); +} + + +void LeFile::readFixups() +{ + sofixups = get_le32(ifpage_table+pages)-get_le32(ifpage_table); + ifixups = new upx_byte[sofixups]; + fif->seek(le_offset + ih.fixup_record_table_offset,SEEK_SET); + fif->readx(ifixups,sofixups); +} + + +void LeFile::writeFixups() +{ + if (fof && ofixups) + fof->write(ofixups,sofixups); +} + + +void LeFile::readImage() +{ + soimage = pages*mps; + iimage.alloc(soimage); + memset(iimage,0,soimage); + + unsigned ic,jc; + for (ic = jc = 0; ic < pages; ic++) + { + if ((ipm_entries[ic].type & 0xC0) == 0) + { + fif->seek(ih.data_pages_offset + exe_offset + + (ipm_entries[ic].m*0x100 + ipm_entries[ic].l-1) * mps,SEEK_SET); + fif->readx(iimage+jc,ic != pages-1 ? mps : ih.bytes_on_last_page); + } + jc += mps; + } +} + + +void LeFile::writeImage() +{ + if (fof && oimage != NULL) + fof->write(oimage, soimage); +} + + +void LeFile::readNonResidentNames() +{ + if (ih.non_resident_name_table_length) + { + inonres_names = new upx_byte[sononres_names = ih.non_resident_name_table_length]; + fif->seek(exe_offset+ih.non_resident_name_table_offset,SEEK_SET); + fif->readx(inonres_names,sononres_names); + } +} + + +void LeFile::writeNonResidentNames() +{ + if (fof && ononres_names) + fof->write(ononres_names,sononres_names); +} + + +bool LeFile::readFileHeader() +{ +#define H(x) get_le16(header+2*(x)) + upx_byte header[0x40]; + le_offset = exe_offset = 0; + int ic; + + for (ic = 0; ic < 20; ic++) + { + fif->seek(le_offset,SEEK_SET); + fif->readx(header,sizeof(header)); + + if (memcmp(header,"MZ",2) == 0) // normal dos exe + { + exe_offset = le_offset; + if (H(0x18/2) >= 0x40 + && memcmp(header+0x19,"TIPPACH",7)) // new format exe + le_offset += H(0x3c/2)+H(0x3e/2)*65536; + else + { + le_offset += H(2)*512+H(1); + if (H(1)) + le_offset -= 512; + else if (H(2) == 0) + return false; + } + } + else if (memcmp(header,"BW",2) == 0) // used in dos4gw.exe + le_offset += H(2)*512+H(1); + else if (memcmp(header,"LE",2) == 0) + break; + else if (memcmp(header,"PMW1",4) == 0) + throwCantPack("already packed with PMWLITE"); + else + return false; + } + if (ic == 20) + return false; + fif->seek(le_offset,SEEK_SET); + fif->readx(&ih,sizeof(ih)); + return true; +#undef H +} + + +void LeFile::writeFile(OutputFile *f, bool le) +{ + fof = f; + memcpy (&oh,&ih,(char*)&oh.memory_pages-(char*)&oh); // copy some members of the orig. header + oh.memory_page_size = mps; + oh.object_table_offset = sizeof(oh); + oh.object_table_entries = soobject_table; + oh.object_pagemap_offset = oh.object_table_offset + soobject_table*sizeof(*iobject_table); + oh.resident_names_offset = oh.object_pagemap_offset + sopm_entries*sizeof(*ipm_entries); + oh.entry_table_offset = oh.resident_names_offset + sores_names; + oh.fixup_page_table_offset = oh.entry_table_offset + soentries; + oh.fixup_record_table_offset = oh.fixup_page_table_offset + sofpage_table*4; + oh.imported_modules_name_table_offset = oh.fixup_record_table_offset + sofixups - FIXUP_EXTRA; + oh.imported_procedures_name_table_offset = oh.imported_modules_name_table_offset; + oh.data_pages_offset = oh.fixup_record_table_offset + sofixups + (le ? 0 : le_offset-exe_offset); + if (ih.non_resident_name_table_length) + { + oh.non_resident_name_table_offset = oh.data_pages_offset + soimage; + oh.non_resident_name_table_length = sononres_names; + } + oh.fixup_size = sofixups + 4*sofpage_table; + oh.loader_size = oh.fixup_size + oh.fixup_page_table_offset - sizeof(oh); + + fof->write(&oh,sizeof(oh)); + writeObjectTable(); + writePageMap(); + writeResidentNames(); + writeEntryTable(); + writeFixupPageTable(); + writeFixups(); + writeImage(); + writeNonResidentNames(); +} + + +void LeFile::countFixups(unsigned *counts) const +{ + memset(counts,0,sizeof(unsigned)*(objects+2)); + // counts[0..objects-1] - # of 32-bit offset relocations in for that objects + // counts[objects] - # of selector fixups + // counts[objects+1] - # of self-relative fixups + + const upx_byte *fix = ifixups; + const unsigned sfixups = get_le32(ifpage_table+pages); + unsigned ll; + + while ((unsigned)(fix - ifixups) < sfixups) + { + if ((fix[1] & ~0x10) != 0) + throwCantPack("unsupported fixup record"); + switch (*fix) + { + case 2: // selector fixup + counts[objects] += 9; + fix += 5; + break; + case 0x12: // alias selector + throwCantPack("16-bit selector alias fixup not yet supported"); + case 5: // 16-bit offset + fix += (fix[1] & 0x10) ? 9 : 7; + break; + case 6: // 16:32 pointer + counts[objects] += 9; + case 7: // 32-bit offset + counts[fix[4]-1] += 4; + fix += (fix[1] & 0x10) ? 9 : 7; + break; + case 0x27: // 32-bit offset list + ll = fix[2]; + counts[fix[3]-1] += ll*4; + fix += (fix[1] & 0x10) ? 6 : 4; + fix += ll*2; + break; + case 8: // 32-bit self relative fixup + counts[objects+1] += 4; + fix += (fix[1] & 0x10) ? 9 : 7; + break; + default: + throwCantPack("unsupported fixup record"); + } + } + counts[objects]++; // extra space for 'ret' + counts[objects+1] += 4; // extra space for 0xFFFFFFFF +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/lefile.h b/src/lefile.h new file mode 100644 index 0000000000..935ad5bda0 --- /dev/null +++ b/src/lefile.h @@ -0,0 +1,223 @@ +/* lefile.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_LEFILE_H +#define __UPX_LEFILE_H + +class InputFile; +class OutputFile; + + +/************************************************************************* +// +**************************************************************************/ + +class LeFile +{ +public: + LeFile(InputFile *); + virtual ~LeFile(); + + virtual bool readFileHeader(); + virtual void writeFile(OutputFile *, bool); + +protected: + enum { FIXUP_EXTRA = 3 }; + + struct le_header_t + { // 0x00 + char _[2]; // signature: 'LE' || 'LX' + char byte_order; // 0 little endian + char word_order; // 0 little endian + LE32 exe_format_level; // 0 + LE16 cpu_type; // 1->286..4->586 + LE16 target_os; // 1->OS2 + char _0[4]; // module_version = 0 + // 0x10 + LE32 module_type; // 0x200->compatible with PM windowing + LE32 memory_pages; + LE32 init_cs_object; + LE32 init_eip_offset; + // 0x20 + LE32 init_ss_object; + LE32 init_esp_offset; + LE32 memory_page_size; + LE32 bytes_on_last_page; + // 0x30 + LE32 fixup_size; + char _1[4]; // fixup_checksum = 0 + LE32 loader_size; + char _2[4]; // loader_checksum = 0 + // 0x40 + LE32 object_table_offset; + LE32 object_table_entries; + LE32 object_pagemap_offset; + LE32 object_iterate_data_map_offset; + // 0x50 + char _3[4]; // resource_offset + LE32 resource_entries; + LE32 resident_names_offset; + LE32 entry_table_offset; + // 0x60 + char _4[4]; // module_directives_table_offset = 0 + LE32 module_directives_entries; + LE32 fixup_page_table_offset; + LE32 fixup_record_table_offset; + // 0x70 + LE32 imported_modules_name_table_offset; + LE32 imported_modules_count; + LE32 imported_procedures_name_table_offset; + char _5[4]; // per_page_checksum_table_offset = 0 + // 0x80 + LE32 data_pages_offset; + char _6[4]; // preload_page_count = 0 + LE32 non_resident_name_table_offset; + LE32 non_resident_name_table_length; + // 0x90 +#if 1 + char _7[52]; +#else + LE32 non_resident_names_checksum; + LE32 automatic_data_object; + LE32 debug_info_offset; + LE32 debug_info_length; + // 0xA0 + LE32 preload_instance_pages; + LE32 demand_instance_pages; + LE32 extra_heap_alloc; + char reserved[12]; + LE32 versioninfo; + LE32 unkown; + // 0xC0 + LE16 device_id; + LE16 ddk_version; +#endif + }; + + struct le_object_table_entry_t + { + LE32 virtual_size; + LE32 base_address; + LE32 flags; + LE32 pagemap_index; + LE32 npages; + LE32 reserved; + }; + + struct le_pagemap_entry_t + { + upx_byte h; + upx_byte m; + upx_byte l; + upx_byte type; // 0x00-legal;0x40-iterated;0x80-invalid;0xC0-zeroed + }; + + virtual void readObjectTable(); + virtual void writeObjectTable(); + //virtual void encodeObjectTable(){oobject_table = iobject_table; iobject_table = NULL;} + //virtual void decodeObjectTable(){encodeObjectTable();} + + virtual void readFixupPageTable(); + virtual void writeFixupPageTable(); + //virtual void encodeFixupPageTable(){ofpage_table = ifpage_table; ifpage_table = NULL;} + //virtual void decodeFixupPageTable(){encodeFixupPageTable();} + + virtual void readPageMap(); + virtual void writePageMap(); + virtual void encodePageMap(){opm_entries = ipm_entries; ipm_entries = NULL;} + virtual void decodePageMap(){encodePageMap();} + + virtual void readResidentNames(); + virtual void writeResidentNames(); + virtual void encodeResidentNames(){ores_names = ires_names; ires_names = NULL;} + virtual void decodeResidentNames(){encodeResidentNames();} + + virtual void readNonResidentNames(); + virtual void writeNonResidentNames(); + virtual void encodeNonResidentNames(){ononres_names = inonres_names; inonres_names = NULL;} + virtual void decodeNonResidentNames(){encodeNonResidentNames();} + + virtual void readEntryTable(); + virtual void writeEntryTable(); + //virtual void encodeEntryTable(){oentries = ientries; ientries = NULL;} + //virtual void decodeEntryTable(){encodeEntryTable();} + + virtual void readFixups(); + virtual void writeFixups(); + //virtual void encodeFixups(){ofixups = ifixups; ifixups = NULL;} + //virtual void decodeFixups(){encodeFixups();} + + virtual void readImage(); + virtual void writeImage(); + //virtual void encodeImage(){oimage = iimage; iimage = NULL;} + //virtual void decodeImage(){encodeImage();} + + void countFixups(unsigned *) const; + + InputFile *fif; + OutputFile *fof; + long le_offset; + long exe_offset; + + le_header_t ih; + le_header_t oh; + + le_object_table_entry_t *iobject_table; + le_object_table_entry_t *oobject_table; + unsigned *ifpage_table; + unsigned *ofpage_table; + le_pagemap_entry_t *ipm_entries; + le_pagemap_entry_t *opm_entries; + upx_byte *ires_names; + upx_byte *ores_names; + upx_byte *ifixups; + upx_byte *ofixups; + upx_byte *inonres_names; + upx_byte *ononres_names; + MemBuffer iimage; + MemBuffer oimage; + upx_byte *ientries; + upx_byte *oentries; + + unsigned soobject_table; + unsigned sofpage_table; + unsigned sopm_entries; + unsigned sores_names; + unsigned sofixups; + unsigned sononres_names; + unsigned soimage; + unsigned soentries; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/linker.cpp b/src/linker.cpp new file mode 100644 index 0000000000..3aa79793ee --- /dev/null +++ b/src/linker.cpp @@ -0,0 +1,204 @@ +/* linker.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "linker.h" + + +struct Linker::section +{ + int istart; + int ostart; + int len; + char name[8]; +}; + +struct Linker::jump +{ + int pos; + int len; + char tsect[8]; + int toffs; +}; + + +Linker::Linker(const void *pdata, int plen, int pinfo) +{ + iloader = new char[(ilen = plen) + 4096]; + memcpy(iloader,pdata,plen); + oloader = new char[plen]; + olen = 0; + align_hack = 0; + info = pinfo; + njumps = nsections = frozen = 0; + jumps = new jump [200]; + sections = new section[200]; + + char *p = iloader + info; + while (get_le32(p) != (unsigned)(-1)) + { + if (get_le32(p)) + { + memcpy(sections[nsections].name,p,8); + sections[nsections].istart = get_le32(p+8); + sections[nsections++].ostart = -1; + p += 12; + assert(nsections < 200); + } + else + { + int l; + for (l = get_le32(p+4) - 1; iloader[l] == 0; l--) + ; + + jumps[njumps].pos = l+1; + jumps[njumps].len = get_le32(p+4)-jumps[njumps].pos; + memcpy(jumps[njumps].tsect,p+8,8); + jumps[njumps++].toffs = get_le32(p+16); + p += 20; + assert(njumps < 200); + } + } + + int ic; + for (ic = 0; ic < nsections - 1; ic++) + sections[ic].len = sections[ic+1].istart - sections[ic].istart; + sections[ic].len = 0; +} + + +Linker::~Linker() +{ + delete [] iloader; + delete [] oloader; + delete [] jumps; + delete [] sections; +} + + +void Linker::addSection(const char *sect) +{ + int ic; + while (*sect) + { + if (*sect == '+') // alignment + { + if (sect[1] == '0') + align_hack = olen; + else + { + ic = (sect[1] & 0xf) + (sect[1] > '9' ? 9 : 0); + ic = (ic + (sect[2] & 0xf) + (sect[2] > '9' ? 9 : 0) + - (olen - align_hack) % ic) % ic; + memset(oloader+olen,sect[3] == 'C' ? 0x90 : 0,ic); + olen += ic; + } + } + else + { + for (ic = 0; ic < nsections; ic++) + if (memcmp(sect,sections[ic].name,8) == 0) + { + memcpy(oloader+olen,iloader+sections[ic].istart,sections[ic].len); + sections[ic].ostart = olen; + olen += sections[ic].len; + break; + } + //printf("%8.8s",section); + assert(ic!=nsections); + } + sect += 8; + } +} + + +void Linker::addSection(const char *sname, const void *sdata, unsigned len) +{ + // add a new section - can be used for adding stuff like ident or header + memcpy(sections[nsections].name,sname,8); + sections[nsections].istart = ilen; + sections[nsections].len = len; + sections[nsections++].ostart = olen; + assert(nsections < 200); + memcpy(iloader+ilen,sdata,len); + ilen += len; +} + + +const char *Linker::getLoader(int *llen) +{ + if (!frozen) + { + int ic,jc,kc; + for (ic = 0; ic < njumps; ic++) + { + for (jc = 0; jc < nsections-1; jc++) + if (jumps[ic].pos >= sections[jc].istart + && jumps[ic].pos < sections[jc+1].istart) + break; + assert(jc!=nsections-1); + if (sections[jc].ostart < 0) + continue; + + for (kc = 0; kc < nsections-1; kc++) + if (memcmp(jumps[ic].tsect,sections[kc].name,8) == 0) + break; + assert(kc!=nsections-1); + + int offs = sections[kc].ostart+jumps[ic].toffs - + (jumps[ic].pos+jumps[ic].len - + sections[jc].istart+sections[jc].ostart); + + set_le32(&offs,offs); + memcpy(oloader+sections[jc].ostart+jumps[ic].pos-sections[jc].istart,&offs,jumps[ic].len); + } + frozen=1; + } + if (llen) *llen = olen; + return oloader; +} + + +int Linker::getSection(const char *name, int *slen) const +{ + if (!frozen) + return -1; + for (int ic = 0; ic < nsections; ic++) + if (memcmp(name,sections[ic].name,8) == 0) + { + if (slen) + *slen = sections[ic].len; + return sections[ic].ostart; + } + return -1; +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/linker.h b/src/linker.h new file mode 100644 index 0000000000..a75edfdd8f --- /dev/null +++ b/src/linker.h @@ -0,0 +1,65 @@ +/* linker.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_LINKER_H +#define __UPX_LINKER_H + + +class Linker +{ +public: + Linker(const void *pdata, int plen, int pinfo); + ~Linker(); + void addSection(const char *sect); + void addSection(const char *sname, const void *sdata, unsigned len); + const char *getLoader(int *llen); + int getSection(const char *name, int *slen) const; + +private: + struct section; + struct jump; + + char *iloader, *oloader; + int ilen, olen; + int info; + jump *jumps; + int njumps; + section *sections; + int nsections; + int frozen; + int align_hack; +}; + + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000000..e96360cc1d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,1134 @@ +/* main.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "mygetopt.h" +#include "file.h" + + +/************************************************************************* +// options +**************************************************************************/ + +static void init_options(struct options_t *o) +{ + memset(o, 0, sizeof(*o)); + memset(&o->crp, 0xff, sizeof(o->crp)); + + o->cmd = CMD_NONE; + o->method = -1; + o->level = 7; + o->mem_level = -1; + o->filter = -1; + + o->backup = -1; + o->overlay = -1; + + o->console = CON_INIT; + o->verbose = 2; + + o->w32pe.compress_exports = true; + o->w32pe.compress_icons = 2; + o->w32pe.compress_resources = true; + o->w32pe.strip_relocs = -1; +} + +static struct options_t global_options; +struct options_t * volatile opt = &global_options; + +static int done_output_name = 0; +static int done_script_name = 0; + + +const char *argv0 = ""; +const char *progname = ""; + + +static int num_files = -1; +static int exit_code = EXIT_OK; + + +/************************************************************************* +// exit handlers +**************************************************************************/ + +#if defined(__GNUC__) +static void do_exit(void) __attribute__((noreturn)); +#endif +static void do_exit(void) +{ + static bool in_exit = false; + + if (in_exit) + exit(exit_code); + in_exit = true; + + fflush(con_term); + fflush(stderr); + exit(exit_code); +} + + +#define EXIT_FATAL 3 + +static bool set_eec(int ec, int *eec) +{ + if (ec == EXIT_FATAL) + { + *eec = EXIT_ERROR; + return 1; + } + else if (ec < 0 || ec == EXIT_ERROR) + { + *eec = EXIT_ERROR; + } + else if (ec == EXIT_WARN) + { + if (!opt->ignorewarn) + if (*eec == EXIT_OK) + *eec = ec; + } + else if (ec == EXIT_OK) + { + /* do nothing */ + } + else + { + assert(0); + } + return 0; +} + +bool set_ec(int ec) +{ + return set_eec(ec,&exit_code); +} + + +void e_exit(int ec) +{ + (void) set_ec(ec); + do_exit(); +} + + +void e_usage(void) +{ + show_usage(); + e_exit(EXIT_USAGE); +} + + +void e_memory(void) +{ + show_head(); + fflush(con_term); + fprintf(stderr,"%s: out of memory\n", argv0); + e_exit(EXIT_MEMORY); +} + + +static void e_method(int m, int l) +{ + fflush(con_term); + fprintf(stderr,"%s: illegal method option -- %d/%d\n", argv0, m, l); + e_usage(); +} + + +static void e_optarg(const char *n) +{ + fflush(con_term); + fprintf(stderr,"%s: invalid argument in option `%s'\n", argv0, n); + e_exit(EXIT_USAGE); +} + + +#if defined(OPTIONS_VAR) +void e_envopt(const char *n) +{ + fflush(con_term); + if (n) + fprintf(stderr,"%s: invalid string `%s' in environment variable `%s'\n", + argv0, n, OPTIONS_VAR); + else + fprintf(stderr,"%s: illegal option in environment variable `%s'\n", + argv0, OPTIONS_VAR); + e_exit(EXIT_USAGE); +} +#endif /* defined(OPTIONS_VAR) */ + + +RETSIGTYPE SIGTYPEENTRY e_sighandler(int signum) +{ + UNUSED(signum); + e_exit(EXIT_FATAL); +} + + +/************************************************************************* +// check options +**************************************************************************/ + +void check_not_both(bool e1, bool e2, const char *c1, const char *c2) +{ + if (e1 && e2) + { + fprintf(stderr,"%s: ",argv0); + fprintf(stderr,"cannot use both `%s' and `%s'\n", c1, c2); + e_usage(); + } +} + + +void check_options(int i, int argc) +{ + assert(i <= argc); + + // set default overlay action + if (!(opt->cmd == CMD_COMPRESS || opt->cmd == CMD_DECOMPRESS)) + opt->overlay = opt->COPY_OVERLAY; + else if (opt->overlay < 0) + opt->overlay = opt->COPY_OVERLAY; + + // set default backup option + if (opt->backup < 0) + opt->backup = 0; + if (!(opt->cmd == CMD_COMPRESS || opt->cmd == CMD_DECOMPRESS)) + opt->backup = 1; + + check_not_both(opt->to_stdout, opt->output_name != NULL, "--stdout", "-o"); + if (opt->to_stdout || opt->output_name) + { + if (i + 1 != argc) + { + fprintf(stderr,"%s: need exactly one argument when using `%s'\n", + argv0, opt->to_stdout ? "--stdout" : "-o"); + e_usage(); + } + } +} + + +/************************************************************************* +// misc +**************************************************************************/ + +void e_help(void) +{ + show_help(); + e_exit(EXIT_USAGE); +} + + +static void set_term(FILE *f) +{ + if (f) + con_term = f; + else + con_term = isafile(STDIN_FILENO) ? stderr : stdout; +} + + +static void set_cmd(int cmd) +{ + if (cmd > opt->cmd) + opt->cmd = cmd; +} + + +static bool set_method(int m, int l) +{ + if (m > 0) + opt->method = m; + if (l > 0) + opt->level = l; + set_cmd(CMD_COMPRESS); + return true; +} + + +static void set_output_name(const char *n, bool allow_m) +{ +#if 1 + if (done_output_name > 0) + { + fprintf(stderr,"%s: option `-o' more than once given\n",argv0); + e_usage(); + } +#endif + if (!n || !n[0] || (!allow_m && n[0] == '-')) + { + fprintf(stderr,"%s: missing output name\n",argv0); + e_usage(); + } + if (strlen(n) >= PATH_MAX - 4) + { + fprintf(stderr,"%s: output name too long\n",argv0); + e_usage(); + } + opt->output_name = n; + done_output_name++; +} + +static void set_script_name(const char *n, bool allow_m) +{ +#if 1 + if (done_script_name > 0) + { + fprintf(stderr,"%s: option `-script' more than once given\n",argv0); + e_usage(); + } +#endif + if (!n || !n[0] || (!allow_m && n[0] == '-')) + { + fprintf(stderr,"%s: missing script name\n",argv0); + e_usage(); + } + if (strlen(n) >= SCRIPT_MAX - 3) + { + fprintf(stderr,"%s: script name too long\n",argv0); + e_usage(); + } + opt->script_name = n; + done_script_name++; +} + + +/************************************************************************* +// get options +**************************************************************************/ + +static +char* prepare_shortopts(char *buf, const char *n, + const struct mfx_option *longopts) +{ + char *o = buf; + + for ( ; n && *n; n++) + if (*n != ' ') + *o++ = *n; + *o = 0; + for ( ; longopts && longopts->name; longopts++) + { + int v = longopts->val; +#if !defined(NDEBUG) + assert(longopts->name[0] != '\0'); + assert(longopts->name[0] != '-'); + if (longopts->has_arg & 0x20) + assert((longopts->has_arg & 0xf) == 1); +#endif +#if 0 + static char vopts[1024]; + if (v > 0 && v < 1024) + { + if (vopts[v] && strchr(buf,v) == NULL) + printf("warning: duplicate option %d ('%c')!\n", v, v & 127); + vopts[v] = 1; + } +#endif + if (v > 0 && v < 256 && strchr(buf,v) == NULL) + { + *o++ = (char) v; + if ((longopts->has_arg & 0xf) >= 1) + *o++ = ':'; + if ((longopts->has_arg & 0xf) >= 2) + *o++ = ':'; + *o = 0; + } + if (longopts->has_arg & 0x20) + assert((longopts->has_arg & 0xf) == 1); + } + return buf; +} + + +template +int getoptvar(T *var, T minval, T maxval) +{ + const char *p = mfx_optarg; + char *endptr; + + if (!p || !p[0]) + return -1; + // avoid interpretation as octal value + while (p[0] == '0' && isdigit(p[1])) + p++; + long n = strtol(p, &endptr, 0); + if (*endptr != '\0') + return -2; + T v = (T) n; + if (v < minval) + return -3; + if (v > maxval) + return -4; + *var = v; + return 0; +} + + +static int do_option(int optc, const char *arg) +{ + int i = 0; + + switch (optc) + { + //case 'c': + case 517: + opt->to_stdout = true; + break; + case 'd': + set_cmd(CMD_DECOMPRESS); + break; + case 'D': + opt->debug++; + break; + case 'f': + opt->force++; + break; + case 901: + set_cmd(CMD_FILEINFO); + break; + case 'h': + case 'H': + case '?': + set_cmd(CMD_HELP); + break; + case 'h'+256: + /* according to GNU standards */ + set_term(stdout); + opt->console = CON_FILE; + show_help(1); + e_exit(EXIT_OK); + break; + case 'i': + opt->info_mode++; + break; + case 'l': + set_cmd(CMD_LIST); + break; + case 'L': + set_cmd(CMD_LICENSE); + break; + case 'o': + set_output_name(mfx_optarg,1); + break; + case 'q': + opt->verbose = (opt->verbose > 1 ? 1 : opt->verbose - 1); + break; + case 's': + set_script_name("/usr/local/lib/upxX", 1); + break; + case 't': + set_cmd(CMD_TEST); + break; + case 'v': + opt->verbose = (opt->verbose < 3 ? 3 : opt->verbose + 1); + break; + case 'V': + set_cmd(CMD_VERSION); + break; + case 'V'+256: + /* according to GNU standards */ + set_term(stdout); + opt->console = CON_FILE; + show_version(0); + e_exit(EXIT_OK); + break; + + // method + case 702: + if (!set_method(M_NRV2B_LE32, -1)) + e_method(M_NRV2B_LE32, opt->level); + break; + case 704: + if (!set_method(M_NRV2D_LE32, -1)) + e_method(M_NRV2D_LE32, opt->level); + break; + + // compression level + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!set_method(-1, optc - '0')) + e_method(opt->method, optc); + break; + case 900: + if (!set_method(-1, 10)) + e_method(opt->method, 10); + break; + + // compression memory level + case 902: + getoptvar(&opt->mem_level, 1, 9); + break; + + // misc + case 512: + opt->console = CON_FILE; + break; + case 513: + opt->console = CON_ANSI_MONO; + break; + case 514: + opt->console = CON_ANSI_COLOR; + break; + case 516: + opt->no_progress = true; + break; + case 519: + opt->no_env = true; + break; + case 520: + set_script_name(mfx_optarg,1); + break; + // compression settings + case 525: + opt->small = 1; + break; + case 526: + getoptvar(&opt->filter, 0, 255); + opt->all_filters = false; + break; + case 527: + opt->all_filters = true; + opt->filter = -1; + break; + // compression parms + case 531: + getoptvar(&opt->crp.c_flags, 0, 3); + break; + case 532: + getoptvar(&opt->crp.s_level, 0, 2); + break; + case 533: + getoptvar(&opt->crp.h_level, 0, 1); + break; + case 534: + getoptvar(&opt->crp.p_level, 0, 7); + break; + case 535: + getoptvar(&opt->crp.max_offset, 256u, ~0u); + break; + case 536: + getoptvar(&opt->crp.max_match, 16u, ~0u); + break; + case 537: + getoptvar(&opt->crp.m_size, 1024u, 512*1024u); + break; + // backup + case 'k': + opt->backup = 1; + break; + case 541: + if (opt->backup != 1) // do not overide `--backup' + opt->backup = 0; + break; + // overlay + case 551: + if (mfx_optarg && strcmp(mfx_optarg,"skip") == 0) + opt->overlay = opt->SKIP_OVERLAY; + else if (mfx_optarg && strcmp(mfx_optarg,"copy") == 0) + opt->overlay = opt->COPY_OVERLAY; + else if (mfx_optarg && strcmp(mfx_optarg,"strip") == 0) + opt->overlay = opt->STRIP_OVERLAY; + else + e_optarg(arg); + break; + case 552: + opt->overlay = opt->SKIP_OVERLAY; + break; + case 553: + opt->overlay = opt->COPY_OVERLAY; + break; + case 554: + opt->overlay = opt->STRIP_OVERLAY; + break; + // CPU + case 560: + if (mfx_optarg && strcmp(mfx_optarg,"8086") == 0) + opt->cpu = opt->CPU_8086; + else if (mfx_optarg && strcmp(mfx_optarg,"386") == 0) + opt->cpu = opt->CPU_386; + else if (mfx_optarg && strcmp(mfx_optarg,"486") == 0) + opt->cpu = opt->CPU_486; + else + e_optarg(arg); + break; + case 561: + opt->cpu = opt->CPU_8086; + break; + case 563: + opt->cpu = opt->CPU_386; + break; + case 564: + opt->cpu = opt->CPU_486; + break; + // + case 600: + opt->dos.force_stub = true; + break; + case 601: + opt->dos.no_reloc = true; + break; + case 610: + opt->djgpp2.coff = true; + break; + case 620: + opt->wcle.le = true; + break; + case 630: + opt->w32pe.compress_exports = true; + if (mfx_optarg && strcmp(mfx_optarg,"0") == 0) + opt->w32pe.compress_exports = false; + //printf("compress_exports: %d\n", opt->w32pe.compress_exports); + break; + case 631: + opt->w32pe.compress_icons = 1; + getoptvar(&opt->w32pe.compress_icons, 0, 2); + //printf("compress_icons: %d\n", opt->w32pe.compress_icons); + break; + case 632: + opt->w32pe.compress_resources = true; + if (mfx_optarg && strcmp(mfx_optarg,"0") == 0) + opt->w32pe.compress_resources = false; + //printf("compress_resources: %d\n", opt->w32pe.compress_resources); + break; + case 633: + opt->w32pe.strip_relocs = 1; + if (mfx_optarg && strcmp(mfx_optarg,"0") == 0) + opt->w32pe.strip_relocs = 0; + //printf("strip_relocs: %d\n", opt->w32pe.strip_relocs); + break; + case 650: + opt->tos.split_segments = true; + break; + case 660: + getoptvar(&opt->unix.blocksize, 8192u, ~0u); + break; + + case '\0': + return -1; + case ':': + return -2; + default: + fprintf(stderr,"%s: internal error in getopt (%d)\n",argv0,optc); + return -3; + } + + UNUSED(i); + return 0; +} + + +static int get_options(int argc, char **argv) +{ + +static const struct mfx_option longopts[] = +{ + // commands + {"best", 0, 0, 900}, // compress best + {"decompress", 0, 0, 'd'}, // decompress + {"fast", 0, 0, '1'}, // compress faster + {"fileinfo", 0x10, 0, 901}, // display info about file + {"help", 0, 0, 'h'+256}, // give help + {"license", 0, 0, 'L'}, // display software license + {"list", 0, 0, 'l'}, // list compressed exe + {"test", 0, 0, 't'}, // test compressed file integrity + {"uncompress", 0, 0, 'd'}, // decompress + {"version", 0, 0, 'V'+256}, // display version number + + // options + {"debug", 0x10, 0, 'D'}, + {"force", 0, 0, 'f'}, // force overwrite of output files + {"force-compress", 0, 0, 'f'}, // and compression of suspicious files + {"info", 0, 0, 'i'}, // info mode + {"no-env", 0x10, 0, 519}, // no environment var + {"no-progress", 0, 0, 516}, // no progress bar + {"output", 0x21, 0, 'o'}, + {"quiet", 0, 0, 'q'}, // quiet mode + {"script", 0x31, 0, 520}, // --script= + {"silent", 0, 0, 'q'}, // quiet mode + {"stdout", 0x10, 0, 517}, // write output on standard output + {"to-stdout", 0x10, 0, 517}, // write output on standard output + {"verbose", 0, 0, 'v'}, // verbose mode + + // backup options + {"backup", 0, 0, 'k'}, + {"keep", 0, 0, 'k'}, + {"no-backup", 0, 0, 541}, + + // overlay options + {"overlay", 0x31, 0, 551}, // --overlay= + {"skip-overlay", 0, 0, 552}, + {"no-overlay", 0, 0, 552}, // old name + {"copy-overlay", 0, 0, 553}, + {"strip-overlay", 0, 0, 554}, + + // CPU options + {"cpu", 0x31, 0, 560}, // --cpu= + {"8086", 0x10, 0, 561}, + {"386", 0x10, 0, 563}, + {"486", 0x10, 0, 564}, + + // color options + {"no-color", 0, 0, 512}, + {"mono", 0, 0, 513}, + {"color", 0, 0, 514}, + //{"intro", 0, 0, 515}, + + // compression method + {"2b", 0x10, 0, 702}, // --2b + {"n2b", 0x10, 0, 702}, // --n2b + {"nrv2b", 0x10, 0, 702}, // --nrv2b + {"2d", 0x10, 0, 704}, // --2d + {"n2d", 0x10, 0, 704}, // --n2d + {"nrv2d", 0x10, 0, 704}, // --nrv2d + // compression settings + {"all-filters", 0x10, 0, 527}, + {"filter", 0x31, 0, 526}, // --filter= + {"mem", 0x31, 0, 902}, // --mem= + {"small", 0x10, 0, 525}, + // compression runtime parameters + {"crp-cf", 0x31, 0, 531}, + {"crp-sl", 0x31, 0, 532}, + {"crp-hl", 0x31, 0, 533}, + {"crp-pl", 0x31, 0, 534}, + {"crp-mo", 0x31, 0, 535}, + {"crp-mm", 0x31, 0, 536}, + {"crp-ms", 0x31, 0, 537}, + + // atari/tos + {"split-segments", 0x10, 0, 650}, + // djgpp2/coff + {"coff", 0, 0, 610}, // produce COFF output + // dos/com + // dos/exe + //{"force-stub", 0x10, 0, 600}, + {"no-reloc", 0x10, 0, 601}, // no reloc. record into packer dos/exe + // dos/sys + // unix + {"blocksize", 0x31, 0, 660}, // --blocksize= + // watcom/le + {"le", 0, 0, 620}, // produce LE output + // win32/pe + {"compress-exports", 2, 0, 630}, + {"compress-icons", 2, 0, 631}, + {"compress-resources", 2, 0, 632}, + {"strip-relocs", 2, 0, 633}, + + { 0, 0, 0, 0 } +}; + + int optc, longind; + char buf[256]; + + prepare_shortopts(buf,"123456789hH?sV",longopts), + mfx_optind = 0; + mfx_opterr = 1; + while ((optc = mfx_getopt_long(argc, argv, buf, longopts, &longind)) >= 0) + { + if (do_option(optc, argv[mfx_optind-1]) != 0) + e_usage(); + } + + return mfx_optind; +} + + +#if defined(OPTIONS_VAR) +static void get_envoptions(int argc, char **argv) +{ + +/* only some options are allowed in the environment variable */ + +static const struct mfx_option longopts[] = +{ + // commands + {"best", 0, 0, 900}, // compress best + {"fast", 0, 0, '1'}, // compress faster + + // options + {"info", 0, 0, 'i'}, // info mode + {"no-progress", 0, 0, 516}, // no progress bar + {"quiet", 0, 0, 'q'}, // quiet mode + {"silent", 0, 0, 'q'}, // quiet mode + {"verbose", 0, 0, 'v'}, // verbose mode + + // backup options + {"backup", 0, 0, 'k'}, + {"keep", 0, 0, 'k'}, + {"no-backup", 0x10, 0, 541}, + + // overlay options + {"overlay", 0x31, 0, 551}, // --overlay= + {"skip-overlay", 0, 0, 552}, + {"no-overlay", 0, 0, 552}, // old name + {"copy-overlay", 0, 0, 553}, + {"strip-overlay", 0, 0, 554}, + + // CPU options + {"cpu", 0x31, 0, 560}, // --cpu= + {"8086", 0x10, 0, 561}, + {"386", 0x10, 0, 563}, + {"486", 0x10, 0, 564}, + + // color options + {"no-color", 0, 0, 512}, + {"mono", 0, 0, 513}, + {"color", 0, 0, 514}, + + // win32/pe + {"compress-exports", 2, 0, 630}, + {"compress-icons", 2, 0, 631}, + {"compress-resources", 2, 0, 632}, + {"strip-relocs", 2, 0, 633}, + + { 0, 0, 0, 0 } +}; + + char *env, *p; + const char *var; + int i, optc, longind; + int targc; + char **targv = NULL; + static const char sep[] = " \t"; + char buf[256]; + + var = getenv(OPTIONS_VAR); + if (var == NULL || !var[0]) + return; + env = strdup(var); + if (env == NULL) + return; + + /* count arguments */ + for (p = env, targc = 1; ; ) + { + while (*p && strchr(sep,*p)) + p++; + if (*p == '\0') + break; + targc++; + while (*p && !strchr(sep,*p)) + p++; + if (*p == '\0') + break; + p++; + } + + /* alloc temp argv */ + if (targc > 1) + targv = (char **) calloc(targc+1,sizeof(char *)); + if (targv == NULL) + { + free(env); + return; + } + + /* fill temp argv */ + targv[0] = argv[0]; + for (p = env, targc = 1; ; ) + { + while (*p && strchr(sep,*p)) + p++; + if (*p == '\0') + break; + targv[targc++] = p; + while (*p && !strchr(sep,*p)) + p++; + if (*p == '\0') + break; + *p++ = '\0'; + } + targv[targc] = NULL; + + /* check that only options are in temp argv */ + for (i = 1; i < targc; i++) + if (targv[i][0] != '-' || !targv[i][1] || strcmp(targv[i],"--") == 0) + e_envopt(targv[i]); + + /* handle options */ + prepare_shortopts(buf,"123456789",longopts); + mfx_optind = 0; + mfx_opterr = 1; + while ((optc = mfx_getopt_long(targc, targv, buf, longopts, &longind)) >= 0) + { + if (do_option(optc, targv[mfx_optind-1]) != 0) + e_envopt(NULL); + } + + if (mfx_optind < targc) + e_envopt(targv[mfx_optind]); + + /* clean up */ + free(targv); + free(env); + UNUSED(argc); +} +#endif /* defined(OPTIONS_VAR) */ + + +static void first_options(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) + if (strcmp(argv[i],"--version") == 0) + do_option('V'+256, argv[i]); + for (i = 1; i < argc; i++) + if (strcmp(argv[i],"--help") == 0) + do_option('h'+256, argv[i]); + for (i = 1; i < argc; i++) + if (strcmp(argv[i],"--no-env") == 0) + do_option(519, argv[i]); +} + + +/************************************************************************* +// assert a sane architecture +**************************************************************************/ + +void upx_sanity_check(void) +{ + assert(sizeof(char) == 1); + assert(sizeof(short) == 2); + assert(sizeof(int) == 4); + assert(sizeof(long) >= 4); + assert(sizeof(void *) >= 4); + assert(sizeof(long) >= sizeof(void *)); + assert(((off_t) -1) < 0); + + assert(sizeof(BE16) == 2); + assert(sizeof(BE32) == 4); + assert(sizeof(LE16) == 2); + assert(sizeof(LE32) == 4); + + struct align_assertion_1a_t + { + struct foo_t { + char c1; + LE16 v[4]; + } d[3]; + }; + struct align_assertion_1b_t + { + struct foo_t { + char c1; + char v[4*2]; + } d[3]; + }; + struct align_assertion_2a_t + { + struct foo_t { + char c1; + LE32 v[4]; + } d[3]; + }; + struct align_assertion_2b_t + { + struct foo_t { + char c1; + char v[4*4]; + } d[3]; + }; + //printf("%d\n", (int) sizeof(align_assertion_1a_t)); + //printf("%d\n", (int) sizeof(align_assertion_1b_t)); + //printf("%d\n", (int) sizeof(align_assertion_2a_t)); + //printf("%d\n", (int) sizeof(align_assertion_2b_t)); + assert(sizeof(align_assertion_1a_t) == sizeof(align_assertion_1b_t)); + assert(sizeof(align_assertion_2a_t) == sizeof(align_assertion_2b_t)); + assert(sizeof(align_assertion_1a_t) == 3*9); + assert(sizeof(align_assertion_2a_t) == 3*17); +} + + +/************************************************************************* +// main entry point +**************************************************************************/ + +int main(int argc, char *argv[]) +{ + int i; + static char default_argv0[] = "upx"; + int cmdline_cmd = CMD_NONE; + + upx_sanity_check(); + init_options(opt); + +#if defined(WITH_MSS) + MSS_DISABLE_LOG_OUTPUT; +#endif + +#if defined(__EMX__) + _response(&argc,&argv); + _wildcard(&argc,&argv); +#endif + + if (!argv[0] || !argv[0][0]) + argv[0] = default_argv0; + argv0 = argv[0]; +#if defined(DOSISH) + { + char *prog = fn_basename(argv0); + char *p; + bool allupper = 1; + for (p = prog; *p; p++) + if (islower((unsigned char)*p)) + allupper = 0; + if (allupper) + fn_strlwr(prog); + if (strlen(prog) > 4) + { + p = prog + strlen(prog) - 4; + if (fn_strcmp(p, ".exe") == 0 || fn_strcmp(p, ".ttp") == 0) + *p = 0; + } + progname = prog; + } +#else + progname = fn_basename(argv0); +#endif + + set_term(stderr); + +#if defined(WITH_UCL) + if (ucl_init() != UCL_E_OK) + { + show_head(); + fprintf(stderr,"ucl_init() failed - check your UCL installation !\n"); + if (UCL_VERSION != ucl_version()) + fprintf(stderr,"library version conflict (%lx, %lx) - check your UCL installation !\n", + UCL_VERSION, (long) ucl_version()); + e_exit(EXIT_INIT); + } +#elif defined(WITH_NRV) + if (nrv_init() != NRV_E_OK) + { + show_head(); + fprintf(stderr,"nrv_init() failed - check your NRV installation !\n"); + if (NRV_VERSION != nrv_version()) + fprintf(stderr,"library version conflict (%lx, %lx) - check your NRV installation !\n", + NRV_VERSION, (long) nrv_version()); + e_exit(EXIT_INIT); + } +#else +#error +#endif + + //srand((int) time(NULL)); + srand((int) clock()); + + /* get options */ + first_options(argc,argv); +#if defined(OPTIONS_VAR) + if (!opt->no_env) + get_envoptions(argc,argv); +#endif + i = get_options(argc,argv); + assert(i <= argc); + + set_term(0); + cmdline_cmd = opt->cmd; + switch (opt->cmd) + { + case CMD_NONE: + /* default - compress */ + set_cmd(CMD_COMPRESS); + break; + case CMD_COMPRESS: + break; + case CMD_DECOMPRESS: + break; + case CMD_TEST: + break; + case CMD_LIST: + break; + case CMD_FILEINFO: + break; + case CMD_LICENSE: + show_license(); + e_exit(EXIT_OK); + break; + case CMD_HELP: + show_help(1); + e_exit(EXIT_OK); + break; + case CMD_VERSION: + show_version(1); + e_exit(EXIT_OK); + break; + default: + /* ??? */ + break; + } + + if (opt->cmd != CMD_COMPRESS) + { + // invalidate compression options + opt->method = 0; + opt->level = 0; + opt->mem_level = 0; + memset(&opt->crp, 0xff, sizeof(opt->crp)); + } + + /* check options */ + if (argc == 1) + e_help(); + set_term(stderr); + check_options(i,argc); + num_files = argc - i; + if (num_files < 1) + { + if (opt->verbose >= 2) + e_help(); + else + e_usage(); + } + + /* start work */ + set_term(stdout); + do_files(i,argc,argv); + +#if 0 && defined(__GLIBC__) + //malloc_stats(); +#endif + do_exit(); + return exit_code; +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/mem.cpp b/src/mem.cpp new file mode 100644 index 0000000000..9b6e519532 --- /dev/null +++ b/src/mem.cpp @@ -0,0 +1,115 @@ +/* mem.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "mem.h" + + +/************************************************************************* +// +**************************************************************************/ + +MemBuffer::MemBuffer(unsigned size=0) : + ptr(NULL), alloc_ptr(NULL), alloc_size(0) +{ + if (size > 0) + alloc(size, 0); +} + + +MemBuffer::~MemBuffer() +{ + free(); +} + + +void MemBuffer::free() +{ + if (alloc_ptr) + ::free(alloc_ptr); + alloc_ptr = ptr = NULL; + alloc_size = 0; +} + + +unsigned MemBuffer::getSize() const +{ + if (!alloc_ptr) + return 0; + unsigned size = alloc_size - (ptr - alloc_ptr); + assert((int)size > 0); + return size; +} + + +void MemBuffer::alloc(unsigned size, unsigned base_offset) +{ + assert(alloc_ptr == NULL); + //free(); + assert((int)size > 0); + size = base_offset + size; + alloc_ptr = (unsigned char *) malloc(size); + if (!alloc_ptr) + { + throwCantPack("out of memory"); + //exit(1); + } + alloc_size = size; + ptr = alloc_ptr + base_offset; +} + + +void MemBuffer::alloc(unsigned size) +{ + alloc(size, 0); +} + + +void MemBuffer::allocForCompression(unsigned uncompressed_size) +{ + // Idea: + // We allocate the buffer at an offset of 4096 so + // that we could do an in-place decompression for + // verifying our overlap overhead at the end + // of packing. + // + // See Packer::verifyOverlappingDecompression(). + + alloc(uncompressed_size + uncompressed_size/8 + 256, MAX_OVERLAP_OVERHEAD); +} + + +void MemBuffer::allocForUncompression(unsigned uncompressed_size) +{ + alloc(uncompressed_size + 512, 0); // 512 safety bytes +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/mem.h b/src/mem.h new file mode 100644 index 0000000000..ab2424cde4 --- /dev/null +++ b/src/mem.h @@ -0,0 +1,82 @@ +/* mem.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_MEM_H +#define __UPX_MEM_H + + +/************************************************************************* +// +**************************************************************************/ + +class MemBuffer +{ +public: + MemBuffer(unsigned size=0); + ~MemBuffer(); + + void alloc(unsigned size); + void allocForCompression(unsigned uncompressed_size); + void allocForUncompression(unsigned uncompressed_size); + + void free(); + + const unsigned char *getBuf() const { return ptr; } + unsigned getSize() const ; + + operator unsigned char * () { return ptr; } + //operator const unsigned char * () const { return ptr; } + + enum { MAX_OVERLAP_OVERHEAD = 4096 }; + +private: + void alloc(unsigned size, unsigned base_offset); + + unsigned char *ptr; + unsigned char *alloc_ptr; + unsigned alloc_size; + +private: + // disable copy and assignment + MemBuffer(MemBuffer const &); // {} + MemBuffer& operator= (MemBuffer const &); // { return *this; } + + // disable dynamic allocation + static void *operator new (size_t); // {} + static void *operator new[] (size_t); // {} + //static void operator delete (void *) {} + //static void operator delete[] (void *) {} +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/merge.sh b/src/merge.sh new file mode 100644 index 0000000000..567d5c9239 --- /dev/null +++ b/src/merge.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -x + +for i in *.h *.cpp +do + diff3 -m ./$i ../../upx-ancestor/src/$i ../../upx-1.10/src/$i > tmp.$$ + mv tmp.$$ ./$i + read junk +done diff --git a/src/msg.cpp b/src/msg.cpp new file mode 100644 index 0000000000..cc74413c3c --- /dev/null +++ b/src/msg.cpp @@ -0,0 +1,232 @@ +/* msg.cpp -- info and error messages + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + + +/************************************************************************* +// FIXME: if stdout is redirected to a file and stderr is not, should +// we write all error messages to both stderr and stdout ? +**************************************************************************/ + +static int pr_need_nl = 0; + + +void printSetNl(int need_nl) +{ + pr_need_nl = need_nl; +} + +void printClearLine(FILE *f) +{ + static char clear_line_msg[1+79+1+1]; + if (!clear_line_msg[0]) + { + char *msg = clear_line_msg; + msg[0] = '\r'; + memset(msg+1,' ',79); + msg[80] = '\r'; + msg[81] = 0; + } + + fflush(stdout); fflush(stderr); + if (f == NULL) + f = stdout; + con_fprintf(f,clear_line_msg); + fflush(f); + printSetNl(0); +} + + +static void pr_print(bool c, const char *msg) +{ + if (c && !opt->to_stdout) + con_fprintf(stderr,msg); + else + fprintf(stderr,msg); +} + +static void pr_error(const char *iname, const char *msg, bool is_warning) +{ + set_ec(EXIT_ERROR); + fflush(stdout); fflush(stderr); + char buf[1024]; + buf[0] = 0; + if (pr_need_nl == 2) + printClearLine(stdout); + else if (pr_need_nl) + { + buf[0] = '\n'; + buf[1] = 0; + printSetNl(0); + } + + // This hack is needed, otherwise error messages may get lost + // when the cursor is not yet at the bottom of the screen. + // At least I can use some colors then... + bool c = isatty(STDERR_FILENO) ? 1 : 0; + + int fg = con_fg(stderr,FG_BRTRED); + upx_snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),"%s: ", progname); + pr_print(c,buf); + //(void)con_fg(stderr,FG_RED); + upx_snprintf(buf,sizeof(buf),"%s: ", iname); + pr_print(c,buf); + //(void)con_fg(stderr,FG_BRTRED); + pr_print(c,msg); + pr_print(c,"\n"); + fflush(stdout); fflush(stderr); + fg = con_fg(stderr,fg); + + UNUSED(is_warning); +} + + +void printErr(const char *iname, const Throwable *e) +{ + char buf[1024]; + + upx_snprintf(buf, sizeof(buf), "%s", prettyName(typeid(*e))); + if (e->getMsg()) + upx_snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),": %s", e->getMsg()); + if (e->getErrno()) + upx_snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),": %s", strerror(e->getErrno())); + pr_error(iname,buf,e->isWarning()); +} + + +void printErr(const char *iname, const char *format, ...) +{ + va_list args; + char buf[1024]; + + va_start(args,format); + upx_vsnprintf(buf,sizeof(buf),format,args); + va_end(args); + + pr_error(iname,buf,false); +} + + +void printWarn(const char *iname, const char *format, ...) +{ + va_list args; + char buf[1024]; + + va_start(args,format); + upx_vsnprintf(buf,sizeof(buf),format,args); + va_end(args); + + pr_error(iname,buf,true); +} + + +/************************************************************************* +// FIXME: should use colors and a consistent layout here +**************************************************************************/ + +static int info_header = 0; + + +static void info_print(const char *msg) +{ + if (opt->info_mode <= 0) + return; + FILE *f = opt->to_stdout ? stderr : stdout; + if (pr_need_nl) + { + printClearLine(f); + con_fprintf(f,"%s\n",msg); + } + else if (pr_need_nl) + con_fprintf(f,"\n%s\n",msg); + else + con_fprintf(f,"%s\n",msg); + fflush(f); + printSetNl(0); +} + + +void infoHeader() +{ + info_header = 0; +} + +void infoHeader(const char *format, ...) +{ + if (opt->info_mode <= 0) + return; + va_list args; + char buf[1024]; + va_start(args,format); + upx_vsnprintf(buf,sizeof(buf),format,args); + va_end(args); + info_print(buf); + info_header = 1; +} + + +void info(const char *format, ...) +{ + if (opt->info_mode <= 0) + return; + va_list args; + char buf[1024]; + const int n = 4 * info_header; + memset(buf, ' ', n); + va_start(args,format); + upx_vsnprintf(buf+n,sizeof(buf)-n,format,args); + va_end(args); + info_print(buf); +} + + +void infoWarning(const char *format, ...) +{ + if (opt->info_mode <= 0) + return; + va_list args; + char buf[1024]; + va_start(args,format); + upx_vsnprintf(buf,sizeof(buf),format,args); + va_end(args); + info("[WARNING] %s\n", buf); +} + + +void infoWriting(const char *what, long size) +{ + if (opt->info_mode <= 0) + return; + info("Writing %s: %ld bytes", what, size); +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/mygetopt.cpp b/src/mygetopt.cpp new file mode 100644 index 0000000000..da1dba386d --- /dev/null +++ b/src/mygetopt.cpp @@ -0,0 +1,696 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + 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; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "tailor.h" +#ifndef EOF +#include +#include +#endif + + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ +#undef GETOPT_COMPAT + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "mygetopt.h" +#define option mfx_option +#define optarg mfx_optarg +#define optind mfx_optind +#define opterr mfx_opterr +#define optopt mfx_optopt +#define my_index strchr +#define my_strlen strlen +#undef BAD_OPTION + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +#define BAD_OPTION '\0' +int optopt = BAD_OPTION; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. + + To perform the swap, we first reverse the order of all elements. So + all options now come before all non options, but they are in the + wrong order. So we put back the options and non options in original + order by reversing them again. For example: + original input: a b c -x -y + reverse all: -y -x c b a + reverse options: -x -y c b a + reverse non options: -x -y a b c +*/ + + +static void exchange (char **argv) +{ + char *temp, **first, **last; + + /* Reverse all the elements [first_nonopt, optind) */ + first = &argv[first_nonopt]; + last = &argv[optind-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + /* Put back the options in order */ + first = &argv[first_nonopt]; + first_nonopt += (optind - last_nonopt); + last = &argv[first_nonopt - 1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + + /* Put back the non options in order */ + first = &argv[first_nonopt]; + last_nonopt = optind; + last = &argv[last_nonopt-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return BAD_OPTION after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return BAD_OPTION. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +static int _getopt_internal (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only) +{ + static char empty_string[1]; + int option_index; + + if (longind != NULL) + *longind = -1; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } +#if 0 + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; +#endif + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound = 0; + int needexact = 0; + +#if defined(DOSISH) + /* allow `--option#value' because you cannout assign a '=' + to an environment variable under DOS command.com */ + while (*s && *s != '=' && * s != '#') + s++; +#else + while (*s && *s != '=') + s++; +#endif + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, (unsigned) (s - nextchar))) + { + if (p->has_arg & 0x10) + needexact = 1; + if ((unsigned) (s - nextchar) == my_strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + /* don't allow nonexact longoptions */ + if (needexact && !exact) + { + if (opterr) + fprintf (stderr, "%s: unrecognized option `%s'\n", + argv[0], argv[optind]); + nextchar += my_strlen (nextchar); + optind++; + return BAD_OPTION; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += my_strlen (nextchar); + optind++; + return BAD_OPTION; + } + + if (pfound != NULL) + { + int have_arg = (s[0] != '\0'); + if (have_arg && (pfound->has_arg & 0xf)) + have_arg = (s[1] != '\0'); + option_index = indfound; + optind++; + if (have_arg) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg & 0xf) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += my_strlen (nextchar); + return BAD_OPTION; + } + } + else if ((pfound->has_arg & 0xf) == 1) + { +#if 0 + if (optind < argc) +#else + if (optind < argc && (pfound->has_arg & 0x20) == 0) +#endif + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `--%s%s' requires an argument\n", + argv[0], pfound->name, + (pfound->has_arg & 0x20) ? "=" : ""); + nextchar += my_strlen (nextchar); + return optstring[0] == ':' ? ':' : BAD_OPTION; + } + } + nextchar += my_strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = empty_string; + optind++; + return BAD_OPTION; + } + ambig = ambig; + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return BAD_OPTION; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = BAD_OPTION; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int mfx_getopt(int argc, char **argv, const char *optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +int mfx_getopt_long(int argc, char **argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case BAD_OPTION: + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/mygetopt.h b/src/mygetopt.h new file mode 100644 index 0000000000..56970dec33 --- /dev/null +++ b/src/mygetopt.h @@ -0,0 +1,102 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + 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; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __MFX_GETOPT_H +#define __MFX_GETOPT_H 1 + +#ifdef __cplusplus +//extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *mfx_optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int mfx_optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int mfx_opterr; + +/* Set to an option character which was unrecognized. */ + +extern int mfx_optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct mfx_option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define mfx_no_argument 0 +#define mfx_required_argument 1 +#define mfx_optional_argument 2 +#define mfx_exact_argument 0x10 /* no abbrev. */ + +int mfx_getopt(int argc, char **argv, const char *shortopts); +int mfx_getopt_long(int argc, char **argv, const char *shortopts, + const struct mfx_option *longopts, int *longind); + +#ifdef __cplusplus +//} +#endif + +#endif /* _GETOPT_H */ diff --git a/src/p_com.cpp b/src/p_com.cpp new file mode 100644 index 0000000000..d09650afbb --- /dev/null +++ b/src/p_com.cpp @@ -0,0 +1,268 @@ +/* p_com.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "filter.h" +#include "packer.h" +#include "p_com.h" + +static const +#include "stub/l_com.h" + +#define STACKSIZE 0x60 + +//#define TESTING + + +/************************************************************************* +// +**************************************************************************/ + +int PackCom::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_LE16; +#if 0 + // NOT IMPLEMENTED + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_LE16; +#endif + return M_NRV2B_LE16; +} + + +const int *PackCom::getFilters() const +{ + static const int filters[] = { 0x06, 0x03, 0x04, 0x01, 0x05, 0x02, -1 }; + return filters; +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackCom::canPack() +{ + unsigned char buf[128]; + + fi->readx(buf,128); + if (memcmp(buf,"MZ",2) == 0 || memcmp(buf,"ZM",2) == 0 // .exe + || memcmp (buf,"\xff\xff\xff\xff",4) == 0) // .sys + return false; + if (!fn_has_ext(fi->getName(),"com")) + return false; + if (find_le32(buf,128,UPX_MAGIC_LE32)) + throwAlreadyPacked(); + if (file_size < 1024) + throwCantPack("file is too small"); + if (file_size > 0xFF00) + throwCantPack("file is too big for dos/com"); + return true; +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackCom::patchLoader(OutputFile *fo, + upx_byte *loader, int lsize, + unsigned calls, unsigned overlapoh) +{ + const int filter_id = ph.filter; + const int e_len = getLoaderSection("COMCUTPO"); + const int d_len = lsize - e_len; + assert(e_len > 0 && e_len < 256); + assert(d_len > 0 && d_len < 256); + + const unsigned upper_end = ph.u_len + overlapoh + d_len + 0x100; + if (upper_end + STACKSIZE > 0xfffe) + throwNotCompressible(); + + if (filter_id) + { + assert(calls > 0); + patch_le16(loader,lsize,"CT",calls); + } + // NOTE: Depends on: decompr_start == cutpoint+1 !!! + patch_le16(loader,e_len,"JM",upper_end - 0xff - d_len - getLoaderSection("UPX1HEAD")); + loader[getLoaderSection("COMSUBSI") - 1] = (upx_byte) -e_len; + patch_le16(loader,e_len,"DI",upper_end); + patch_le16(loader,e_len,"SI",ph.c_len + lsize + 0x100); + patch_le16(loader,e_len,"CX",ph.c_len + lsize); + patch_le16(loader,e_len,"SP",upper_end + STACKSIZE); + + // write loader + compressed file + fo->write(loader,e_len); // entry + fo->write(obuf,ph.c_len); + fo->write(loader+e_len,d_len); // decompressor +} + + +int PackCom::buildLoader(const Filter *ft) +{ + const int filter_id = ft->id; + initLoader(nrv2b_loader,sizeof(nrv2b_loader)); + addLoader("COMMAIN1""COMSUBSI", + filter_id ? "COMCALLT" : "", + "COMMAIN2""UPX1HEAD""COMCUTPO""NRV2B160", + filter_id ? "NRVDDONE" : "NRVDRETU", + "NRVDECO1", + ph.max_offset_found <= 0xd00 ? "NRVLED00" : "NRVGTD00", + "NRVDECO2""NRV2B169", + NULL + ); + if (filter_id) + addFilter16(filter_id); + return getLoaderSize(); +} + + +void PackCom::addFilter16(int filter_id) +{ + assert(filter_id > 0); + assert(isValidFilter(filter_id)); + + if (filter_id % 3 == 0) + addLoader("CALLTR16", + filter_id < 4 ? "CT16SUB0" : "", + filter_id < 4 ? "" : (opt->cpu == opt->CPU_8086 ? "CT16I086" : "CT16I286""CT16SUB0"), + "CALLTRI2", + getFormat() == UPX_F_DOS_COM ? "CORETURN" : "", + NULL + ); + else + addLoader(filter_id%3 == 1 ? "CT16E800" : "CT16E900", + "CALLTRI5", + getFormat() == UPX_F_DOS_COM ? "CT16JEND" : "CT16JUL2", + filter_id < 4 ? "CT16SUB1" : "", + filter_id < 4 ? "" : (opt->cpu == opt->CPU_8086 ? "CT16I087" : "CT16I287""CT16SUB1"), + "CALLTRI6", + NULL + ); +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackCom::pack(OutputFile *fo) +{ + // read file + ibuf.alloc(file_size); + obuf.allocForCompression(file_size); + fi->seek(0,SEEK_SET); + fi->readx(ibuf,file_size); + + // prepare packheader + ph.u_len = file_size; + ph.filter = 0; + // prepare filter + Filter ft(opt->level); + ft.addvalue = getCallTrickOffset(); + // prepare other settings + const unsigned overlap_range = ph.u_len < 0xFE00 - ft.addvalue ? 32 : 0; + unsigned overlapoh; + + int strategy = -1; // try the first working filter + if (opt->filter >= 0 && isValidFilter(opt->filter)) + // try opt->filter or 0 if that fails + strategy = -2; + else if (opt->all_filters || opt->level > 9) + // choose best from all available filters + strategy = 0; + else if (opt->level == 9) + // choose best from the first 4 filters + strategy = 4; + compressWithFilters(&ft, &overlapoh, overlap_range, strategy); + + const int lsize = getLoaderSize(); + MemBuffer loader(lsize); + memcpy(loader,getLoader(),lsize); + putPackHeader(loader,lsize); + + const unsigned calls = ft.id % 3 ? ft.lastcall - 2 * ft.calls : ft.calls; + patchLoader(fo, loader, lsize, calls, overlapoh); + + // verify + verifyOverlappingDecompression(&obuf, overlapoh); +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackCom::canUnpack() +{ + if (!readPackHeader(128, 0)) + return false; + if (file_size <= (off_t) ph.c_len) + return false; + return true; +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackCom::unpack(OutputFile *fo) +{ + ibuf.alloc(file_size); + obuf.allocForUncompression(ph.u_len); + + // read whole file + fi->seek(0,SEEK_SET); + fi->readx(ibuf,file_size); + + // get compressed data offset + int e_len = ph.buf_offset + ph.getPackHeaderSize(); + if (file_size <= e_len + (off_t)ph.c_len) + throwCantUnpack("file damaged"); + + // decompress + decompress(ibuf+e_len,obuf); + + // unfilter + Filter ft(ph.level); + ft.init(ph.filter, getCallTrickOffset()); + ft.unfilter(obuf,ph.u_len); + + // write decompressed file + if (fo) + fo->write(obuf,ph.u_len); +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_com.h b/src/p_com.h new file mode 100644 index 0000000000..8b3b244b01 --- /dev/null +++ b/src/p_com.h @@ -0,0 +1,69 @@ +/* p_com.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_COM_H +#define __UPX_P_COM_H + + +/************************************************************************* +// dos/com +**************************************************************************/ + +class PackCom : public Packer +{ + typedef Packer super; +public: + PackCom(InputFile *f) : super(f) { } + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_DOS_COM; } + virtual const char *getName() const { return "dos/com"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + +protected: + virtual const unsigned getCallTrickOffset() const { return 0x100; } + +protected: + virtual int buildLoader(const Filter *ft); + virtual void patchLoader(OutputFile *fo, upx_byte *, int, unsigned, unsigned); + virtual void addFilter16(int filter_id); +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_djgpp2.cpp b/src/p_djgpp2.cpp new file mode 100644 index 0000000000..4ade2505bd --- /dev/null +++ b/src/p_djgpp2.cpp @@ -0,0 +1,433 @@ +/* p_djgpp2.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + +#include "conf.h" +#include "file.h" +#include "filter.h" +#include "packer.h" +#include "p_djgpp2.h" + +static const unsigned char stubify_stub[] = { +#include "stub/stubify.h" +}; + +static const +#include "stub/l_djgpp2.h" + + +/************************************************************************* +// +**************************************************************************/ + +PackDjgpp2::PackDjgpp2(InputFile *f) : + super(f), coff_offset(0) +{ + assert(sizeof(coff_hdr) == 0xa8); + assert(sizeof(stubify_stub) == 2048); +} + + +int PackDjgpp2::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_LE32; + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_LE32; + return opt->level > 1 && file_size >= 512*1024 ? M_NRV2D_LE32 : M_NRV2B_LE32; +} + +const int *PackDjgpp2::getFilters() const +{ + static const int filters[] = { 0x26, 0x24, 0x11, 0x14, 0x13, 0x16, + 0x25, 0x15, 0x12, -1 }; + return filters; +} + + +int PackDjgpp2::buildLoader(const Filter *ft) +{ + // prepare loader + initLoader(nrv_loader,sizeof(nrv_loader)); + addLoader("IDENTSTR""DJ2MAIN1", + ft->id ? "DJCALLT1" : "", + "DJ2MAIN2", + getDecompressor(), + "DJ2BSS00", + NULL + ); + if (ft->id) + { + addLoader("DJCALLT2",NULL); + addFilter32(ft->id); + } + addLoader("DJRETURN+40DXXXXUPX1HEAD",NULL); + return getLoaderSize(); +} + + +/************************************************************************* +// util +**************************************************************************/ + +void PackDjgpp2::handleStub(OutputFile *fo) +{ + if (fo && !opt->djgpp2.coff) + { + if (coff_offset > 0) + { + // copy stub from exe + Packer::handleStub(fi,fo,coff_offset); + } + else + { + // "stubify" stub + info("Adding stub: %ld bytes", (long)sizeof(stubify_stub)); + fo->write(stubify_stub,sizeof(stubify_stub)); + } + } +} + + +static bool is_dlm(InputFile *fi,long coff_offset) +{ + unsigned char buf[4]; + long off; + + try { + fi->seek(coff_offset,SEEK_SET); + fi->readx(buf,4); + off = get_le32(buf); + if (off < 0 || off > coff_offset + 4) + return false; + fi->seek(off,SEEK_SET); + fi->readx(buf,4); + if (memcmp(buf,"DLMF",4) == 0) + return true; + } catch (IOException&) { + } + return false; +} + + +static void handle_allegropak(InputFile *fi,OutputFile *fo) +{ + unsigned char buf[0x4000]; + unsigned pfsize=0, ic; + + try { + fi->seek(-8,SEEK_END); + fi->readx(buf,8); + if (memcmp(buf,"slh+",4) != 0) + return; + pfsize = get_be32(buf+4); + fi->seek(-(off_t)pfsize,SEEK_END); + } catch (IOException&) { + return; + } + while (pfsize) + { + ic = pfsize < sizeof(buf) ? pfsize : sizeof(buf); + fi->readx(buf,ic); + fo->write(buf,ic); + pfsize -= ic; + } +} + + +bool PackDjgpp2::readFileHeader() +{ + unsigned char hdr[0x1c]; + unsigned char magic[8]; + + fi->seek(0,SEEK_SET); + fi->readx(hdr,sizeof(hdr)); + if (get_le16(hdr) == 0x5a4d) // MZ exe signature, stubbed? + { + coff_offset = 512 * get_le16(hdr+4); + if (get_le16(hdr+2) != 0) + coff_offset += get_le16(hdr+2) - 512; + fi->seek(512,SEEK_SET); + fi->readx(magic,8); + if (memcmp("go32stub",magic,8) != 0) + return false; // not V2 image + fi->seek(coff_offset,SEEK_SET); + if (fi->read(&coff_hdr,sizeof(coff_hdr)) != sizeof(coff_hdr)) + throwCantPack("skipping djgpp symlink"); + } + else + { + fi->seek(coff_offset,SEEK_SET); + fi->readx(&coff_hdr,0xa8); + } + if (coff_hdr.f_magic != 0x014c) // I386MAGIC + return false; + if ((coff_hdr.f_flags & 2) == 0) // F_EXEC - COFF executable + return false; + if (coff_hdr.a_magic != 0413) // ZMAGIC - demand load format + return false; + // FIXME: check for Linux etc. + + text = coff_hdr.sh; + data = text + 1; + bss = data + 1; + return true; +} + + +// "strip" debug info +void PackDjgpp2::stripDebug() +{ + coff_hdr.f_symptr = 0; + coff_hdr.f_nsyms = 0; + coff_hdr.f_flags = 0x10f; // 0x100: "32 bit machine: LSB first" + memset(text->misc,0,12); +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackDjgpp2::canPack() +{ + if (!readFileHeader()) + return false; + if (is_dlm(fi,coff_offset)) + throwCantPack("can't handle DLM"); + + if (opt->force == 0) + if (text->size != coff_hdr.a_tsize || data->size != coff_hdr.a_dsize) + throwAlreadyPacked(); + if (text->vaddr + text->size != data->vaddr + || data->vaddr + data->size != bss->vaddr) + { + if (text->vaddr + text->size < data->vaddr && + data->vaddr - text->vaddr == data->scnptr - text->scnptr) + { + // This hack is needed to compress Quake 1! + text->size = coff_hdr.a_tsize = data->vaddr - text->vaddr; + } + else + throwAlreadyPacked(); + } + // FIXME: check for Linux etc. + return true; +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackDjgpp2::pack(OutputFile *fo) +{ + handleStub(fo); + + // patch coff header #1: "strip" debug info + stripDebug(); + + // read file + const unsigned size = text->size + data->size; + const unsigned tpos = text->scnptr; + const unsigned usize = size + (tpos & 0x1ff); + const unsigned hdrsize = 20 + 28 + (40 * coff_hdr.f_nscns); + if (hdrsize < sizeof(coff_hdr) || hdrsize > tpos) + throwCantPack("coff header error"); + + ibuf.alloc(usize); + obuf.allocForCompression(usize); + + fi->seek(coff_offset,SEEK_SET); + fi->readx(ibuf,hdrsize); // orig. coff header + memset(ibuf + hdrsize, 0, tpos - hdrsize); + fi->seek(coff_offset+tpos,SEEK_SET); + fi->readx(ibuf + (tpos & 0x1ff),size); + +#if 0 + // filter + Filter ft(opt->level); + tryFilters(&ft, ibuf, usize - data->size, text->vaddr & ~0x1ff); + + // compress + ph.filter = ft.id; + ph.filter_cto = ft.cto; + ph.u_len = usize; + if (!compress(ibuf,obuf)) + throwNotCompressible(); + + unsigned overlapoh = findOverlapOverhead(obuf,ibuf,512); + overlapoh = (overlapoh + 0x3ff) & ~0x1ff; + + // verify filter + ft.verifyUnfilter(); +#else + // new version using compressWithFilters() + // prepare packheader + ph.u_len = usize; + ph.filter = 0; + // prepare filter + Filter ft(opt->level); + ft.buf_len = usize - data->size; + ft.addvalue = text->vaddr & ~0x1ff; + // prepare other settings + const unsigned overlap_range = 512; + unsigned overlapoh; + + int strategy = -1; // try the first working filter + if (opt->filter >= 0 && isValidFilter(opt->filter)) + // try opt->filter or 0 if that fails + strategy = -2; + else if (opt->all_filters) + // choose best from all available filters + strategy = 0; + compressWithFilters(&ft, &overlapoh, overlap_range, strategy); + overlapoh = (overlapoh + 0x3ff) & ~0x1ff; +#endif + + // patch coff header #2 + const unsigned lsize = getLoaderSize(); + text->size = lsize; // new size of .text + data->size = ph.c_len; // new size of .data + + if (bss->size < overlapoh) // give it a .bss + bss->size = overlapoh; + + text->scnptr = sizeof(coff_hdr); + data->scnptr = text->scnptr + text->size; + data->vaddr = bss->vaddr + ((data->scnptr + data->size) & 0x1ff) - data->size + overlapoh - 0x200; + coff_hdr.f_nscns = 3; + + // prepare loader + MemBuffer loader(lsize); + memcpy(loader,getLoader(),lsize); + + // patch loader + putPackHeader(loader,lsize); + patch_le32(loader,lsize,"ENTR",coff_hdr.a_entry); + if (ft.id) + { + assert(ft.calls > 0); + if (ft.id > 0x20) + patch_le16(loader,lsize,"??",'?' + (ft.cto << 8)); + patch_le32(loader,lsize,"TEXL",(ft.id & 0xf) % 3 == 0 ? ft.calls : + ft.lastcall - ft.calls * 4); + } + patch_le32(loader,lsize,"BSSL",overlapoh/4); + assert(bss->vaddr == ((size + 0x1ff) &~ 0x1ff) + (text->vaddr &~ 0x1ff)); + patch_le32(loader,lsize,"OUTP",text->vaddr &~ 0x1ff); + patch_le32(loader,lsize,"INPP",data->vaddr); + + // patch coff header #3 + text->vaddr = sizeof(coff_hdr); + coff_hdr.a_entry = sizeof(coff_hdr) + getLoaderSection("DJ2MAIN1"); + bss->vaddr += overlapoh; + bss->size -= overlapoh; + + // because of a feature (bug?) in stub.asm we need some padding + memcpy(obuf+data->size,"UPX",3); + data->size = ALIGN_UP(data->size,4); + + // write coff header, loader and compressed file + fo->write(&coff_hdr,sizeof(coff_hdr)); + fo->write(loader,lsize); + fo->write(obuf,data->size); + + // verify + verifyOverlappingDecompression(&obuf, overlapoh); + + // handle overlay + // FIXME: only Allegro pakfiles are supported + handle_allegropak(fi,fo); +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackDjgpp2::canUnpack() +{ + if (!readFileHeader()) + return false; + if (is_dlm(fi,coff_offset)) + throwCantUnpack("can't handle DLM"); + return readPackHeader(1024, coff_offset); +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackDjgpp2::unpack(OutputFile *fo) +{ + handleStub(fo); + + ibuf.alloc(ph.c_len); + obuf.allocForUncompression(ph.u_len); + + fi->seek(coff_offset + ph.buf_offset + ph.getPackHeaderSize(),SEEK_SET); + fi->readx(ibuf,ph.c_len); + + // decompress + decompress(ibuf,obuf); + + // unfilter + if (ph.filter) + { + memcpy(&coff_hdr,obuf,sizeof(coff_hdr)); + + Filter ft(ph.level); + ft.init(ph.filter, text->vaddr &~ 0x1ff); + ft.cto = (unsigned char) ph.filter_cto; + if (ph.version < 11) + { + unsigned char ctobuf[4]; + fi->readx(ctobuf, 4); + ft.cto = (unsigned char) (get_le32(ctobuf) >> 24); + } + ft.unfilter(obuf, ph.u_len - data->size); + } + + // fixup for the aligning bug in strip 2.8+ + text = ((coff_header_t*) (unsigned char *) obuf)->sh; + data = text + 1; + text->scnptr &= 0x1ff; + data->scnptr = text->scnptr + text->size; + + // write decompressed file + if (fo) + { + fo->write(obuf,ph.u_len); + handle_allegropak(fi,fo); + } +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_djgpp2.h b/src/p_djgpp2.h new file mode 100644 index 0000000000..8981cdf213 --- /dev/null +++ b/src/p_djgpp2.h @@ -0,0 +1,107 @@ +/* p_djgpp2.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_DJGPP2_H +#define __UPX_P_DJGPP2_H + + +/************************************************************************* +// djgpp2/coff +**************************************************************************/ + +class PackDjgpp2 : public Packer +{ + typedef Packer super; + +public: + PackDjgpp2(InputFile *f); + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_DJGPP2_COFF; } + virtual const char *getName() const { return "djgpp2/coff"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + +protected: + virtual int buildLoader(const Filter *ft); + virtual void handleStub(OutputFile *fo); + + long coff_offset; + + struct external_scnhdr_t + { + char _[12]; // name, paddr + LE32 vaddr; + LE32 size; + LE32 scnptr; + char misc[12]; // relptr, lnnoptr, nreloc, nlnno + char __[4]; // flags + }; + + struct coff_header_t + { + // ext_file_hdr + LE16 f_magic; + LE16 f_nscns; + char _[4]; // f_timdat + LE32 f_symptr; + LE32 f_nsyms; + char __[2]; // f_opthdr + LE16 f_flags; + + // aout_hdr + LE16 a_magic; + char ___[2]; // a_vstamp + LE32 a_tsize; + LE32 a_dsize; + char ____[4]; // a_bsize + LE32 a_entry; + char _____[8]; // a_text_start a_data_start + + // section headers + external_scnhdr_t sh[3]; + } coff_hdr; + + external_scnhdr_t *text,*data,*bss; + + bool readFileHeader(); + void stripDebug(); +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_elf.h b/src/p_elf.h new file mode 100644 index 0000000000..be76ad0d51 --- /dev/null +++ b/src/p_elf.h @@ -0,0 +1,83 @@ +/* p_elf.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_ELF_H +#define __UPX_P_ELF_H + + +/************************************************************************* +// Some ELF type definitinons +**************************************************************************/ + +// The ELF file header. This appears at the start of every ELF file. +struct Elf_LE32_Ehdr +{ + unsigned char e_ident[16]; /* Magic number and other info */ + LE16 e_type; /* Object file type */ + LE16 e_machine; /* Architecture */ + LE32 e_version; /* Object file version */ + LE32 e_entry; /* Entry point virtual address */ + LE32 e_phoff; /* Program header table file offset */ + LE32 e_shoff; /* Section header table file offset */ + LE32 e_flags; /* Processor-specific flags */ + LE16 e_ehsize; /* ELF header size in bytes */ + LE16 e_phentsize; /* Program header table entry size */ + LE16 e_phnum; /* Program header table entry count */ + LE16 e_shentsize; /* Section header table entry size */ + LE16 e_shnum; /* Section header table entry count */ + LE16 e_shstrndx; /* Section header string table index */ +}; + + +// Program segment header. +struct Elf_LE32_Phdr +{ + LE32 p_type; /* Segment type */ + LE32 p_offset; /* Segment file offset */ + LE32 p_vaddr; /* Segment virtual address */ + LE32 p_paddr; /* Segment physical address */ + LE32 p_filesz; /* Segment size in file */ + LE32 p_memsz; /* Segment size in memory */ + LE32 p_flags; /* Segment flags */ + LE32 p_align; /* Segment alignment */ +}; + + +// Values for p_type +#define PT_LOAD 1 /* Loadable program segment */ + +// Values for p_flags +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ + +#endif /* already included */ + +/* +vi:ts=4:et +*/ + diff --git a/src/p_exe.cpp b/src/p_exe.cpp new file mode 100644 index 0000000000..5103e92017 --- /dev/null +++ b/src/p_exe.cpp @@ -0,0 +1,658 @@ +/* p_exe.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "filter.h" +#include "packer.h" +#include "p_exe.h" + +static const +#include "stub/l_exe.h" + +#define RSFCRI 4096 // reserved space for compressed relocation info +#define MAXMATCH 0x2000 +#define MAXRELOCS (0x8000-MAXMATCH) + + +/************************************************************************* +// +**************************************************************************/ + +PackExe::PackExe(InputFile *f) : + super(f) +{ + assert(sizeof(exe_header_t) == 32); + ih_exesize = ih_imagesize = ih_overlay = 0; +} + + +int PackExe::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_8; + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_8; + return opt->level > 1 && ih_imagesize >= 300000 ? M_NRV2D_8 : M_NRV2B_8; +} + + +const int *PackExe::getFilters() const +{ + return NULL; +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackExe::readExeHeader() +{ + ih_exesize = ih_imagesize = ih_overlay = 0; + fi->readx(&ih,sizeof(ih)); + if (ih.ident != 'M' + 'Z'*256 && ih.ident != 'Z' + 'M'*256) + return false; + ih_exesize = ih.m512 + ih.p512*512 - (ih.m512 ? 512 : 0); + ih_imagesize = ih_exesize - ih.headsize16*16; + ih_overlay = file_size - ih_exesize; + if (ih.m512+ih.p512*512u < sizeof (ih)) + throwCantPack("illegal exe header"); + if (file_size < (off_t)ih_exesize || ih_imagesize <= 0 || ih_imagesize > ih_exesize) + throwCantPack("exe header corrupted"); +#if 0 + printf("dos/exe header: %d %d %d\n", ih_exesize, ih_imagesize, ih_overlay); +#endif + return true; +} + + +bool PackExe::canPack() +{ + if (fn_has_ext(fi->getName(),"sys")) + return false; + if (!readExeHeader()) + return false; + if (file_size < 1024) + throwCantPack("file is too small"); + fi->seek(0x3c,SEEK_SET); + LE32 offs; + fi->readx(&offs,sizeof (offs)); + if (ih.relocoffs >= 0x40 && offs) + { + if (opt->dos.force_stub) + opt->overlay = opt->COPY_OVERLAY; + else + throwCantPack("can't pack new-exe"); + } + return true; +} + + +/************************************************************************* +// +**************************************************************************/ + +static +unsigned optimize_relocs(upx_byte *b, const unsigned size, + const upx_byte *relocs, const unsigned nrelocs, + upx_byte *crel,bool *has_9a) +{ + upx_byte *crel_save = crel; + unsigned i; + unsigned seg_high = 0; +#if 0 + unsigned seg_low = 0xffffffff; + unsigned off_low = 0xffffffff; + unsigned off_high = 0; + unsigned linear_low = 0xffffffff; + unsigned linear_high = 0; +#endif + + // pass 1 - find 0x9a bounds + for (i = 0; i < nrelocs; i++) + { + unsigned addr = get_le32(relocs+4*i); + if (addr >= size - 1) + throwCantPack("unexpected relocation 1"); + if (addr >= 3 && b[addr-3] == 0x9a) + { + unsigned seg = get_le16(b+addr); + if (seg > seg_high) + seg_high = seg; +#if 0 + if (seg < seg_low) + seg_low = seg; + unsigned off = get_le16(b+addr-2); + if (off < off_low) + off_low = off; + if (off > off_high) + off_high = off; + unsigned l = (seg << 4) + off; + if (l < linear_low) + linear_low = l; + if (l > linear_high) + linear_high = l; +#endif + } + } + //printf("%d %d\n", seg_low, seg_high); + //printf("%d %d\n", off_low, off_high); + //printf("%d %d\n", linear_low, linear_high); + + + // pass 2 - reloc + + crel += 4; // to be filled in later + + unsigned ones = 0; + unsigned es = 0,di,t; + i = 0; + do + { + unsigned addr = get_le32(relocs+4*i); + set_le16(crel,di = addr & 0x0f); + set_le16(crel+2,(addr >> 4) - es); + es = addr >> 4; + crel += 4; + + for (++i; i < nrelocs; i++) + { + addr = get_le32(relocs+4*i); + //printf ("%x\n",es*16+di); + if (addr - es*16 > 0xfffe) + { + // segment change + t = 1+(0xffff-di)/254; + memset(crel,1,t); + crel += t; + ones += t-1; // -1 is used to help the assembly stuff + break; + } + unsigned offs = addr - es*16; + if (offs >= 3 && b[es*16 + offs-3] == 0x9a) + { + for (t = di; t < offs-3; t++) + if (b[es*16+t] == 0x9a && get_le16(b+es*16+t+3) <= seg_high) + break; + if (t == offs-3) + { + // code 0: search for 0x9a + *crel++ = 0; + di = offs; + *has_9a = true; + continue; + } + } + t = offs - di; + if (t < 2) + throwCantPack("unexpected relocation 1"); + + while (t >= 256) + { + // code 1: add 254, don't reloc + *crel++ = 1; + t -= 254; + ones++; + } + *crel++ = (unsigned char) t; + di = offs; + } + } while (i < nrelocs); + *crel++ = 1; + ones++; + set_le16 (crel_save,ones); + set_le16 (crel_save+2,seg_high); + +#if 0 // def TESTING + //if (opt->debug >= 3) + { + FILE *f1=fopen ("x.rel","wb"); + fwrite (crel_save,crel-crel_save,1,f1); + fclose (f1); + } +#endif + return crel - crel_save; +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackExe::pack(OutputFile *fo) +{ + unsigned ic; + unsigned char flag = 0; + + char extra_info[32]; + unsigned eisize = 0; + + // + const unsigned exesize = ih_exesize; + const unsigned imagesize = ih_imagesize; + const unsigned overlay = ih_overlay; + if (ih.relocs > MAXRELOCS) + throwCantPack("too many relocations"); + checkOverlay(overlay); + + // alloc buffers + unsigned relocsize = RSFCRI + 4*ih.relocs; + + ibuf.alloc(imagesize+16+relocsize+2); + obuf.allocForCompression(imagesize+16+relocsize+2); + + // read image + fi->seek(ih.headsize16*16,SEEK_SET); + fi->readx(ibuf,imagesize); + + if (find_le32(ibuf,imagesize < 127 ? imagesize : 127,UPX_MAGIC_LE32)) + throwAlreadyPacked(); + + // relocations + has_9a = false; + upx_byte *w = ibuf + imagesize; + if (ih.relocs) + { + upx_byte *wr = w + RSFCRI; + + fi->seek(ih.relocoffs,SEEK_SET); + fi->readx(wr,4*ih.relocs); + + for (ic = 0; ic < ih.relocs; ic++) + { + unsigned jc = get_le32(wr+4*ic); + set_le32(wr+4*ic, (jc>>16)*16+(jc&0xffff)); + } + qsort(wr,ih.relocs,4,le32_compare); + relocsize = optimize_relocs(ibuf, imagesize, wr, ih.relocs, w, &has_9a); + set_le16(w+relocsize, relocsize+2); + relocsize += 2; + if (relocsize > MAXRELOCS) + throwCantPack("too many relocations"); +#if 0 + upx_byte out[9*relocsize/8+1024]; + unsigned in_len = relocsize; + unsigned out_len = 0; + ucl_nrv2b_99_compress(w, in_len, out, &out_len, NULL, 9, NULL, NULL); + printf("reloc compress: %d -> %d\n", in_len, out_len); +#endif + } + else + { + flag |= NORELOC; + relocsize = 0; + } + + ph.u_len = imagesize+relocsize; + if (!compress(ibuf,obuf,0,MAXMATCH)) + throwNotCompressible(); + const unsigned overlapoh = findOverlapOverhead(obuf,32); + + if (ph.max_run_found + ph.max_match_found > 0x8000) + throwCantPack("decompressor limit exceeded, send a bugreport"); +#ifdef TESTING + if (opt->debug) + { + printf("image+relocs %d -> %d\n",imagesize+relocsize,ph.c_len); + printf("offsets: %d - %d\nmatches: %d - %d\nruns: %d - %d\n", + ph.min_offset_found,ph.max_offset_found, + ph.min_match_found,ph.max_match_found, + ph.min_run_found,ph.max_run_found); + } +#endif + const unsigned packedsize = ph.c_len; + + if (!opt->dos.no_reloc) + flag |= USEJUMP; + + // fill new exe header + memset(&oh,0,sizeof(oh)); + oh.ident = 'M' + 'Z' * 256; + + unsigned destpara = (ph.u_len+overlapoh-packedsize+31)/16; + + oh.ss = packedsize/16+destpara; + if (ih.ss*16 + ih.sp < 0x100000 && ih.ss > oh.ss && ih.sp > 0x200) + oh.ss = ih.ss; + oh.sp = ih.sp > 0x200 ? ih.sp : 0x200; + if (oh.ss*16u + 0x50 < ih.ss*16u + ih.sp + && oh.ss*16u + 0x200 > ih.ss*16u + ih.sp) + oh.ss += 0x20; + + destpara = oh.ss - packedsize/16; + if (oh.ss != ih.ss) + { + set_le16(extra_info+eisize,ih.ss); + eisize += 2; + flag |= SS; + } + if (oh.sp != ih.sp) + { + set_le16(extra_info+eisize,ih.sp); + eisize += 2; + flag |= SP; + } + +#define DI_LIMIT 0xff00 // see the assembly why + // prepare loader + initLoader(nrv_loader,sizeof(nrv_loader)); + addLoader("EXEENTRY", + relocsize ? "EXERELPU" : "", + "EXEMAIN4""+G5DXXXX""UPX1HEAD""EXECUTPO", + NULL + ); + if (ph.method == M_NRV2B_8) + addLoader("NRV2B16S", // decompressor + ph.u_len > DI_LIMIT ? "NDIGT64K" : "", + "NRV2BEX1", + opt->cpu == opt->CPU_8086 ? "N2BX8601" : "N2B28601", + "NRV2BEX2", + opt->cpu == opt->CPU_8086 ? "N2BX8602" : "N2B28602", + "NRV2BEX3", + packedsize > 0xffff ? "NSIGT64K" : "", + "NRV2BEX9""NRV2B16E", + NULL + ); + else if (ph.method == M_NRV2D_8) + addLoader("NRV2D16S", + ph.u_len > DI_LIMIT ? "NDIGT64D" : "", + "NRV2DEX1", + opt->cpu == opt->CPU_8086 ? "N2DX8601" : "N2D28601", + "NRV2DEX2", + opt->cpu == opt->CPU_8086 ? "N2DX8602" : "N2D28602", + "NRV2DEX3", + packedsize > 0xffff ? "NSIGT64D" : "", + "NRV2DEX9""NRV2D16E", + NULL + ); + else + throwInternalError("unknown compression method"); + addLoader("EXEMAIN5", NULL); + if (relocsize) + addLoader(ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? "EXENOADJ" : "EXEADJUS", + "EXERELO1", + has_9a ? "EXEREL9A" : "", + "EXERELO2", + exesize > 0xFE00 ? "EXEREBIG" : "", + "EXERELO3", + NULL + ); + addLoader("EXEMAIN8", + (flag & SS) ? "EXESTACK" : "", + (flag & SP) ? "EXESTASP" : "", + (flag & USEJUMP) ? "EXEJUMPF" : "", + NULL + ); + if (!(flag & USEJUMP)) + addLoader(ih.cs ? "EXERCSPO" : "", + "EXERETIP", + NULL + ); + const unsigned lsize = getLoaderSize(); + MemBuffer loader(lsize); + memcpy(loader,getLoader(),lsize); + //OutputFile::dump("xxloader.dat", loader, lsize); + + // patch loader + putPackHeader(loader,lsize); + const unsigned e_len = getLoaderSection("EXECUTPO"); + const unsigned d_len = lsize - e_len; + assert((e_len&15) == 0); + + const unsigned copysize = (1+packedsize+d_len) & ~1; + const unsigned firstcopy = copysize%0x10000 ? copysize%0x10000 : 0x10000; + + oh.headsize16 = 2; + oh.ip = 0; + + ic = ih.min*16+imagesize; + if (ic < oh.ss*16u + oh.sp) + ic = oh.ss*16u + oh.sp; + + oh.min = (ic - (packedsize + lsize)) / 16; + ic = ((unsigned) oh.min) + (ih.max - ih.min); + oh.max = ic < 0xffff && ih.max != 0xffff ? ic : 0xffff; + + if (ih.min != oh.min) + { + set_le16(extra_info+eisize,ih.min); + eisize += 2; + flag |= MINMEM; + } + if (ih.max != oh.max) + { + set_le16(extra_info+eisize,ih.max); + eisize += 2; + flag |= MAXMEM; + } + + putPackHeader(loader,lsize); + upx_bytep p = find_le32(loader,lsize,get_le32("IPCS")); + if (p == NULL && (flag & USEJUMP)) + throwBadLoader(); + if (flag & USEJUMP) + memcpy(p,&ih.ip,4); + else + { + patch_le16(loader,lsize,"IP",ih.ip); + if (ih.cs) + patch_le16(loader,lsize,"CS",ih.cs); + } + if (flag & SP) + patch_le16(loader,lsize,"SP",ih.sp); + if (flag & SS) + patch_le16(loader,lsize,"SS",ih.ss); + if (relocsize) + patch_le16(loader,lsize,"RS",(ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? 0 : MAXRELOCS) - relocsize); + + patch_le16(loader,e_len,"BX",0x800F + 0x10*((packedsize&15)+1) - 0x10); + patch_le16(loader,e_len,"BP",(packedsize&15)+1); + + patch_le16(loader,e_len,"ES",destpara-e_len/16); + patch_le16(loader,e_len,"DS",e_len/16+(copysize-firstcopy)/16); + patch_le16(loader,e_len,"SI",firstcopy-2); + patch_le16(loader,e_len,"CX",firstcopy/2); + + // finish --stub support + //if (ih.relocoffs >= 0x40 && memcmp(&ih.relocoffs,">TIPPACH",8)) + // throwCantPack("FIXME"); + // I use a relocation entry to set the original cs + oh.relocs = (flag & USEJUMP) ? 1 : 0; + oh.relocoffs = (char*)(&oh.firstreloc)-(char*)&oh; + oh.firstreloc = (p-loader) + packedsize + 2; + oh.firstreloc = (oh.firstreloc&0xf)+((oh.firstreloc>>4)<<16); + if (!(flag & USEJUMP)) + oh.firstreloc = ih.cs*0x10000 + ih.ip; + + extra_info[eisize++] = flag; + const unsigned outputlen = sizeof(oh)+lsize+packedsize+eisize; + oh.m512 = outputlen & 511; + oh.p512 = (outputlen + 511) >> 9; + +//fprintf(stderr,"\ne_len=%x d_len=%x clen=%x oo=%x ulen=%x destp=%x copys=%x images=%x",e_len,d_len,packedsize,overlapoh,ph.u_len,destpara,copysize,imagesize); + // write header + write loader + compressed file +#ifdef TESTING + if (opt->debug) + printf("\n%d %d %d %d\n",(int)sizeof(oh),e_len,packedsize,d_len); +#endif + fo->write(&oh,sizeof(oh)); + fo->write(loader,e_len); // entry + fo->write(obuf,packedsize); + fo->write(loader+e_len,d_len); // decompressor + fo->write(extra_info,eisize); + + // verify + verifyOverlappingDecompression(&obuf, overlapoh); + + // copy the overlay + copyOverlay(fo, overlay, &obuf); +//fprintf (stderr,"%x %x\n",relocsize,ph.u_len); +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackExe::canUnpack() +{ + if (!readExeHeader()) + return false; + const off_t off = ih.headsize16*16; + bool b = readPackHeader(128, off); + return b && (off + (off_t) ph.c_len <= file_size); +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackExe::unpack(OutputFile *fo) +{ + ibuf.alloc(file_size); + obuf.allocForUncompression(ph.u_len); + + // read the file + unsigned imagesize = ih_imagesize; + const unsigned overlay = ih_overlay; + + fi->seek(ih.headsize16*16,SEEK_SET); + fi->readx(ibuf,imagesize); + + // get compressed data offset + unsigned e_len = ph.buf_offset + ph.getPackHeaderSize(); + if (imagesize <= e_len + ph.c_len) + throwCantUnpack("file damaged"); + + checkOverlay(overlay); + + // decompress + decompress(ibuf+e_len,obuf); + + const unsigned char flag = ibuf[imagesize-1]; + + unsigned relocn = 0; + upx_byte *relocs = obuf + ph.u_len; + + MemBuffer wrkmem; + if (!(flag & NORELOC)) + { + relocs -= get_le16(obuf+ph.u_len-2); + ph.u_len -= 2; + upx_byte *p; + + wrkmem.alloc(4*MAXRELOCS); + unsigned es = 0,ones = get_le16(relocs); + unsigned seghi = get_le16(relocs+2); + p = relocs + 4; + + while (ones) + { + unsigned di = get_le16(p); + es += get_le16(p+2); + bool dorel = true; + for (p += 4; ones && di < 0x10000; p++) + { + if (dorel) + { + set_le16(wrkmem+4*relocn,di); + set_le16(wrkmem+2+4*relocn++,es); + //printf ("%x\n",es*16+di); + } + dorel = true; + if (*p == 0) + { + upx_byte *q; + for (q = obuf+es*16+di; !(*q == 0x9a && get_le16(q+3) <= seghi); q++) + ; + di = q - (obuf+es*16) + 3; + } + else if (*p == 1) + { + di += 254; + if (di < 0x10000) + ones--; + dorel = false; + } + else + di += *p; + } + } + } + // fill new exe header + memset(&oh,0,sizeof(oh)); + oh.ident = 'M' + 'Z'*256; + + oh.relocs = relocn; + while (relocn&3) + set_le32(wrkmem+4*relocn++,0); + + unsigned outputlen = sizeof(oh)+relocn*4+relocs-obuf; + oh.m512 = outputlen & 511; + oh.p512 = (outputlen + 511) >> 9; + oh.headsize16 = 2+relocn/4; + + imagesize--; + oh.max = ih.max; + oh.min = ih.min; + oh.sp = ih.sp; + oh.ss = ih.ss; + + if (flag & MAXMEM) + imagesize -= 2, oh.max = get_le16(ibuf+imagesize); + if (flag & MINMEM) + imagesize -= 2, oh.min = get_le16(ibuf+imagesize); + if (flag & SP) + imagesize -= 2, oh.sp = get_le16(ibuf+imagesize); + if (flag & SS) + imagesize -= 2, oh.ss = get_le16(ibuf+imagesize); + + unsigned ip = (flag & USEJUMP) ? get_le32(ibuf+imagesize-4) : ih.firstreloc; + oh.ip = ip & 0xffff; + oh.cs = ip >> 16; + + oh.relocoffs = sizeof(oh); + oh.firstreloc = 0; + if (!fo) + return; + + // write header + relocations + uncompressed file + fo->write(&oh,sizeof(oh)); + fo->write(wrkmem,relocn*4); + fo->write(obuf,relocs-obuf); + + // copy the overlay + copyOverlay(fo, overlay, &obuf); +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_exe.h b/src/p_exe.h new file mode 100644 index 0000000000..422d16a72c --- /dev/null +++ b/src/p_exe.h @@ -0,0 +1,109 @@ +/* p_exe.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_EXE_H +#define __UPX_P_EXE_H + + +/************************************************************************* +// dos/exe +**************************************************************************/ + +class PackExe : public Packer +{ + typedef Packer super; +public: + PackExe(InputFile *f); + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_DOS_EXE; } + virtual const char *getName() const { return "dos/exe"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + + // unpacker capabilities + virtual bool canUnpackVersion(int version) const + { + // NOTE: could adapt p_exe.cpp to support (version >= 8) + return (version >= 10); + } + virtual bool canUnpackFormat(int format) const + { + return (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH); + } + +protected: + virtual bool readExeHeader(void); + + struct exe_header_t + { + LE16 ident; + LE16 m512; + LE16 p512; + LE16 relocs; + LE16 headsize16; + LE16 min; + LE16 max; + LE16 ss; + LE16 sp; + char _[2]; // checksum + LE16 ip; + LE16 cs; + LE16 relocoffs; + char __[2]; // overlnum + LE32 firstreloc; + } ih, oh; + + unsigned ih_exesize; + unsigned ih_imagesize; + unsigned ih_overlay; + + bool has_9a; + + enum { + NORELOC = 1, + USEJUMP = 2, + SS = 4, + SP = 8, + MINMEM = 16, + MAXMEM = 32 + }; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_lx_elf.cpp b/src/p_lx_elf.cpp new file mode 100644 index 0000000000..7436f3b854 --- /dev/null +++ b/src/p_lx_elf.cpp @@ -0,0 +1,504 @@ +/* p_lx_elf.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + Copyright (C) 2000 John F. Reiser. All rights reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + John F. Reiser + jreiser@BitWagon.com + */ + + +#include "conf.h" + +#include "file.h" +#include "packer.h" +#include "p_elf.h" +#include "p_unix.h" +#include "p_lx_elf.h" + + +static const +#include "stub/l_le_n2b.h" +static const +#include "stub/l_le_n2d.h" + +PackLinuxI386elf::~PackLinuxI386elf() +{ +} + +PackLinuxI386elf::PackLinuxI386elf(InputFile *f) + :super(f) + ,phdri(0) +{ +} + +const upx_byte *PackLinuxI386elf::getLoader() const +{ + if (M_IS_NRV2B(opt->method)) + return linux_i386elf_nrv2b_loader; + if (M_IS_NRV2D(opt->method)) + return linux_i386elf_nrv2d_loader; + return NULL; +} + +int PackLinuxI386elf::getLoaderSize() const +{ + if (0 != lsize) { + return lsize; + } + if (M_IS_NRV2B(opt->method)) + return sizeof(linux_i386elf_nrv2b_loader); + if (M_IS_NRV2D(opt->method)) + return sizeof(linux_i386elf_nrv2d_loader); + return 0; +} + + +static inline off_t min_off_t(off_t a, off_t b) +{ + return a < b ? a : b; +} + +static inline off_t max_off_t(off_t a, off_t b) +{ + return a > b ? a : b; +} + +static off_t getbrk(Elf_LE32_Phdr const *phdr, int e_phnum) +{ + off_t brka = 0; + for (int j = 0; j < e_phnum; ++phdr, ++j) if (PT_LOAD==phdr->p_type) { + brka = max_off_t(brka, phdr->p_vaddr + phdr->p_memsz); + } + return brka; +} + + +void PackLinuxI386elf::updateLoader(OutputFile *fo) +{ +#define PAGE_MASK (~0<<12) + Elf_LE32_Phdr *const phdro = (Elf_LE32_Phdr *)(sizeof(Elf_LE32_Ehdr)+loader); + off_t const totlen = fo->getBytesWritten(); + phdro->p_filesz = totlen; + + // pre-calculate for benefit of runtime disappearing act via munmap() + phdro->p_memsz = PAGE_MASK & (~PAGE_MASK + totlen); + + patchLoaderChecksum(); + fo->seek(0, SEEK_SET); + fo->rewrite(loader, 0x80); +#undef PAGE_MASK +} + +void PackLinuxI386elf::patchLoader() +{ + lsize = getLoaderSize(); + Elf_LE32_Ehdr *const ehdr = (Elf_LE32_Ehdr *)(void *)loader; + Elf_LE32_Phdr *const phdr = (Elf_LE32_Phdr *)(1+ehdr); + + // stub/scripts/setfold.pl puts address of 'fold_begin' in phdr[1].p_offset + off_t const fold_begin = phdr[1].p_offset + 0x80; + upx_byte *cprLoader = new upx_byte[lsize]; + + // compress compiled C-code portion of loader + upx_compress_config_t conf; memset(&conf, 0xff, sizeof(conf)); + conf.c_flags = 0; + upx_uint result_buffer[16]; + size_t cprLsize; + upx_compress( + loader + fold_begin, lsize - fold_begin, + cprLoader, &cprLsize, + 0, // progress_callback_t ?? + getCompressionMethod(), 9, + &conf, + result_buffer + ); + set_le32(0+fold_begin+loader, lsize - fold_begin); + set_le32(4+fold_begin+loader, cprLsize); + memcpy( 8+fold_begin+loader, cprLoader, cprLsize); + lsize = 8 + fold_begin + cprLsize; + patchVersion(loader,lsize); + + // Info for OS kernel to set the brk() + unsigned const brka = getbrk(phdri, ehdri.e_phnum); + phdr[1].p_offset = 0xfff&brka; + phdr[1].p_vaddr = brka; + phdr[1].p_paddr = brka; + phdr[1].p_filesz = 0; + phdr[1].p_memsz = 0; + + // The beginning of our loader consists of a elf_hdr (52 bytes) and + // two sections elf_phdr (2 * 32 byte), so we have 12 free bytes + // from offset 116 to the program start at offset 128. + assert(ehdr->e_phoff == sizeof(*ehdr)); + assert(ehdr->e_ehsize == sizeof(*ehdr)); + assert(ehdr->e_phentsize == sizeof(Elf_LE32_Phdr)); + assert(ehdr->e_phnum == 2); + assert(ehdr->e_shnum == 0); + assert(lsize > 128 && lsize < 4096); + + patchLoaderChecksum(); +} + + +bool PackLinuxI386elf::canPack() +{ + unsigned char buf[512]; + + fi->readx(buf,512); + fi->seek(0,0); + if (0==memcmp(buf, "\x7f\x45\x4c\x46\x01\x01\x01", 7)) { // ELF 32-bit LSB + Elf_LE32_Ehdr const *const ehdr = (Elf_LE32_Ehdr const *)buf; + Elf_LE32_Phdr const *phdr = (Elf_LE32_Phdr const *)(ehdr->e_phoff + + (char const *)ehdr); + if (ehdr->e_phoff != sizeof(*ehdr)) {// Phdrs not contiguous with Ehdr + return false; + } + // The first PT_LOAD must cover the beginning of the file (0==p_offset). + for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) { + if (PT_LOAD==phdr->p_type) { + if (phdr->p_offset!=0) { + return false; + } + break; + } + } + return super::canPack(); + } + return false; +} + + +void PackLinuxI386elf::packExtent( + Extent const &x, + OutputFile *fo, + unsigned &total_in, + unsigned &total_out +) +{ + blocksize = opt->unix.blocksize; + if ((off_t)blocksize > x.size) + blocksize = x.size; + + fi->seek(x.offset, SEEK_SET); + for (off_t rest = x.size; 0!=rest; ) + { + int l = fi->readx(ibuf, min_off_t(rest, blocksize)); + rest -= l; + if (l == 0) + break; + + // Note: compression for a block can fail if the + // file is e.g. blocksize + 1 bytes long + + // compress + ph.u_len = l; + compress(ibuf, obuf); // ignore return value + + if (ph.c_len < ph.u_len) + { + if (!testOverlappingDecompression(obuf, OVERHEAD)) + throwNotCompressible(); + } + else + { + ph.c_len = ph.u_len; + // must update checksum of compressed data + ph.c_adler = upx_adler32(ph.c_adler, ibuf, ph.u_len); + } + + // write block sizes + unsigned char size[8]; + set_native32(size+0, ph.u_len); + set_native32(size+4, ph.c_len); + fo->write(size, 8); + + // write compressed data + if (ph.c_len < ph.u_len) + { + fo->write(obuf, ph.c_len); + verifyOverlappingDecompression(&obuf, OVERHEAD); + } + else + fo->write(ibuf, ph.u_len); + + total_in += ph.u_len; + total_out += ph.c_len; + } +} + + +void PackLinuxI386elf::pack(OutputFile *fo) +{ + // set options + opt->unix.blocksize = file_size; + blocksize = file_size; + progid = 0; // not used + + fi->readx(&ehdri, sizeof(ehdri)); + off_t const sz_phdrs = ehdri.e_phnum * ehdri.e_phentsize; + + phdri = new Elf_LE32_Phdr[ehdri.e_phnum]; + fi->seek(ehdri.e_phoff, SEEK_SET); + fi->readx(phdri, sz_phdrs); + + // prepare loader + lsize = getLoaderSize(); + loader.alloc(lsize + sizeof(p_info)); + memcpy(loader,getLoader(),lsize); + + // patch loader, prepare header info, write loader + header info + patchLoader(); // can change lsize by packing upx_main + p_info *const hbuf = (p_info *)(loader + lsize); + set_native32(&hbuf->p_progid, progid); + set_native32(&hbuf->p_filesize, file_size); + set_native32(&hbuf->p_blocksize, blocksize); + fo->write(loader, lsize + sizeof(p_info)); + + // init compression buffers + ibuf.alloc(blocksize); + obuf.allocForCompression(blocksize); + + assert(ehdri.e_phoff == sizeof(Elf_LE32_Ehdr)); // checked by canPack() + Extent x; + unsigned k; + + // count + total_passes = 0; + off_t ptload0hi=0, ptload1lo=0; + int nx = 0; + for (k = 0; k < ehdri.e_phnum; ++k) { + if (PT_LOAD==phdri[k].p_type) { + x.offset = phdri[k].p_offset; + x.size = phdri[k].p_filesz; + if (0==ptload0hi) { + ptload0hi = x.size + x.offset; + } + else if (0==ptload1lo) { + ptload1lo = x.offset; + } + total_passes++; + } else { + if (nx++ == 0) + total_passes++; + } + } + if (ptload0hi < ptload1lo) + total_passes++; + + // compress extents + unsigned total_in = 0; + unsigned total_out = 0; + + x.offset = 0; + x.size = sizeof(Elf_LE32_Ehdr) + sz_phdrs; + pass = -1; + packExtent(x, fo, total_in, total_out); + pass = 0; + + nx = 0; + for (k = 0; k < ehdri.e_phnum; ++k) if (PT_LOAD==phdri[k].p_type) { + x.offset = phdri[k].p_offset; + x.size = phdri[k].p_filesz; + if (0==nx) { + x.offset += sizeof(Elf_LE32_Ehdr) + sz_phdrs; + x.size -= sizeof(Elf_LE32_Ehdr) + sz_phdrs; + } + packExtent(x, fo, total_in, total_out); + ++nx; + } + if (ptload0hi < ptload1lo) { // alignment hole? + x.offset = ptload0hi; + x.size = ptload1lo - ptload0hi; + packExtent(x, fo, total_in, total_out); + } + if ((off_t)total_in < file_size) { // non-PT_LOAD stuff + x.offset = total_in; + x.size = file_size - total_in; + packExtent(x, fo, total_in, total_out); + } + + if ((off_t)total_in != file_size) + throw EOFException(); + + // write block end marker (uncompressed size 0) + fo->write("\x00\x00\x00\x00", 4); + + // update header with totals + ph.u_len = total_in; + ph.c_len = total_out; + + // write header + const int hsize = ph.getPackHeaderSize(); + set_le32(obuf, ph.magic); // note: always le32 + putPackHeader(obuf, hsize); + fo->write(obuf, hsize); + + // write overlay offset (needed for decompression) + set_native32(obuf, lsize); + fo->write(obuf, 4); + + updateLoader(fo); + + // finally check compression ratio + if (!super::checkCompressionRatio(fo->getBytesWritten(), ph.u_len)) + throwNotCompressible(); +} + + +void PackLinuxI386elf::unpackExtent(unsigned wanted, OutputFile *fo, + unsigned &total_in, unsigned &total_out, + unsigned &c_adler, unsigned &u_adler) +{ + while (wanted) { + fi->readx(ibuf, 8); + int const sz_unc = ph.u_len = get_native32(ibuf+0); + int const sz_cpr = ph.c_len = get_native32(ibuf+4); + + if (sz_unc == 0) { // must never happen while 0!=wanted + throwCompressedDataViolation(); + break; + } + if (sz_unc <= 0 || sz_cpr <= 0) + throwCompressedDataViolation(); + if (sz_cpr > sz_unc || sz_unc > (int)blocksize) + throwCompressedDataViolation(); + + int j = blocksize + OVERHEAD - sz_cpr; + fi->readx(ibuf+j, sz_cpr); + // update checksum of compressed data + c_adler = upx_adler32(c_adler, ibuf + j, sz_cpr); + // decompress + if (sz_cpr < sz_unc) + { + decompress(ibuf+j, ibuf, false); + j = 0; + } + // update checksum of uncompressed data + u_adler = upx_adler32(u_adler, ibuf + j, sz_unc); + total_in += sz_cpr; + total_out += sz_unc; + // write block + if (fo) + fo->write(ibuf + j, sz_unc); + wanted -= sz_unc; + } +} + + +bool PackLinuxI386elf::canUnpackFormat(int format) const +{ + return UPX_F_LINUX_ELF_i386==format || UPX_F_LINUX_SEP_i386==format; +} + +void PackLinuxI386elf::unpack(OutputFile *fo) +{ +#define MAX_ELF_HDR 512 + char bufehdr[MAX_ELF_HDR]; + Elf_LE32_Ehdr *const ehdr = (Elf_LE32_Ehdr *)bufehdr; + Elf_LE32_Phdr const *phdr = (Elf_LE32_Phdr *)(1+ehdr); + + fi->seek(overlay_offset, SEEK_SET); + p_info hbuf; + fi->readx(&hbuf, sizeof(hbuf)); + unsigned orig_file_size = get_native32(&hbuf.p_filesize); + blocksize = get_native32(&hbuf.p_blocksize); + if (file_size > (off_t)orig_file_size || blocksize > orig_file_size) + throwCantUnpack("file header corrupted"); + + ibuf.alloc(blocksize + OVERHEAD); + fi->readx(ibuf, 2*4); + ph.u_len = get_native32(0+ibuf); + ph.c_len = get_native32(4+ibuf); + + // Uncompress Ehdr and Phdrs. + fi->readx(ibuf, ph.c_len); + decompress(ibuf, (upx_byte *)ehdr, false); + + unsigned total_in = 0; + unsigned total_out = 0; + unsigned c_adler = upx_adler32(0, NULL, 0); + unsigned u_adler = upx_adler32(0, NULL, 0); + off_t ptload0hi=0, ptload1lo=0; + + // decompress PT_LOAD + fi->seek(-(2*4 + ph.c_len), SEEK_CUR); + for (unsigned j=0; j < ehdr->e_phnum; ++phdr, ++j) { + if (PT_LOAD==phdr->p_type) { + if (0==ptload0hi) { + ptload0hi = phdr->p_filesz + phdr->p_offset; + } + else if (0==ptload1lo) { + ptload1lo = phdr->p_offset; + } + if (fo) + fo->seek(phdr->p_offset, SEEK_SET); + unpackExtent(phdr->p_filesz, fo, total_in, total_out, c_adler, u_adler); + } + } + + if (ptload0hi < ptload1lo) { // alignment hole? + if (fo) + fo->seek(ptload0hi, SEEK_SET); + unpackExtent(ptload1lo - ptload0hi, fo, total_in, total_out, c_adler, u_adler); + } + if (total_out != orig_file_size) { // non-PT_LOAD stuff + if (fo) + fo->seek(0, SEEK_END); + unpackExtent(orig_file_size - total_out, fo, total_in, total_out, c_adler, u_adler); + } + + // check for end-of-file + fi->readx(ibuf, 2*4); + unsigned const sz_unc = ph.u_len = get_native32(ibuf+0); + + if (sz_unc == 0) { // uncompressed size 0 -> EOF + // note: magic is always stored le32 + unsigned const sz_cpr = get_le32(ibuf+4); + if (sz_cpr != UPX_MAGIC_LE32) // sz_cpr must be h->magic + throwCompressedDataViolation(); + } + else { // extra bytes after end? + throwCompressedDataViolation(); + } + + // update header with totals + ph.c_len = total_in; + ph.u_len = total_out; + + // all bytes must be written + if (total_out != orig_file_size) + throw EOFException(); + + // finally test the checksums + if (ph.c_adler != c_adler || ph.u_adler != u_adler) + throwChecksumError(); +#undef MAX_ELF_HDR +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_lx_elf.h b/src/p_lx_elf.h new file mode 100644 index 0000000000..c7dffc0bde --- /dev/null +++ b/src/p_lx_elf.h @@ -0,0 +1,84 @@ +/* p_lx_elf.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + Copyright (C) 2000 John F. Reiser. All rights reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + John F. Reiser + jreiser@BitWagon.com + */ + + +#ifndef __UPX_P_LX_ELF_H //{ +#define __UPX_P_LX_ELF_H + +#include "p_unix.h" +#include "p_elf.h" + +class PackLinuxI386elf : public PackLinuxI386 +{ + typedef PackLinuxI386 super; +public: + PackLinuxI386elf(InputFile *f); + virtual ~PackLinuxI386elf(); + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_LINUX_ELF_i386; } + virtual const char *getName() const { return "linux/elf386"; } + virtual const int *getFilters() const { return NULL; } + + virtual bool canPack(); + virtual bool canUnpackFormat(int format) const; + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canUnpackVersion(int version) const + { return (version >= 11); } + + struct Extent { + off_t offset; + off_t size; + }; + virtual void packExtent(Extent const &x, OutputFile *fo, + unsigned &total_in, unsigned &total_out); + virtual void unpackExtent(unsigned wanted, OutputFile *fo, + unsigned &total_in, unsigned &total_out, + unsigned &c_adler, unsigned &u_adler); + +protected: + virtual const upx_byte *getLoader() const; + virtual int getLoaderSize() const; + + virtual void patchLoader(); + virtual void updateLoader(OutputFile *); + + Elf_LE32_Ehdr ehdri; // from input file + Elf_LE32_Phdr *phdri; // for input file +}; + +#endif //}__UPX_P_LX_ELF_H + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_lx_sep.cpp b/src/p_lx_sep.cpp new file mode 100644 index 0000000000..9461b42722 --- /dev/null +++ b/src/p_lx_sep.cpp @@ -0,0 +1,85 @@ +/* p_lxsep.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + Copyright (C) 2000 John F. Reiser. All rights reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + John F. Reiser + jreiser@BitWagon.com + */ + + +#include "conf.h" + +#include "file.h" +#include "packer.h" +#include "p_lx_sep.h" + + +/************************************************************************* +// +**************************************************************************/ + +const upx_byte *PackLinuxI386sep::getLoader() const +{ + static char script[SCRIPT_MAX + sizeof(l_info)]; + memset(script, 0, sizeof(script)); + char const *name = opt->script_name; + if (0==name) { + name = "/usr/local/lib/upxX"; + } + sprintf(script, "#!%s\n", name); + if (M_IS_NRV2B(opt->method)) { + script[strlen(script)-2] = 'b'; + return (upx_byte const *)script; + } + if (M_IS_NRV2D(opt->method)) { + script[strlen(script)-2] = 'd'; + return (upx_byte const *)script; + } + return NULL; +} + +int PackLinuxI386sep::getLoaderSize() const +{ + if (M_IS_NRV2B(opt->method)) + return SCRIPT_MAX + sizeof(l_info); + if (M_IS_NRV2D(opt->method)) + return SCRIPT_MAX + sizeof(l_info); + return 0; +} + +int PackLinuxI386sep::getLoaderPrefixSize() const +{ + return SCRIPT_MAX; +} + +void PackLinuxI386sep::patchLoader() +{ + patchLoaderChecksum(); +} + +/* +vi:ts=4:et +*/ + diff --git a/src/p_lx_sep.h b/src/p_lx_sep.h new file mode 100644 index 0000000000..e64fafc208 --- /dev/null +++ b/src/p_lx_sep.h @@ -0,0 +1,55 @@ +/* p_lx_sep.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 2000 John F. Reiser + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + John F. Reiser + jreiser@BitWagon.com + */ + +#ifndef __UPX_P_LX_SEP_H // { +#define __UPX_P_LX_SEP_H + +#include "p_lx_elf.h" + +class PackLinuxI386sep : public PackLinuxI386elf +{ + typedef PackLinuxI386elf super; +public: + PackLinuxI386sep(InputFile *f) : super(f) { } + virtual int getFormat() const { return UPX_F_LINUX_SEP_i386; } + virtual const char *getName() const { return "linux/sep386"; } + +protected: + virtual const upx_byte *getLoader() const; + virtual int getLoaderSize() const; + virtual int getLoaderPrefixSize() const; + + virtual void patchLoader(); + virtual void updateLoader(OutputFile *) {} +}; + + +#endif __UPX_P_LX_SEP_H //} +#define __UPX_P_LX_SEP_H + +/* +vi:ts=4:et +*/ + diff --git a/src/p_lx_sh.cpp b/src/p_lx_sh.cpp new file mode 100644 index 0000000000..ffdd2a8148 --- /dev/null +++ b/src/p_lx_sh.cpp @@ -0,0 +1,223 @@ +/* p_lx_sh.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + Copyright (C) 2000 John F. Reiser. All rights reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + John F. Reiser + jreiser@BitWagon.com + */ + + +#include "conf.h" + +#include "file.h" +#include "packer.h" +#include "p_elf.h" +#include "p_unix.h" +#include "p_lx_sh.h" + +static const +#include "stub/l_sh_n2b.h" +static const +#include "stub/l_sh_n2d.h" + +PackLinuxI386sh::~PackLinuxI386sh() +{ +} + +PackLinuxI386sh::PackLinuxI386sh(InputFile *f) + :super(f) + ,o_shname(0) + ,l_shname(0) +{ +} + +const upx_byte *PackLinuxI386sh::getLoader() const +{ + if (M_IS_NRV2B(opt->method)) + return linux_i386sh_nrv2b_loader; + if (M_IS_NRV2D(opt->method)) + return linux_i386sh_nrv2d_loader; + return NULL; +} + +int PackLinuxI386sh::getLoaderSize() const +{ + if (0 != lsize) { + return lsize; + } + if (M_IS_NRV2B(opt->method)) + return sizeof(linux_i386sh_nrv2b_loader); + if (M_IS_NRV2D(opt->method)) + return sizeof(linux_i386sh_nrv2d_loader); + return 0; +} + + +static inline off_t max_off_t(off_t a, off_t b) +{ + return a > b ? a : b; +} + +static off_t getbrk(Elf_LE32_Phdr const *phdr, int e_phnum) +{ + off_t brka = 0; + for (int j = 0; j < e_phnum; ++phdr, ++j) if (PT_LOAD==phdr->p_type) { + brka = max_off_t(brka, phdr->p_vaddr + phdr->p_memsz); + } + return brka; +} + + +void PackLinuxI386sh::patchLoader() +{ + lsize = getLoaderSize(); + ehdri = (Elf_LE32_Ehdr *)(void *)loader; + Elf_LE32_Phdr *const phdri = (Elf_LE32_Phdr *)(1+ehdri); + + patch_le32(loader,lsize,"UPX3",l_shname); + patch_le32(loader,lsize,"UPX2",o_shname); + + // stub/scripts/setfold.pl puts address of 'fold_begin' in phdr[1].p_offset + off_t const fold_begin = phdri[1].p_offset + 0x80; + MemBuffer cprLoader(lsize); + + // compress compiled C-code portion of loader + upx_compress_config_t conf; memset(&conf, 0xff, sizeof(conf)); + conf.c_flags = 0; + upx_uint result_buffer[16]; + size_t cprLsize; + upx_compress( + loader + fold_begin, lsize - fold_begin, + cprLoader, &cprLsize, + 0, // progress_callback_t ?? + getCompressionMethod(), 9, + &conf, + result_buffer + ); + set_le32(0+fold_begin+loader, lsize - fold_begin); + set_le32(4+fold_begin+loader, cprLsize); + memcpy( 8+fold_begin+loader, cprLoader, cprLsize); + lsize = 8 + fold_begin + cprLsize; + patchVersion(loader,lsize); + + unsigned const brka = getbrk(phdri, ehdri->e_phnum); + phdri[1].p_offset = 0xfff&brka; + phdri[1].p_vaddr = brka; + phdri[1].p_paddr = brka; + phdri[1].p_filesz = 0; + phdri[1].p_memsz = 0; + + // The beginning of our loader consists of a elf_hdr (52 bytes) and + // two sections elf_phdr (2 * 32 byte), so we have 12 free bytes + // from offset 116 to the program start at offset 128. + assert(ehdri->e_phoff == sizeof(Elf_LE32_Ehdr)); + assert(ehdri->e_ehsize == sizeof(Elf_LE32_Ehdr)); + assert(ehdri->e_phentsize == sizeof(Elf_LE32_Phdr)); + assert(ehdri->e_phnum == 2); + assert(ehdri->e_shnum == 0); + assert(lsize > 128 && lsize < 4096); + + patchLoaderChecksum(); +} + + +bool PackLinuxI386sh::getShellName(char *buf) +{ + exetype = -1; + l_shname = strcspn(buf, " \t\n\v\f\r"); + buf[l_shname] = 0; + char const *const basename = 1+strrchr(buf, '/'); + static char const *const shname[] = { // known shells that accept "-c" arg + "sh", "ash", "bsh", "csh", "ksh", "bash", "tcsh", "pdksh", + 0 + }; + for (int j=0; 0 != shname[j]; ++j) { + if (0==strcmp(shname[j], basename)) { + o_shname += 3; // space for "-c\x00" + return super::canPack(); + } + } + return false; +} + + +bool PackLinuxI386sh::canUnpackFormat(int format) const +{ + return UPX_F_LINUX_SH_i386==format; +} + +bool PackLinuxI386sh::canPack() +{ +#if defined(__linux__) //{ + // only compress i386sh scripts when running under Linux + char buf[512]; + + fi->readx(buf, 512); + fi->seek(0, SEEK_SET); + buf[511] = 0; + if (!memcmp(buf, "#!/", 3)) { // #!/bin/sh + o_shname = 2; + return getShellName(&buf[o_shname]); + } + else if (!memcmp(buf, "#! /", 4)) { // #! /bin/sh + o_shname = 3; + return getShellName(&buf[o_shname]); + } +#endif //} + return false; +} + + +void PackLinuxI386sh::pack(OutputFile *fo) +{ +#define PAGE_MASK (~0<<12) + opt->unix.blocksize = file_size; + PackUnix::pack(fo); + + // update loader + Elf_LE32_Phdr *const phdro = (Elf_LE32_Phdr *)(sizeof(Elf_LE32_Ehdr)+loader); + off_t const totlen = fo->getBytesWritten(); + phdro[0].p_filesz = totlen; + phdro[0].p_memsz = PAGE_MASK & (~PAGE_MASK + totlen); + + // Try to make brk() work by faking it for exec(). + unsigned const brka = 0x08048000; + phdro[1].p_offset = 0xfff&brka; + phdro[1].p_vaddr = brka; + phdro[1].p_paddr = brka; + phdro[1].p_filesz = 0; + phdro[1].p_memsz = 0; + + patchLoaderChecksum(); + fo->seek(0, SEEK_SET); + fo->rewrite(loader, 0x80); +#undef PAGE_MASK +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_lx_sh.h b/src/p_lx_sh.h new file mode 100644 index 0000000000..bac2314c74 --- /dev/null +++ b/src/p_lx_sh.h @@ -0,0 +1,73 @@ +/* p_lx_sh.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + Copyright (C) 2000 John F. Reiser. All rights reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + John F. Reiser + jreiser@BitWagon.com + */ + + +#ifndef __UPX_P_LX_SH_H //{ +#define __UPX_P_LX_SH_H + +class PackLinuxI386sh : public PackLinuxI386 +{ + typedef PackLinuxI386 super; +public: + PackLinuxI386sh(InputFile *f); + virtual ~PackLinuxI386sh(); + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_LINUX_SH_i386; } + virtual const char *getName() const { return "linux/sh386"; } + virtual const int *getFilters() const { return NULL; } + + virtual bool canPack(); + virtual void pack(OutputFile *fo); + // virtual void unpack(OutputFile *fo) { super::unpack(fo); } + + virtual bool canUnpackFormat(int format) const; + virtual bool canUnpackVersion(int version) const + { return (version >= 11); } + +protected: + virtual const upx_byte *getLoader() const; + virtual int getLoaderSize() const; + virtual bool getShellName(char *buf); + + virtual void patchLoader(); + + Elf_LE32_Ehdr *ehdri; // from input file + + int o_shname; // offset to name_of_shell + int l_shname; // length of name_of_shell +}; + +#endif //}__UPX_P_LX_SH_H + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_sys.cpp b/src/p_sys.cpp new file mode 100644 index 0000000000..fa9b54f629 --- /dev/null +++ b/src/p_sys.cpp @@ -0,0 +1,132 @@ +/* p_sys.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "filter.h" +#include "packer.h" +#include "p_com.h" +#include "p_sys.h" + +static const +#include "stub/l_sys.h" + + +/************************************************************************* +// +**************************************************************************/ + +bool PackSys::canPack() +{ + unsigned char buf[128]; + + fi->readx(buf,128); + if (memcmp (buf,"\xff\xff\xff\xff",4) != 0) + return false; + if (!fn_has_ext(fi->getName(),"sys")) + return false; + if (find_le32(buf,128,UPX_MAGIC_LE32)) + throwAlreadyPacked(); + if (file_size < 1024) + throwCantPack("file is too small"); + if (file_size > 0x10000) + throwCantPack("file is too big for dos/sys"); + return true; +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackSys::patchLoader(OutputFile *fo, + upx_byte *loader, int lsize, + unsigned calls, unsigned overlapoh) +{ + const int filter_id = ph.filter; + const int e_len = getLoaderSection("SYSCUTPO"); + const int d_len = lsize - e_len; + assert(e_len > 0 && e_len < 256); + assert(d_len > 0 && d_len < 256); + + if (ph.u_len + d_len + overlapoh > 0xfffe) + throwNotCompressible(); + + memcpy(loader,ibuf,6); // copy from orig. header + memcpy(loader+8,ibuf+8,2); // opendos wants this word too + + unsigned copy_to = ph.u_len + d_len + overlapoh; + + patch_le16(loader,lsize,"JO",get_le16(ibuf+6)-copy_to-1); + if (filter_id) + { + assert(calls > 0); + patch_le16(loader,lsize,"CT",calls); + } + + unsigned jmp_pos; + jmp_pos = find_le16(loader,e_len,get_le16("JM")) - loader; + patch_le16(loader,e_len,"JM",ph.u_len+overlapoh+2-jmp_pos-2); + loader[getLoaderSection("SYSSUBSI") - 1] = (upx_byte) -e_len; + patch_le16(loader,e_len,"DI",copy_to); + patch_le16(loader,e_len,"SI",ph.c_len+e_len+d_len-1); + + // write loader + compressed file + fo->write(loader,e_len); // entry + fo->write(obuf,ph.c_len); + fo->write(loader+e_len,d_len); // decompressor +} + + +int PackSys::buildLoader(const Filter *ft) +{ + const int filter_id = ft->id; + initLoader(nrv2b_loader,sizeof(nrv2b_loader)); + addLoader("SYSMAIN1", + opt->cpu == opt->CPU_8086 ? "SYSI0861" : "SYSI2861", + "SYSMAIN2""SYSSUBSI", + filter_id ? "SYSCALLT" : "", + "SYSMAIN3""UPX1HEAD""SYSCUTPO""NRV2B160""NRVDDONE""NRVDECO1", + ph.max_offset_found <= 0xd00 ? "NRVLED00" : "NRVGTD00", + "NRVDECO2""NRV2B169", + NULL + ); + if (filter_id) + addFilter16(filter_id); + addLoader("SYSMAIN5", + opt->cpu == opt->CPU_8086 ? "SYSI0862" : "SYSI2862", + "SYSJUMP1", + NULL + ); + return getLoaderSize(); +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_sys.h b/src/p_sys.h new file mode 100644 index 0000000000..0e17aec766 --- /dev/null +++ b/src/p_sys.h @@ -0,0 +1,62 @@ +/* p_sys.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_SYS_H +#define __UPX_P_SYS_H + + +/************************************************************************* +// dos/sys +**************************************************************************/ + +class PackSys : public PackCom +{ + typedef PackCom super; +public: + PackSys(InputFile *f) : super(f) { } + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_DOS_SYS; } + virtual const char *getName() const { return "dos/sys"; } + + virtual bool canPack(); + +protected: + virtual const unsigned getCallTrickOffset() const { return 0; } + +protected: + virtual int buildLoader(const Filter *ft); + virtual void patchLoader(OutputFile *fo, upx_byte *, int, unsigned, unsigned); +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_tmt.cpp b/src/p_tmt.cpp new file mode 100644 index 0000000000..2e9b09fb15 --- /dev/null +++ b/src/p_tmt.cpp @@ -0,0 +1,333 @@ +/* p_tmt.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + +#include "conf.h" +#include "file.h" +#include "filter.h" +#include "packer.h" +#include "p_tmt.h" + +static const +#include "stub/l_tmt.h" + +#define EXTRA_INFO 4 // original entry point + + +/************************************************************************* +// +**************************************************************************/ + +int PackTmt::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_LE32; + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_LE32; + return opt->level > 1 && file_size >= 512*1024 ? M_NRV2D_LE32 : M_NRV2B_LE32; +} + + +const int *PackTmt::getFilters() const +{ + static const int filters[] = { 0x26, 0x24, 0x11, 0x14, 0x13, 0x16, + 0x25, 0x12, 0x15, -1 }; + return filters; +} + + +/************************************************************************* +// util +**************************************************************************/ + +bool PackTmt::readFileHeader() +{ +#define H(x) get_le16(h,2*(x)) +#define H4(x) get_le32(h,x) + upx_byte h[0x40]; + int ic; + unsigned exe_offset = 0; + adam_offset = 0; + + for (ic = 0; ic < 20; ic++) + { + fi->seek(adam_offset,SEEK_SET); + fi->readx(h,sizeof(h)); + + if (memcmp(h,"MZ",2) == 0) // dos exe + { + exe_offset = adam_offset; + adam_offset += H(2)*512+H(1); + if (H(1)) + adam_offset -= 512; + if (H(0x18/2) == 0x40 && H4(0x3c)) + adam_offset = H4(0x3c); + } + else if (memcmp(h,"BW",2) == 0) + adam_offset += H(2)*512+H(1); + else if (memcmp(h,"PMW1",4) == 0) + { + fi->seek(adam_offset + H4(0x18),SEEK_SET); + adam_offset += H4(0x24); + int objs = H4(0x1c); + while (objs--) + { + fi->readx(h,0x18); + adam_offset += H4(4); + } + } + else if (memcmp(h,"LE",2) == 0) + { + // + (memory_pages-1)*memory_page_size+bytes_on_last_page + unsigned offs = exe_offset + (H4(0x14) - 1) * H4(0x28) + H4(0x2c); + fi->seek(adam_offset+0x80,SEEK_SET); + fi->readx(h,4); + // + data_pages_offset + adam_offset = offs + H4(0); + } + else if (memcmp(h,"Adam",4) == 0) + break; + else + return false; + } + if (ic == 20) + return false; + fi->seek(adam_offset,SEEK_SET); + fi->readx(&ih,sizeof(ih)); + + return true; +#undef H4 +#undef H +} + + +bool PackTmt::canPack() +{ + return PackTmt::readFileHeader(); +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackTmt::pack(OutputFile *fo) +{ + Packer::handleStub(fi,fo,adam_offset); + + const unsigned usize = ih.imagesize; + const unsigned rsize = ih.relocsize; + + ibuf.alloc(usize+rsize+128); + obuf.allocForCompression(usize+rsize+128); + + MemBuffer wrkmem; + wrkmem.alloc(rsize+EXTRA_INFO); // relocations + + fi->seek(adam_offset+sizeof(ih),SEEK_SET); + fi->readx(ibuf,usize); + fi->readx(wrkmem+4,rsize); + const unsigned overlay = file_size - fi->tell(); + + if (find_le32(ibuf,128,get_le32("UPX "))) + throwAlreadyPacked(); + if (rsize == 0) + throwCantPack("file is already compressed with another packer"); + + checkOverlay(overlay); + + unsigned relocsize = 0; + int big; + //if (rsize) + { + for (unsigned ic=4; ic<=rsize; ic+=4) + set_le32(wrkmem+ic,get_le32(wrkmem+ic)-4); + relocsize = optimizeReloc32(wrkmem+4,rsize/4,wrkmem,ibuf,1,&big)-wrkmem; + } + + // filter + Filter ft(opt->level); + tryFilters(&ft, ibuf, usize); + + wrkmem[relocsize++] = 0; + set_le32(wrkmem+relocsize,ih.entry); // save original entry point + relocsize += 4; + set_le32(wrkmem+relocsize,relocsize+4); + relocsize += 4; + memcpy(ibuf+usize,wrkmem,relocsize); + + ph.filter = ft.id; + ph.filter_cto = ft.cto; + ph.u_len = usize+relocsize; + if (!compress(ibuf,obuf)) + throwNotCompressible(); + // make sure the decompressor will be paragraph aligned + const unsigned overlapoh = ((findOverlapOverhead(obuf,512)+0x20) + &~ 0xf) - (ph.u_len & 0xf); + + // verify filter + ft.verifyUnfilter(); + + // prepare loader + initLoader(nrv_loader,sizeof(nrv_loader)); + addLoader("IDENTSTR""TMTMAIN1", + ft.id ? "TMTCALT1" : "", + "TMTMAIN2""UPX1HEAD""TMTCUTPO""+0XXXXXX", + getDecompressor(), + "TMTMAIN5", + NULL + ); + if (ft.id) + { + assert(ft.calls > 0); + addLoader("TMTCALT2",NULL); + addFilter32(ft.id); + } + addLoader("TMTRELOC""RELOC320", + big ? "REL32BIG" : "", + "RELOC32J""TMTJUMP1", + NULL + ); + + const unsigned lsize = getLoaderSize(); + MemBuffer loader(lsize); + memcpy(loader,getLoader(),lsize); + + const unsigned s_point = getLoaderSection("TMTMAIN1"); + int e_len = getLoaderSection("TMTCUTPO"); + const unsigned d_len = lsize - e_len; + assert(e_len > 0 && s_point > 0); + + // patch loader + patch_le32(loader,lsize,"JMPO",ih.entry-(ph.u_len+overlapoh+d_len)); + + if (ft.id) + { + assert(ft.calls > 0); + if (ft.id > 0x20) + patch_le16(loader,lsize,"??",'?'+(ph.filter_cto << 8)); + patch_le32(loader,lsize,"TEXL",(ft.id & 0xf) % 3 == 0 ? ft.calls : + ft.lastcall - ft.calls * 4); + } + unsigned jmp_pos; + jmp_pos = find_le32(loader,e_len,get_le32("JMPD")) - loader; + patch_le32(loader,e_len,"JMPD",ph.u_len+overlapoh-jmp_pos-4); + + patch_le32(loader,e_len,"ECX0",ph.c_len+d_len); + patch_le32(loader,e_len,"EDI0",ph.u_len+overlapoh+d_len-1); + patch_le32(loader,e_len,"ESI0",ph.c_len+e_len+d_len-1); + putPackHeader(loader,e_len); + //fprintf(stderr,"\nelen=%x dlen=%x copy_len=%x copy_to=%x oo=%x jmp_pos=%x ulen=%x clen=%x \n\n", + // e_len,d_len,copy_len,copy_to,overlapoh,jmp_pos,ph.u_len,ph.c_len); + + memcpy(&oh,&ih,sizeof(oh)); + oh.imagesize = ph.c_len+e_len+d_len; // new size + oh.entry = s_point; // new entry point + oh.relocsize = 4; + + // write loader + compressed file + fo->write(&oh,sizeof(oh)); + fo->write(loader,e_len); + fo->write(obuf,ph.c_len); + fo->write(loader+lsize-d_len,d_len); // decompressor + char rel_entry[4]; + set_le32(rel_entry,5 + s_point); + fo->write(rel_entry,sizeof (rel_entry)); + + // verify + verifyOverlappingDecompression(&obuf, overlapoh); + + // copy the overlay + copyOverlay(fo, overlay, &obuf); +} + + +bool PackTmt::canUnpack() +{ + if (!PackTmt::readFileHeader()) + return false; + return readPackHeader(512,adam_offset); +} + + +void PackTmt::unpack(OutputFile *fo) +{ + Packer::handleStub(fi,fo,adam_offset); + + ibuf.alloc(ph.c_len); + obuf.allocForUncompression(ph.u_len); + + fi->seek(adam_offset + ph.buf_offset + ph.getPackHeaderSize(),SEEK_SET); + fi->readx(ibuf,ph.c_len); + + // decompress + decompress(ibuf,obuf); + + // decode relocations + const unsigned osize = ph.u_len - get_le32(obuf+ph.u_len-4); + upx_byte *relocs = obuf + osize; + const unsigned origstart = get_le32(obuf+ph.u_len-8); + + // unfilter + if (ph.filter) + { + Filter ft(ph.level); + ft.init(ph.filter, 0); + ft.cto = (unsigned char) (ph.version < 11 ? (get_le32(obuf+ph.u_len-12) >> 24) : ph.filter_cto); + ft.unfilter(obuf, relocs-obuf); + } + + // decode relocations + MemBuffer wrkmem; + unsigned relocn = unoptimizeReloc32(&relocs,obuf,&wrkmem,1); + for (unsigned ic = 0; ic < relocn; ic++) + set_le32(wrkmem+ic*4,get_le32(wrkmem+ic*4)+4); + + memcpy(&oh,&ih,sizeof(oh)); + oh.imagesize = osize; + oh.entry = origstart; + oh.relocsize = relocn*4; + + const unsigned overlay = file_size - adam_offset - ih.imagesize + - ih.relocsize - sizeof(ih); + checkOverlay(overlay); + + // write decompressed file + if (fo) + { + fo->write(&oh,sizeof(oh)); + fo->write(obuf,osize); + fo->write(wrkmem,relocn*4); + } + + // copy the overlay + copyOverlay(fo, overlay, &obuf); +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_tmt.h b/src/p_tmt.h new file mode 100644 index 0000000000..c9bfaedf80 --- /dev/null +++ b/src/p_tmt.h @@ -0,0 +1,76 @@ +/* p_tmt.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_TMT_H +#define __UPX_P_TMT_H + + +/************************************************************************* +// tmt/adam +**************************************************************************/ + +class PackTmt : public Packer +{ + typedef Packer super; +public: + PackTmt(InputFile *f) : super(f) { assert(sizeof(tmt_header_t) == 44); } + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_TMT_ADAM; } + virtual const char *getName() const { return "tmt/adam"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + +protected: + bool readFileHeader(); + + unsigned adam_offset; + + struct tmt_header_t + { + char _[16]; // signature,linkerversion,minversion,exesize,imagestart + LE32 imagesize; + char __[4]; // initial memory + LE32 entry; + char ___[12]; // esp,numfixups,flags + LE32 relocsize; + } ih,oh; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_tos.cpp b/src/p_tos.cpp new file mode 100644 index 0000000000..8f16c1a609 --- /dev/null +++ b/src/p_tos.cpp @@ -0,0 +1,544 @@ +/* p_tos.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#include "file.h" +#include "packer.h" +#include "p_tos.h" + +static const +#include "stub/l_t_n2b.h" +static const +#include "stub/l_t_n2bs.h" +static const +#include "stub/l_t_n2d.h" +static const +#include "stub/l_t_n2ds.h" + +// #define TESTING + + +/************************************************************************* +// +**************************************************************************/ + +#define FH_SIZE sizeof(tos_header_t) + +PackTos::PackTos(InputFile *f) : + super(f) +{ + assert(FH_SIZE == 28); +} + + +int PackTos::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_8; + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_8; + return opt->level > 1 && file_size >= 512*1024 ? M_NRV2D_8 : M_NRV2B_8; +} + + +const int *PackTos::getFilters() const +{ + return NULL; +} + + +const upx_byte *PackTos::getLoader() const +{ + if (M_IS_NRV2B(opt->method)) + return opt->small ? nrv2b_loader_small : nrv2b_loader; + if (M_IS_NRV2D(opt->method)) + return opt->small ? nrv2d_loader_small : nrv2d_loader; + return NULL; +} + + +int PackTos::getLoaderSize() const +{ + if (M_IS_NRV2B(opt->method)) + return opt->small ? sizeof(nrv2b_loader_small) : sizeof(nrv2b_loader); + if (M_IS_NRV2D(opt->method)) + return opt->small ? sizeof(nrv2d_loader_small) : sizeof(nrv2d_loader); + return 0; +} + + +/************************************************************************* +// +**************************************************************************/ + +/* flags for curproc->memflags */ +/* also used for program headers fh_flag */ +#define F_FASTLOAD 0x01 // don't zero heap +#define F_ALTLOAD 0x02 // OK to load in alternate ram +#define F_ALTALLOC 0x04 // OK to malloc from alt. ram +#define F_RESERVED 0x08 // reserved for future use +#define F_MEMFLAGS 0xf0 // reserved for future use +#define F_SHTEXT 0x800 // program's text may be shared + +#define F_MINALT 0xf0000000 // used to decide which type of RAM to load in + +/* Bit in Mxalloc's arg for "don't auto-free this memory" */ +#define F_KEEP 0x4000 + +#define F_OS_SPECIAL 0x8000 // mark as a special process + +/* flags for curproc->memflags (that is, fh_flag) and also Mxalloc mode. */ +/* (Actually, when users call Mxalloc, they add 0x10 to what you see here) */ +#define F_PROTMODE 0xf0 // protection mode bits +#define F_PROT_P 0x00 // no read or write +#define F_PROT_G 0x10 // any access OK +#define F_PROT_S 0x20 // any super access OK +#define F_PROT_PR 0x30 // any read OK, no write +#define F_PROT_I 0x40 // invalid page + + +/************************************************************************* +// util +**************************************************************************/ + +bool PackTos::readExeHeader() +{ + fi->seek(0,SEEK_SET); + fi->readx(&ih, FH_SIZE); + if (ih.fh_magic != 0x601a) + return false; + if (FH_SIZE + ih.fh_text + ih.fh_data + ih.fh_sym > (unsigned) file_size) + return false; + return true; +} + + +bool PackTos::checkExeHeader() +{ + const unsigned f = ih.fh_flag; + //printf("flags: 0x%x, text: %d, data: %d, bss: %d, sym: %d\n", f, (int)ih.fh_text, (int)ih.fh_data, (int)ih.fh_bss, (int)ih.fh_sym); + if ((ih.fh_text & 1) || (ih.fh_data & 1)) + throwCantPack("odd size values in text/data"); + if (f & F_OS_SPECIAL) + throwCantPack("I won't pack F_OS_SPECIAL programs"); + if ((f & F_PROTMODE) > F_PROT_I) + throwCantPack("invalid protection mode"); + if (ih.fh_reserved != 0) + { + if (opt->force < 1) + throwCantPack("reserved header field set; use option `-f' to force packing"); + } + if ((f & F_PROTMODE) != F_PROT_P) + { + if (opt->force < 1) + throwCantPack("no private memory protection; use option `-f' to force packing"); + } + if (f & F_SHTEXT) + { + if (opt->force < 1) + throwCantPack("shared text segment; use option `-f' to force packing"); + } + return true; +} + + +void PackTos::patch_d0_subq(void *l, int llen, + const char *d0_old, const char *subq_old, + unsigned d0_new) +{ + void *p; + // patch "subq.l #1,d0" or "subq.w #1,d0" + p = find_be16(l, llen, get_be16(subq_old)); + checkPatch(l, p, 2); + set_be16(p, d0_new > 65535 ? 0x5380 : 0x5340); + // + p = find_be32(l, llen, get_be32(d0_old)); + checkPatch(l, p, 4); + set_be32(p, d0_new); + assert(get_be16(p, -2) == 0x203c); // move.l #XXXXXXXX,d0 +} + + +/************************************************************************* +// relocs +**************************************************************************/ + +// Check relocation for errors to make sure our loader can handle it. +static int check_relocs(const upx_byte *relocs, unsigned rsize, unsigned isize, + unsigned *relocsize, unsigned *overlay) +{ + unsigned fixup = get_be32(relocs); + unsigned last_fixup = fixup; + unsigned i = 4; + + assert(isize >= 4); + assert(fixup > 0); + + for (;;) + { + if (fixup & 1) // must be word-aligned + return -1; + if (fixup + 4 > isize) // too far + return -1; + if (i >= rsize) // premature EOF in relocs + return -1; + int c = relocs[i++]; + if (c == 0) // end marker + break; + else if (c == 1) // increase fixup, no reloc + fixup += 254; + else if (c & 1) // must be word-aligned + return -1; + else // next reloc is here + { + fixup += c; + if (fixup - last_fixup < 4) // overlapping relocation + return -1; + last_fixup = fixup; + } + } + + *relocsize = i; + *overlay = rsize - i; + return 0; +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackTos::canPack() +{ + if (!readExeHeader()) + return false; + + unsigned char buf[512]; + fi->readx(buf,sizeof(buf)); + if (find_le32(buf,sizeof(buf),UPX_MAGIC_LE32)) + throwAlreadyPacked(); + + if (!checkExeHeader()) + throwCantPack("unsupported header flags"); + if (file_size < 256) + throwCantPack("program too small"); + return true; +} + + +void PackTos::fileInfo() +{ + if (!readExeHeader()) + return; + con_fprintf(stdout, " text: %d, data: %d, sym: %d, bss: %d, flags=0x%x\n", + (int)ih.fh_text, (int)ih.fh_data, (int)ih.fh_sym, (int)ih.fh_bss, (int)ih.fh_flag); +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackTos::pack(OutputFile *fo) +{ + unsigned t; + unsigned relocsize = 0; + unsigned overlay = 0; + + const unsigned lsize = getLoaderSize(); + const unsigned e_len = get_be16(getLoader()+lsize-4); + const unsigned d_len = get_be16(getLoader()+lsize-2); + assert(e_len + d_len == lsize - 4); + assert((e_len & 3) == 0 && (d_len & 1) == 0); + + const unsigned i_text = ih.fh_text; + const unsigned i_data = ih.fh_data; + const unsigned i_sym = ih.fh_sym; + const unsigned i_bss = ih.fh_bss; + + // read file + const unsigned isize = file_size - i_sym; + ibuf.alloc(isize); + fi->seek(FH_SIZE, SEEK_SET); + // read text + data + t = i_text + i_data; + fi->readx(ibuf,t); + // skip symbols + fi->seek(i_sym,SEEK_CUR); + // read relocations + overlay + overlay = file_size - (FH_SIZE + i_text + i_data + i_sym); + fi->readx(ibuf+t,overlay); + +#if 0 || defined(TESTING) + printf("text: %d, data: %d, sym: %d, bss: %d, flags=0x%x\n", + i_text, i_data, i_sym, i_bss, (int)fh_flag); + printf("xx1 reloc: %d, overlay: %d, fixup: %d\n", relocsize, overlay, overlay >= 4 ? (int)get_be32(ibuf+t) : -1); +#endif + + // Check relocs (see load_and_reloc() in mint/src/mem.c). + // Must work around TOS bugs and lots of broken programs. + int r = 0; + if (overlay < 4) + { + // Bug workaround: Whatever this is, silently keep it in + // the (unused) relocations for byte-identical unpacking. + relocsize = overlay; + overlay = 0; + } + else if (get_be32(ibuf+t) == 0) + { + // Bug workaround - check the empty fixup before testing fh_reloc. + relocsize = 4; + overlay -= 4; + } + else if (ih.fh_reloc != 0) + relocsize = 0; + else + r = check_relocs(ibuf+t, overlay, t, &relocsize, &overlay); + +#if 0 || defined(TESTING) + printf("xx2 reloc: %d, overlay: %d, t: %d\n", relocsize, overlay, t); +#endif + + if (r != 0) + throwCantPack("bad relocation table"); + checkOverlay(overlay); + + // Append original fileheader. + t += relocsize; + ih.fh_sym = 0; // we stripped all symbols + memcpy(ibuf+t, &ih, FH_SIZE); + t += FH_SIZE; +#if 0 || defined(TESTING) + printf("xx3 reloc: %d, overlay: %d, t: %d\n", relocsize, overlay, t); +#endif + assert(t <= isize); + + // Now the data in ibuf[0..t] looks like this: + // text + data + relocs + original file header + // After compression this will become the first part of the + // data segement. The second part will be the decompressor. + + // alloc buffer + obuf.allocForCompression(t + d_len + 512); + + // compress (max_match = 65535) + ph.u_len = t; + if (!compress(ibuf,obuf,0,65535)) + throwNotCompressible(); + + // The decompressed data will now get placed at this offset: + const unsigned overlapoh = findOverlapOverhead(obuf, 512); + unsigned offset = (ph.u_len + overlapoh) - ph.c_len; + + // compute addresses + unsigned o_text, o_data, o_bss; + o_text = e_len; + o_data = ph.c_len; + o_bss = i_bss; + + // word align len of compressed data + while (o_data & 1) + { + obuf[o_data++] = 0; + offset++; + } + + // append decompressor (part 2 of loader) + const unsigned d_off = o_data; + memcpy(obuf+d_off, getLoader()+e_len, d_len); + o_data += d_len; + + // dword align the len of the final data segment + while (o_data & 3) + { + obuf[o_data++] = 0; + offset++; + } + // dword align offset + while (offset & 3) + offset++; + + // new bss + if (i_text + i_data + i_bss > o_text + o_data + o_bss) + o_bss = (i_text + i_data + i_bss) - (o_text + o_data); + + // dirty bss + unsigned dirty_bss = (o_data + offset) - (i_text + i_data); + //printf("real dirty_bss: %d\n", dirty_bss); + // dword align (or 16 - for speedup when clearing the dirty bss) + const unsigned dirty_bss_align = opt->small ? 4 : 16; + while (dirty_bss & (dirty_bss_align - 1)) + dirty_bss++; + // adjust bss, assert room for some stack + if (dirty_bss + 256 > o_bss) + o_bss = dirty_bss + 256; + + // dword align the len of the final bss segment + while (o_bss & 3) + o_bss++; + + // prepare loader + MemBuffer loader(o_text); + memcpy(loader,getLoader(),o_text); + + // patch loader + // patch "subq.l #1,d0" or "subq.w #1,d0" - see "up41" below + if (!opt->small) + patchVersion(loader,o_text); + patch_be16(loader,o_text,"u4", + dirty_bss / dirty_bss_align > 65535 ? 0x5380 : 0x5340); + patch_be32(loader,o_text,"up31",d_off + offset); + if (opt->small) + patch_d0_subq(loader,o_text,"up22","u1", o_data/4); + else + { + if (o_data <= 160) + throwNotCompressible(); + unsigned loop1 = o_data / 160; + unsigned loop2 = o_data % 160; + if (loop2 == 0) + { + loop1--; + loop2 = 160; + } + patch_be16(loader,o_text,"u2", 0x7000 + loop2/4-1); // moveq.l #X,d0 + patch_d0_subq(loader,o_text,"up22","u1", loop1); + } + patch_be32(loader,o_text,"up21",o_data + offset); + patch_be32(loader,o_text,"up13",i_bss); // p_blen + patch_be32(loader,o_text,"up12",i_data); // p_dlen + patch_be32(loader,o_text,"up11",i_text); // p_tlen + + putPackHeader(loader,o_text); + + // patch decompressor + upx_byte *p = obuf + d_off; + patch_be32(p,d_len,"up41", dirty_bss / dirty_bss_align); + patch_be16(p,d_len,"u3", 0x7600 + (relocsize > 4)); // moveq.l #X,d3 + + // set new file_hdr + memcpy(&oh, &ih, FH_SIZE); + if (opt->tos.split_segments) + { + oh.fh_text = o_text; + oh.fh_data = o_data; + } + else + { + // put everything into the text segment + oh.fh_text = o_text + o_data; + oh.fh_data = 0; + } + oh.fh_bss = o_bss; + oh.fh_sym = 0; + oh.fh_reserved = 0; + // only keep the following flags: + oh.fh_flag = ih.fh_flag & (F_FASTLOAD | F_ALTALLOC | F_KEEP); + // add an empty relocation fixup to workaround a bug in some TOS versions + oh.fh_reloc = 0; + +#if 0 || defined(TESTING) + printf("old text: %6d, data: %6d, bss: %6d, reloc: %d, overlay: %d\n", + i_text, i_data, i_bss, relocsize, overlay); + printf("new text: %6d, data: %6d, bss: %6d, dirty_bss: %d, flag=0x%x\n", + o_text, o_data, o_bss, dirty_bss, (int)fh_flag); +#endif + + // write new file header, loader and compressed file + fo->write(&oh, FH_SIZE); + fo->write(loader, o_text); // entry + fo->write(obuf, o_data); // compressed + decompressor + + // write empty relocation fixup + fo->write("\x00\x00\x00\x00",4); + + // verify + verifyOverlappingDecompression(&obuf, overlapoh); + + // copy the overlay + copyOverlay(fo, overlay, &obuf); +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackTos::canUnpack() +{ + if (!readPackHeader(512, 0)) + return false; + if (!readExeHeader()) + return false; + // check header as set by packer + if ((ih.fh_text & 3) != 0 || (ih.fh_data & 3) != 0 || (ih.fh_bss & 3) != 0 + || ih.fh_sym != 0 || ih.fh_reserved != 0 || ih.fh_reloc > 1) + throwCantUnpack("file damaged"); + if (!checkExeHeader()) + throwCantUnpack("unsupported header flags"); + return true; +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackTos::unpack(OutputFile *fo) +{ + ibuf.alloc(ph.c_len); + obuf.allocForUncompression(ph.u_len); + + fi->seek(ph.buf_offset + ph.getPackHeaderSize(),SEEK_SET); + fi->readx(ibuf,ph.c_len); + + // decompress + decompress(ibuf,obuf); + + // write original header & decompressed file + if (fo) + { + unsigned overlay = file_size - (FH_SIZE + ih.fh_text + ih.fh_data); + if (ih.fh_reloc == 0 && overlay >= 4) + overlay -= 4; // this is our empty fixup + checkOverlay(overlay); + + fo->write(obuf+ph.u_len-FH_SIZE, FH_SIZE); // orig. file_hdr + fo->write(obuf, ph.u_len-FH_SIZE); // orig. text+data+relocs + + // copy any overlay + copyOverlay(fo, overlay, &obuf); + } +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_tos.h b/src/p_tos.h new file mode 100644 index 0000000000..23717df820 --- /dev/null +++ b/src/p_tos.h @@ -0,0 +1,85 @@ +/* p_tos.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_TOS_H +#define __UPX_P_TOS_H + + +/************************************************************************* +// atari/tos +**************************************************************************/ + +class PackTos : public Packer +{ + typedef Packer super; +public: + PackTos(InputFile *f); + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_ATARI_TOS; } + virtual const char *getName() const { return "atari/tos"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + + virtual void fileInfo(); + +protected: + virtual const upx_byte *getLoader() const; + virtual int getLoaderSize() const; + + bool readExeHeader(); + bool checkExeHeader(); + + struct tos_header_t + { + BE16 fh_magic; + BE32 fh_text; + BE32 fh_data; + BE32 fh_bss; + BE32 fh_sym; + BE32 fh_reserved; + BE32 fh_flag; + BE16 fh_reloc; + } ih, oh; + +protected: + void patch_d0_subq(void *l, int llen, const char*, const char*, unsigned); +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_unix.cpp b/src/p_unix.cpp new file mode 100644 index 0000000000..181539d3db --- /dev/null +++ b/src/p_unix.cpp @@ -0,0 +1,460 @@ +/* p_unix.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#include "file.h" +#include "packer.h" +#include "p_unix.h" +#include "p_elf.h" + +// do not change +#define BLOCKSIZE (512*1024) + + +/************************************************************************* +// +**************************************************************************/ + +PackUnix::PackUnix(InputFile *f) : + super(f), exetype(0), blocksize(0), overlay_offset(0), lsize(0) +{ + assert(sizeof(Elf_LE32_Ehdr) == 52); + assert(sizeof(Elf_LE32_Phdr) == 32); +} + + +// common part of canPack(), enhanced by subclasses +bool PackUnix::canPack() +{ + if (exetype == 0) + return false; + +#if defined(__unix__) + // must be executable by owner + if ((fi->st.st_mode & S_IXUSR) == 0) + throwCantPack("file not executable; try `chmod +x'"); +#endif + if (file_size < 4096) + throwCantPack("file is too small"); + + // info: currently the header is 36 (32+4) bytes before EOF + unsigned char buf[256]; + fi->seek(-(long)sizeof(buf), SEEK_END); + fi->readx(buf,sizeof(buf)); + if (find_le32(buf,sizeof(buf),UPX_MAGIC_LE32)) // note: always le32 + throwAlreadyPacked(); + + return true; +} + + +/************************************************************************* +// Generic Unix pack(). Subclasses must provide patchLoader(). +// +// A typical compressed Unix executable looks like this: +// - loader stub +// - 12 bytes header info +// - the compressed blocks, each with a 8 byte header for block sizes +// - 4 bytes block end marker (uncompressed size 0) +// - 32 bytes UPX packheader +// - 4 bytes overlay offset (needed for decompression) +**************************************************************************/ + +// see note below and Packer::compress() +bool PackUnix::checkCompressionRatio(unsigned, unsigned) const +{ + return true; +} + + +void PackUnix::pack(OutputFile *fo) +{ + // set options + blocksize = opt->unix.blocksize; + if (blocksize <= 0) + blocksize = BLOCKSIZE; + if ((off_t)blocksize > file_size) + blocksize = file_size; + // create a pseudo-unique program id for our paranoid stub + progid = getRandomId(); + + // prepare loader + lsize = getLoaderSize(); + loader.alloc(lsize + sizeof(p_info)); + memcpy(loader,getLoader(),lsize); + + // patch loader, prepare header info + patchLoader(); // can change lsize by packing C-code of upx_main etc. + p_info *const hbuf = (p_info *)(loader + lsize); + set_native32(&hbuf->p_progid, progid); + set_native32(&hbuf->p_filesize, file_size); + set_native32(&hbuf->p_blocksize, blocksize); + fo->write(loader, lsize + sizeof(p_info)); + + // init compression buffers + ibuf.alloc(blocksize); + obuf.allocForCompression(blocksize); + + // compress blocks + unsigned total_in = 0; + unsigned total_out = 0; + this->total_passes = (file_size + blocksize - 1) / blocksize; + if (this->total_passes == 1) + this->total_passes = 0; + fi->seek(0, SEEK_SET); + for (;;) + { + int l = fi->read(ibuf, blocksize); + if (l == 0) + break; + + // Note: compression for a block can fail if the + // file is e.g. blocksize + 1 bytes long + + // compress + ph.u_len = l; + (void) compress(ibuf, obuf); // ignore return value + + if (ph.c_len < ph.u_len) + { + if (!testOverlappingDecompression(obuf, OVERHEAD)) + throwNotCompressible(); + } + else + { + // block is not compressible + ph.c_len = ph.u_len; + // must manually update checksum of compressed data + ph.c_adler = upx_adler32(ph.c_adler, ibuf, ph.u_len); + } + + // write block sizes + unsigned char size[8]; + set_native32(size+0, ph.u_len); + set_native32(size+4, ph.c_len); + fo->write(size, 8); + + // write compressed data + if (ph.c_len < ph.u_len) + { + fo->write(obuf, ph.c_len); + verifyOverlappingDecompression(&obuf, OVERHEAD); + } + else + fo->write(ibuf, ph.u_len); + + total_in += ph.u_len; + total_out += ph.c_len; + } + if ((off_t)total_in != file_size) + throw EOFException(); + + // write block end marker (uncompressed size 0) + fo->write("\x00\x00\x00\x00", 4); + + // update header with totals + ph.u_len = total_in; + ph.c_len = total_out; + + // write packheader + const int hsize = ph.getPackHeaderSize(); + set_le32(obuf, ph.magic); // note: always le32 + putPackHeader(obuf, hsize); + fo->write(obuf, hsize); + + // write overlay offset (needed for decompression) + set_native32(obuf, lsize); + fo->write(obuf, 4); + + // finally check compression ratio + if (!Packer::checkCompressionRatio(fo->getBytesWritten(), ph.u_len)) + throwNotCompressible(); +} + + +/************************************************************************* +// Generic Unix canUnpack(). +**************************************************************************/ + +bool PackUnix::canUnpack() +{ + upx_byte buf[128]; + const int bufsize = sizeof(buf); + + fi->seek(-bufsize, SEEK_END); + if (!readPackHeader(128, -1, buf)) + return false; + + int l = ph.buf_offset + ph.getPackHeaderSize(); + if (l < 0 || l + 4 > bufsize) + throwCantUnpack("file corrupted"); + overlay_offset = get_native32(buf+l); + if ((off_t)overlay_offset >= file_size) + throwCantUnpack("file corrupted"); + + return true; +} + + +/************************************************************************* +// Generic Unix unpack(). +// +// This code looks much like the one in stub/l_linux.c +// See notes there. +**************************************************************************/ + +void PackUnix::unpack(OutputFile *fo) +{ + unsigned c_adler = upx_adler32(0, NULL, 0); + unsigned u_adler = upx_adler32(0, NULL, 0); + + // defaults for ph.version == 8 + unsigned orig_file_size = 0; + blocksize = 512 * 1024; + + fi->seek(overlay_offset, SEEK_SET); + if (ph.version > 8) + { + p_info hbuf; + fi->readx(&hbuf, sizeof(hbuf)); + orig_file_size = get_native32(&hbuf.p_filesize); + blocksize = get_native32(&hbuf.p_blocksize); + + if (file_size > (off_t)orig_file_size || blocksize > orig_file_size) + throwCantUnpack("file header corrupted"); + } + else + { + // skip 4 bytes (program id) + fi->seek(4, SEEK_CUR); + } + + ibuf.alloc(blocksize + OVERHEAD); + + // decompress blocks + unsigned total_in = 0; + unsigned total_out = 0; + for (;;) + { +#define buf ibuf + int i; + int size[2]; + + fi->readx(buf, 8); + ph.u_len = size[0] = get_native32(buf+0); + ph.c_len = size[1] = get_native32(buf+4); + + if (size[0] == 0) // uncompressed size 0 -> EOF + { + // note: must reload size[1] as magic is always stored le32 + size[1] = get_le32(buf+4); + if (size[1] != UPX_MAGIC_LE32) // size[1] must be h->magic + throwCompressedDataViolation(); + break; + } + if (size[0] <= 0 || size[1] <= 0) + throwCompressedDataViolation(); + if (size[1] > size[0] || size[0] > (int)blocksize) + throwCompressedDataViolation(); + + i = blocksize + OVERHEAD - size[1]; + fi->readx(buf+i, size[1]); + // update checksum of compressed data + c_adler = upx_adler32(c_adler, buf + i, size[1]); + // decompress + if (size[1] < size[0]) + { + decompress(buf+i, buf, false); + i = 0; + } + // update checksum of uncompressed data + u_adler = upx_adler32(u_adler, buf + i, size[0]); + total_in += size[1]; + total_out += size[0]; + // write block + if (fo) + fo->write(buf + i, size[0]); +#undef buf + } + + // update header with totals + ph.c_len = total_in; + ph.u_len = total_out; + + // all bytes must be written + if (ph.version > 8 && total_out != orig_file_size) + throw EOFException(); + + // finally test the checksums + if (ph.c_adler != c_adler || ph.u_adler != u_adler) + throwChecksumError(); +} + + +/************************************************************************* +// Linux/i386 specific (execve format) +**************************************************************************/ + +static const +#include "stub/l_lx_n2b.h" +static const +#include "stub/l_lx_n2d.h" + + +int PackLinuxI386::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_LE32; + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_LE32; + return opt->level > 1 && file_size >= 512*1024 ? M_NRV2D_LE32 : M_NRV2B_LE32; +} + + +const upx_byte *PackLinuxI386::getLoader() const +{ + if (M_IS_NRV2B(opt->method)) + return linux_i386exec_nrv2b_loader; + if (M_IS_NRV2D(opt->method)) + return linux_i386exec_nrv2d_loader; + return NULL; +} + +int PackLinuxI386::getLoaderSize() const +{ + if (0!=lsize) { + return lsize; + } + if (M_IS_NRV2B(opt->method)) + return sizeof(linux_i386exec_nrv2b_loader); + if (M_IS_NRV2D(opt->method)) + return sizeof(linux_i386exec_nrv2d_loader); + return 0; +} + +int PackLinuxI386::getLoaderPrefixSize() const +{ + return 116; +} + +bool PackLinuxI386::canPack() +{ + Elf_LE32_Ehdr ehdr; + unsigned char *buf = ehdr.e_ident; + + fi->readx(&ehdr, sizeof(ehdr)); + fi->seek(0, SEEK_SET); + + exetype = 0; + const unsigned l = get_le32(buf); + if (!memcmp(buf, "\x7f\x45\x4c\x46\x01\x01\x01", 7)) // ELF 32-bit LSB + { + exetype = 1; + // now check the ELF header + if (!memcmp(buf+8, "FreeBSD", 7)) // branded + exetype = 0; + if (ehdr.e_type != 2) // executable + exetype = 0; + if (ehdr.e_machine != 3 && ehdr.e_machine != 6) // Intel 80[34]86 + exetype = 0; + if (ehdr.e_version != 1) // version + exetype = 0; + } + else if (l == 0x00640107 || l == 0x00640108 || l == 0x0064010b || l == 0x006400cc) + { + // OMAGIC / NMAGIC / ZMAGIC / QMAGIC + exetype = 2; + // FIXME: N_TRSIZE, N_DRSIZE + // FIXME: check for aout shared libraries + } +#if defined(__linux__) + // only compress scripts when running under Linux + else if (!memcmp(buf, "#!/", 3)) // #!/bin/sh + exetype = -1; + else if (!memcmp(buf, "#! /", 4)) // #! /bin/sh + exetype = -1; + else if (!memcmp(buf, "\xca\xfe\xba\xbe", 4)) // Java bytecode + exetype = -2; +#endif + + return super::canPack(); +} + + +void PackLinuxI386::patchLoader() +{ + lsize = getLoaderSize(); + + // mmapsize is (blocksize + OVERHEAD) rounded up to next PAGE_SIZE + const unsigned pagesize = 4096; + const unsigned mmapsize = ALIGN_UP(blocksize + OVERHEAD, pagesize); + + // patch loader + // note: we only can use /proc//fd when exetype > 0. + // also, we sleep much longer when compressing a script. + patch_le32(loader,lsize,"UPX5",mmapsize); + patch_le32(loader,lsize,"UPX4",exetype > 0 ? 3 : 15); // sleep time + patch_le32(loader,lsize,"UPX3",exetype > 0 ? 0 : 0x7fffffff); + patch_le32(loader,lsize,"UPX2",progid); + patch_le32(loader,lsize,"UPX1",lsize); + patchVersion(loader,lsize); + + // The beginning of our loader consists of a elf_hdr (52 bytes) and + // two sections elf_phdr (2 * 32 byte), so we have 12 free bytes + // from offset 116 to the program start at offset 128. + assert(get_le32(loader + 28) == 52); // e_phoff + assert(get_le32(loader + 32) == 0); // e_shoff + assert(get_le16(loader + 40) == 52); // e_ehsize + assert(get_le16(loader + 42) == 32); // e_phentsize + assert(get_le16(loader + 44) == 2); // e_phnum + assert(get_le16(loader + 48) == 0); // e_shnum + assert(lsize > 128 && lsize < 4096); + + patchLoaderChecksum(); +} + + +void PackLinuxI386::patchLoaderChecksum() +{ + l_info *const lp = (l_info *)(loader + getLoaderPrefixSize()); + // checksum for loader + p_info + lp->l_checksum = 0; // (this checksum is currently unused) + lp->l_magic = UPX_ELF_MAGIC; + lp->l_lsize = lsize; + lp->l_version = (unsigned char) ph.version; + lp->l_format = (unsigned char) ph.format; + unsigned adler = upx_adler32(0,NULL,0); + adler = upx_adler32(adler, loader, lsize + sizeof(p_info)); + lp->l_checksum = adler; +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_unix.h b/src/p_unix.h new file mode 100644 index 0000000000..3dd636e98b --- /dev/null +++ b/src/p_unix.h @@ -0,0 +1,217 @@ +/* p_unix.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_UNIX_H +#define __UPX_P_UNIX_H + + +/************************************************************************* +// Abstract class for all Unix-type packers. +// Already provides most of the functionality. +**************************************************************************/ + +class PackUnix : public Packer +{ + typedef Packer super; +protected: + PackUnix(InputFile *f); +public: + virtual int getVersion() const { return 11; } + virtual const int *getFilters() const { return NULL; } + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + +protected: + // called by the generic pack() + virtual void patchLoader() = 0; + virtual void patchLoaderChecksum() {} + + // in order too share as much code as possible we introduce + // an endian abstraction here + virtual unsigned get_native32(const void *, int off=0) = 0; + virtual void set_native32(void *, unsigned, int off=0) = 0; + + virtual bool checkCompressionRatio(unsigned, unsigned) const; + + int exetype; + unsigned blocksize; + unsigned progid; // program id + unsigned overlay_offset; // used when decompressing + + MemBuffer loader; + int lsize; + + struct l_info { // 12-byte trailer in header for loader + unsigned l_checksum; + unsigned l_magic; + unsigned short l_lsize; + unsigned char l_version; + unsigned char l_format; + }; + struct p_info { // 12-byte packed program header + unsigned p_progid; + unsigned p_filesize; + unsigned p_blocksize; + }; + + // do not change !!! + enum { OVERHEAD = 2048 }; +}; + + +/************************************************************************* +// abstract classes encapsulating endian issues +// note: UPX_MAGIC is always stored in le32 format +**************************************************************************/ + +class PackUnixBe32 : public PackUnix +{ + typedef PackUnix super; +protected: + PackUnixBe32(InputFile *f) : super(f) { } + virtual unsigned get_native32(const void * b, int off=0) + { + return get_be32(b, off); + } + virtual void set_native32(void * b, unsigned v, int off=0) + { + set_be32(b, v, off); + } +}; + + +class PackUnixLe32 : public PackUnix +{ + typedef PackUnix super; +protected: + PackUnixLe32(InputFile *f) : super(f) { } + virtual unsigned get_native32(const void * b, int off=0) + { + return get_le32(b, off); + } + virtual void set_native32(void * b, unsigned v, int off=0) + { + set_le32(b, v, off); + } +}; + + +/************************************************************************* +// linux/i386 (execve format) +**************************************************************************/ + +class PackLinuxI386 : public PackUnixLe32 +{ + typedef PackUnixLe32 super; +public: + PackLinuxI386(InputFile *f) : super(f) { } + virtual int getFormat() const { return UPX_F_LINUX_i386; } + virtual const char *getName() const { return "linux/386"; } + virtual int getCompressionMethod() const; + + virtual bool canPack(); + +protected: + virtual const upx_byte *getLoader() const; + virtual int getLoaderSize() const; + virtual int getLoaderPrefixSize() const; + + virtual void patchLoader(); + virtual void patchLoaderChecksum(); + + enum { + UPX_ELF_MAGIC = 0x5850557f // "\x7fUPX" + }; +}; + + +/************************************************************************* +// bvmlinux/i386 (Linux kernel image) +// vmlinux/i386 (Linux kernel image) +**************************************************************************/ + +class PackBvmlinuxI386 : public Packer +{ + typedef Packer super; +public: + PackBvmlinuxI386(InputFile *f); + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_BVMLINUX_i386; } + virtual const char *getName() const { return "bvmlinux/386"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const { return NULL; } + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + +protected: +// virtual const upx_byte *getLoader() const; +// virtual int getLoaderSize() const; + + unsigned elf_offset; +}; + + + +/************************************************************************* +// solaris/sparc +**************************************************************************/ + +#if 0 +class PackSolarisSparc : public PackUnixBe32 +{ + typedef PackUnixBe32 super; +public: + PackSolarisSparc(InputFile *f) : super(f) { } + virtual int getFormat() const { return UPX_F_SOLARIS_SPARC; } + virtual const char *getName() const { return "solaris/sparc"; } + + virtual bool canPack(); + +protected: + virtual const upx_byte *getLoader() const; + virtual int getLoaderSize() const; + + virtual void patchLoader(); +}; +#endif + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_vmlinux.cpp b/src/p_vmlinux.cpp new file mode 100644 index 0000000000..dba065f338 --- /dev/null +++ b/src/p_vmlinux.cpp @@ -0,0 +1,154 @@ +/* p_vmlinux.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#include "file.h" +#include "packer.h" +#include "p_unix.h" +#include "p_elf.h" +#include + + +/************************************************************************* +// +**************************************************************************/ + +PackBvmlinuxI386::PackBvmlinuxI386(InputFile *f) : + super(f), elf_offset(0) +{ +} + +int PackBvmlinuxI386::getCompressionMethod() const +{ + return M_NRV2D_LE32; +} + + +bool PackBvmlinuxI386::canPack() +{ + Elf_LE32_Ehdr ehdr; + Elf_LE32_Phdr text; + Elf_LE32_Phdr data; + unsigned char *buf = ehdr.e_ident; + + fi->seek(elf_offset, SEEK_SET); + fi->readx(&ehdr, sizeof(ehdr)); + fi->readx(&text, sizeof(text)); + fi->readx(&data, sizeof(data)); + + // check the ELF header + if (memcmp(buf, "\x7f\x45\x4c\x46\x01\x01\x01", 7)) // ELF 32-bit LSB + return false; + if (!memcmp(buf+8, "FreeBSD", 7)) // branded + return false; + if (ehdr.e_type != 2) // executable + return false; + if (ehdr.e_machine != 3 && ehdr.e_machine != 6) // Intel 80[34]86 + return false; + if (ehdr.e_version != 1) // version + return false; + + // now check for bvmlinux + if (ehdr.e_phoff != 52 || ehdr.e_ehsize != 52 || ehdr.e_phentsize != 32) + return false; + if (ehdr.e_entry != 0x100000 || ehdr.e_phnum != 2) + return false; + + // check for bvmlinux - text segment + if (text.p_type != 1 || text.p_offset != 0x1000) + return false; + if (text.p_vaddr != 0x100000 || text.p_paddr != 0x100000) + return false; + if (text.p_flags != 5) + return false; + + // check for bvmlinux - data segment + if (data.p_type != 1) + return false; + if (data.p_filesz < 200000) + return false; + if (data.p_flags != 6) + return false; + + // check gzip data + unsigned char magic[2]; + LE32 uncompressed_size; + off_t gzip_offset = elf_offset + data.p_offset + 472; + fi->seek(gzip_offset, SEEK_SET); + fi->readx(magic, 2); + if (memcmp(magic, "\037\213", 2)) + return false; + fi->seek(data.p_filesz - 2 - 4 - 472, SEEK_CUR); + fi->readx(&uncompressed_size, 4); + if (uncompressed_size < data.p_filesz || uncompressed_size > 4*1024*1024) + return false; + + // uncompress kernel with zlib + fi->seek(gzip_offset, SEEK_SET); + gzFile f = gzdopen(fi->getFd(), "r"); + if (f == NULL) + return false; + ibuf.alloc(uncompressed_size); + unsigned ilen = gzread(f, ibuf, uncompressed_size); + bool ok = ilen == uncompressed_size; + gzclose(f); + + printf("decompressed kernel: %d -> %d\n", (int)data.p_filesz, ilen); + return ok; +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackBvmlinuxI386::pack(OutputFile *fo) +{ + fo = fo; +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackBvmlinuxI386::canUnpack() +{ + return false; +} + +void PackBvmlinuxI386::unpack(OutputFile *fo) +{ + fo = fo; +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_vxd.h b/src/p_vxd.h new file mode 100644 index 0000000000..6cb6f5ca5d --- /dev/null +++ b/src/p_vxd.h @@ -0,0 +1,72 @@ +/* p_vxd.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_VXD_H +#define __UPX_P_VXD_H + + +/************************************************************************* +// Vxd +**************************************************************************/ + +class PackVxd : public PackWcle +{ + typedef PackWcle super; +public: + PackVxd(InputFile *f); + ~PackVxd(); + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_VXD_LE; } + virtual const char *getName() const { return "vxd/le"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + +protected: + virtual void encodeObjectTable(); + virtual void decodeObjectTable(); + + virtual void encodeFixupPageTable(); + virtual void decodeFixupPageTable(); + + virtual void encodeFixups(); + virtual void decodeFixups(); + + virtual void encodeImage(const Filter *ft); + virtual void decodeImage(); +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_w32pe.cpp b/src/p_w32pe.cpp new file mode 100644 index 0000000000..568430600e --- /dev/null +++ b/src/p_w32pe.cpp @@ -0,0 +1,2206 @@ +/* p_w32pe.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + +#include "conf.h" +#include "file.h" +#include "filter.h" +#include "packer.h" +#include "p_w32pe.h" + +static const +#include "stub/l_w32pe.h" + +#define IDSIZE(x) ih.ddirs[x].size +#define IDADDR(x) ih.ddirs[x].vaddr +#define ODSIZE(x) oh.ddirs[x].size +#define ODADDR(x) oh.ddirs[x].vaddr + +#define isdll ((ih.flags & DLL_FLAG) != 0) + +#define FILLVAL 0 + + +// keep the namespace clean +// it would be better to use inner classes except for Interval, which +// could be used elsewhere too + +#define Interval PackW32Pe_Interval +#define Reloc PackW32Pe_Reloc +#define Resource PackW32Pe_Resource +#define import_desc PackW32Pe_import_desc +#define Export PackW32Pe_Export + + +/************************************************************************* +// +**************************************************************************/ + +PackW32Pe::PackW32Pe(InputFile *f) : super(f) +{ + //printf("pe_header_t %d\n", (int) sizeof(pe_header_t)); + //printf("pe_section_t %d\n", (int) sizeof(pe_section_t)); + assert(sizeof(pe_header_t) == 248); + assert(sizeof(pe_section_t) == 40); + + isection = 0; + oimport = 0; + oimpdlls = 0; + orelocs = 0; + oexport = 0; + otls = 0; + oresources = 0; + oxrelocs = 0; + icondir_offset = 0; + icondir_count = 0; + importbyordinal = false; + kernel32ordinal = false; + tlsindex = 0; +} + + +PackW32Pe::~PackW32Pe() +{ + delete [] isection; + delete [] orelocs; + delete [] oimport; + delete [] oimpdlls; + delete [] oexport; + delete [] otls; + delete [] oresources; + delete [] oxrelocs; + //delete res; +} + + +int PackW32Pe::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_LE32; + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_LE32; + return opt->level > 1 && file_size >= 512*1024 ? M_NRV2D_LE32 : M_NRV2B_LE32; +} + + +const int *PackW32Pe::getFilters() const +{ + static const int filters[] = { 0x26, 0x24, 0x16, 0x13, 0x14, 0x11, + 0x25, 0x15, 0x12, -1 }; + return filters; +} + + +/************************************************************************* +// util +**************************************************************************/ + +bool PackW32Pe::readFileHeader() +{ + struct h_t + { + LE16 mz; + LE16 m512; + LE16 p512; + char _[18]; + LE16 relocoffs; + char __[34]; + LE32 nexepos; + } h; + + int ic; + pe_offset = 0; + + for (ic = 0; ic < 20; ic++) + { + fi->seek(pe_offset,SEEK_SET); + fi->readx(&h,sizeof(h)); + + if (h.mz == 'M' + 'Z'*256) // dos exe + { + if (h.relocoffs >= 0x40) // new format exe + pe_offset += h.nexepos; + else + pe_offset += h.p512*512+h.m512 - h.m512 ? 512 : 0; + } + else if (get_le32(&h) == 'P' + 'E'*256) + break; + else + return false; + } + if (ic == 20) + return false; + fi->seek(pe_offset,SEEK_SET); + fi->readx(&ih,sizeof(ih)); + fi->seek(0x200,SEEK_SET); + fi->readx(&h,6); + isrtm = memcmp(&h,"32STUB",6) == 0; + return true; +} + +// FIXME: put these into util.h + +// s/upx_byte/char/g would possibly be better +static inline char* strcpy(unsigned char *s1,const unsigned char *s2) +{ + return strcpy((char*) s1,(const char*) s2); +} +static inline int strcasecmp(const unsigned char *s1,const unsigned char *s2) +{ + return strcasecmp((const char*) s1,(const char*) s2); +} +static inline unsigned strlen(const unsigned char *s) +{ + return strlen((const char*) s); +} +static inline int ptr_diff(const void *p1,const void *p2) +{ + return (const char*) p1 - (const char*) p2; +} + + +/************************************************************************* +// interval handling +**************************************************************************/ + +class Interval +{ + unsigned capacity; + void *base; +public: + struct interval + { + unsigned start, len; + } *ivarr; + + unsigned ivnum; + + Interval(void *b) : capacity(0),base(b),ivarr(0),ivnum(0) {} + ~Interval() {free(ivarr);} + + void add(unsigned start,unsigned len); + void add(const void *start,unsigned len) {add(ptr_diff(start,base),len);} + void add(const void *start,const void *end) {add(ptr_diff(start,base),ptr_diff(end,start));} + void add(const Interval *iv); + void flatten(); + + void clear(); + void dump(); + +private: + static int compare(const void *p1,const void *p2) + { + const interval *i1 = (const interval*) p1; + const interval *i2 = (const interval*) p2; + if (i1->start < i2->start) return -1; + if (i1->start > i2->start) return 1; + if (i1->len < i2->len) return 1; + if (i1->len > i2->len) return -1; + return 0; + } +}; + +void Interval::add(unsigned start,unsigned len) +{ + if (ivnum == capacity) + ivarr = (interval*) realloc(ivarr,(capacity += 15) * sizeof (interval)); + ivarr[ivnum].start = start; + ivarr[ivnum++].len = len; +} + +void Interval::add(const Interval *iv) +{ + for (unsigned ic = 0; ic < iv->ivnum; ic++) + add(iv->ivarr[ic].start,iv->ivarr[ic].len); +} + +void Interval::flatten() +{ + if (!ivnum) + return; + qsort(ivarr,ivnum,sizeof (interval),Interval::compare); + for (unsigned ic = 0; ic < ivnum - 1; ic++) + { + unsigned jc; + for (jc = ic + 1; jc < ivnum && ivarr[ic].start + ivarr[ic].len >= ivarr[jc].start; jc++) + if (ivarr[ic].start + ivarr[ic].len < ivarr[jc].start + ivarr[jc].len) + ivarr[ic].len = ivarr[jc].start + ivarr[jc].len - ivarr[ic].start; + if (jc > ic + 1) + { + memmove(ivarr + ic + 1, ivarr + jc,sizeof(interval) * (ivnum - jc)); + ivnum -= jc - ic - 1; + } + } +} + +void Interval::clear() +{ + for (unsigned ic = 0; ic < ivnum; ic++) + memset((char*) base + ivarr[ic].start,0,ivarr[ic].len); +} + +void Interval::dump() +{ + flatten(); + printf("%d intervals:\n",ivnum); + for (unsigned ic = 0; ic < ivnum; ic++) + printf("%x %x\n",ivarr[ic].start,ivarr[ic].len); +} + + +/************************************************************************* +// relocation handling +**************************************************************************/ + +class Reloc +{ + upx_byte *start; + unsigned size; + + struct reloc + { + LE32 pagestart; + LE32 size; + } *rel; + LE16 *rel1; + void newRelocPos(void *p) { rel = (reloc*) p; rel1 = (LE16*) ((char*) p + sizeof (reloc)); } + + unsigned counts[16]; + +public: + Reloc(upx_byte *,unsigned); + Reloc(unsigned rnum); + // + bool next(unsigned &pos,unsigned &type); + const unsigned *getcounts() const { return counts; } + // + void add(unsigned pos,unsigned type); + void finish(upx_byte *&p,unsigned &size); +}; + +Reloc::Reloc(upx_byte *s,unsigned si) : start(s), size(si), rel(0) +{ + memset(counts,0,sizeof(counts)); + unsigned pos,type; + while (next(pos,type)) + counts[type]++; +} + +Reloc::Reloc(unsigned rnum) : rel(0) +{ + start = new upx_byte[rnum * 4 + 8192]; + counts[0] = 0; +} + +bool Reloc::next(unsigned &pos,unsigned &type) +{ + if (!rel) + newRelocPos(start); + if (ptr_diff(rel, start) >= (int) size || rel->pagestart == 0) + return rel = 0,false; // rewind + + pos = rel->pagestart + (*rel1 & 0xfff); + type = *rel1++ >> 12; + //printf("%x %d\n",pos,type); + if (ptr_diff(rel1,rel) >= (int) rel->size) + newRelocPos(rel1); + return type == 0 ? next(pos,type) : true; +} + +void Reloc::add(unsigned pos,unsigned type) +{ + set_le32(start + 1024 + 4 * counts[0]++,(pos << 4) + type); +} + +void Reloc::finish(upx_byte *&p,unsigned &siz) +{ + unsigned prev = 0xffffffff; + set_le32(start + 1024 + 4 * counts[0]++,0xf0000000); + qsort(start + 1024,counts[0],4,le32_compare); + + rel = (reloc*) start; + rel1 = (LE16*) rel; + for (unsigned ic = 0; ic < counts[0]; ic++) + { + unsigned pos = get_le32(start + 1024 + 4 * ic); + if ((pos ^ prev) >= 0x10000) + { + prev = pos; + *rel1 = 0; + rel->size = ALIGN_UP(ptr_diff(rel1,rel),4); + newRelocPos(rel->size + (char*) rel); + rel->pagestart = (pos >> 4) &~ 0xfff; + } + *rel1++ = (pos << 12) + ((pos >> 4) & 0xfff); + } + p = start; + siz = ptr_diff(rel1,start) &~ 3; + siz -= 8; + assert(siz > 0); + start = 0; // safety +} + +void PackW32Pe::processRelocs(Reloc *rel) // pass2 +{ + rel->finish(oxrelocs,soxrelocs); + if (opt->w32pe.strip_relocs && !isdll) + soxrelocs = 0; +} + +int PackW32Pe::processRelocs() // pass1 +{ + Reloc rel(ibuf + IDADDR(PEDIR_RELOC),IDSIZE(PEDIR_RELOC)); + const unsigned *counts = rel.getcounts(); + const unsigned rnum = counts[1] + counts[2] + counts[3]; + + if ((opt->w32pe.strip_relocs && !isdll) || rnum == 0) + { + if (IDSIZE(PEDIR_RELOC)) + memset(ibuf + IDADDR(PEDIR_RELOC),FILLVAL,IDSIZE(PEDIR_RELOC)); + orelocs = new upx_byte [1]; + return sorelocs = 0; + } + + unsigned ic; + for (ic = 15; ic > 3; ic--) + if (counts[ic]) + infoWarning("skipping unsupported relocation type %d (%d)",ic,counts[ic]); + + LE32 *fix[4]; + for (; ic; ic--) + fix[ic] = new LE32 [counts[ic]]; + + // prepare sorting + unsigned pos,type; + while (rel.next(pos,type)) + { + if (type == 3) + set_le32(ibuf + pos,get_le32(ibuf + pos) - ih.imagebase - rvamin); + if (type < 4) + *fix[type]++ = pos - rvamin; + } + fix[3] -= counts[3]; + + memset(ibuf + IDADDR(PEDIR_RELOC),FILLVAL,IDSIZE(PEDIR_RELOC)); + int big = 0; + orelocs = new upx_byte [rnum * 4 + 1024]; // 1024 - safety + sorelocs = ptr_diff(optimizeReloc32((upx_byte*) fix[3],counts[3],orelocs,ibuf + rvamin,1,&big),orelocs); + + // append relocs type "LOW" then "HIGH" + for (ic = 2; ic ; ic--) + { + fix[ic] -= counts[ic]; + memcpy(orelocs + sorelocs,fix[ic],4 * counts[ic]); + sorelocs += 4 * counts[ic]; + delete [] fix[ic]; + + set_le32(orelocs + sorelocs,0); + if (counts[ic]) + { + sorelocs += 4; + big |= 2 * ic; + } + } + delete [] fix[3]; + info("Relocations: original size: %u bytes, preprocessed size: %u bytes",(unsigned) IDSIZE(PEDIR_RELOC),sorelocs); + return big; +} + + +/************************************************************************* +// import handling +**************************************************************************/ + +struct import_desc +{ + LE32 oft; // orig first thunk + char _[8]; + LE32 dllname; + LE32 iat; // import address table +}; + +void PackW32Pe::processImports(unsigned myimport) // pass 2 +{ + // adjust import data + for (import_desc *im = (import_desc*) oimpdlls; im->dllname; im++) + { + if (im->dllname < myimport) + im->dllname += myimport; + LE32 *p = (LE32*) (oimpdlls + im->iat); + im->iat += myimport; + + while (*p) + if ((*p++ & 0x80000000) == 0) // import by name? + p[-1] += myimport; + } +} + +unsigned PackW32Pe::processImports() // pass 1 +{ + static const upx_byte kernel32dll[] = "KERNEL32.DLL"; + static const char llgpa[] = "\x0\x0""LoadLibraryA\x0\x0""GetProcAddress\x0\x0"; + static const char exitp[] = "ExitProcess\x0\x0\x0"; + + unsigned dllnum = 0; + import_desc *im = (import_desc*) (ibuf + IDADDR(PEDIR_IMPORT)); + import_desc * const im_save = im; + if (IDADDR(PEDIR_IMPORT)) + { + while (im->dllname) + dllnum++, im++; + im = im_save; + } + + struct udll + { + const upx_byte *name; + const upx_byte *shname; + unsigned ordinal; + unsigned iat; + LE32 *lookupt; + unsigned npos; + bool isk32; + unsigned _; // padding to 32 + + static int compare(const void *p1, const void *p2) + { + const udll *u1 = * (const udll * const *) p1; + const udll *u2 = * (const udll * const *) p2; + if (u1->isk32) return -1; + if (u2->isk32) return 1; + int rc = strcasecmp(u1->name,u2->name); + if (rc) return rc; + if (u1->ordinal) return -1; + if (u2->ordinal) return 1; + if (!u1->shname) return 1; + if (!u2->shname) return -1; + return strlen(u1->shname) - strlen(u2->shname); + } + } *dlls, **idlls; + + soimport = 1024; // safety + dlls = new udll[dllnum+1]; // +1 for dllnum=0 + idlls = new udll*[dllnum+1]; + + unsigned ic,k32o; + for (ic = k32o = 0; dllnum && im->dllname; ic++, im++) + { + idlls[ic] = dlls + ic; + dlls[ic].name = ibuf + im->dllname; + dlls[ic].shname = NULL; + dlls[ic].ordinal = 0; + dlls[ic].iat = im->iat; + dlls[ic].lookupt = (LE32*) (ibuf + (im->oft ? im->oft : im->iat)); + dlls[ic].npos = 0; + dlls[ic].isk32 = strcasecmp(kernel32dll,dlls[ic].name) == 0; + + soimport += strlen(dlls[ic].name) + 1 + 4; + + for (LE32 *tarr = dlls[ic].lookupt; *tarr; tarr++) + { + if (*tarr & 0x80000000) + { + importbyordinal = true; + soimport += 2; // ordinal num: 2 bytes + dlls[ic].ordinal = *tarr & 0xffff; + if (dlls[ic].isk32) + kernel32ordinal = true,k32o++; + } + else + { + unsigned len = strlen(ibuf + *tarr + 2); + soimport += len + 1; + if (dlls[ic].shname == NULL || len < strlen (dlls[ic].shname)) + dlls[ic].shname = ibuf + *tarr + 2; + } + soimport++; // separator + } + } + oimport = new upx_byte[soimport]; + memset(oimport,0,soimport); + oimpdlls = new upx_byte[soimport]; + memset(oimpdlls,0,soimport); + + qsort(idlls,dllnum,sizeof (udll*),udll::compare); + + unsigned dllnamelen = sizeof (kernel32dll); + unsigned dllnum2 = 1; + for (ic = 0; ic < dllnum; ic++) + if (!idlls[ic]->isk32 && (ic == 0 || strcasecmp(idlls[ic - 1]->name,idlls[ic]->name))) + { + dllnum2++; + dllnamelen += strlen(idlls[ic]->name) + 1; + } + //fprintf(stderr,"dllnum=%d dllnum2=%d soimport=%d\n",dllnum,dllnum2,soimport); // + + info("Processing imports: %d DLLs", dllnum); + + // create the new import table + im = (import_desc*) oimpdlls; + + LE32 *ordinals = (LE32*) (oimpdlls + (dllnum2 + 1) * sizeof(import_desc)); + LE32 *lookuptable = ordinals + 3 + k32o + (isdll ? 0 : 1); + upx_byte *dllnames = ((upx_byte*) lookuptable) + (dllnum2 - 1) * 8; + upx_byte *importednames = dllnames + (dllnamelen &~ 1); + + unsigned k32namepos = ptr_diff(dllnames,oimpdlls); + + memcpy(importednames,llgpa,sizeof(llgpa)); + if (!isdll) + memcpy(importednames + sizeof(llgpa) - 1,exitp,sizeof(exitp)); + strcpy(dllnames,kernel32dll); + im->dllname = k32namepos; + im->iat = ptr_diff(ordinals,oimpdlls); + *ordinals++ = ptr_diff(importednames,oimpdlls); + *ordinals++ = ptr_diff(importednames,oimpdlls) + 14; + if (!isdll) + *ordinals++ = ptr_diff(importednames,oimpdlls) + sizeof(llgpa) - 3; + dllnames += sizeof(kernel32dll); + importednames += sizeof(llgpa) - 2 + (isdll ? 0 : sizeof(exitp) - 1); + + im++; + for (ic = 0; ic < dllnum; ic++) + if (idlls[ic]->isk32) + { + idlls[ic]->npos = k32namepos; + if (idlls[ic]->ordinal) + for (LE32 *tarr = idlls[ic]->lookupt; *tarr; tarr++) + if (*tarr & 0x80000000) + *ordinals++ = *tarr; + } + else if (ic && strcasecmp(idlls[ic-1]->name,idlls[ic]->name) == 0) + idlls[ic]->npos = idlls[ic-1]->npos; + else + { + im->dllname = idlls[ic]->npos = ptr_diff(dllnames,oimpdlls); + im->iat = ptr_diff(lookuptable,oimpdlls); + + strcpy(dllnames,idlls[ic]->name); + dllnames += strlen(idlls[ic]->name)+1; + if (idlls[ic]->ordinal) + *lookuptable = idlls[ic]->ordinal + 0x80000000; + else if (idlls[ic]->shname) + { + if (ptr_diff(importednames,oimpdlls) & 1) + importednames--; + *lookuptable = ptr_diff(importednames,oimpdlls); + importednames += 2; + strcpy(importednames,idlls[ic]->shname); + importednames += strlen(idlls[ic]->shname) + 1; + } + lookuptable += 2; + im++; + } + soimpdlls = ALIGN_UP(ptr_diff(importednames,oimpdlls),4); + + Interval names(ibuf),iats(ibuf),lookups(ibuf); + // create the preprocessed data + ordinals -= k32o; + upx_byte *ppi = oimport; // preprocessed imports + for (ic = 0; ic < dllnum; ic++) + { + LE32 *tarr = idlls[ic]->lookupt; + if (!*tarr) // no imports from this dll + continue; + set_le32(ppi,idlls[ic]->npos); + set_le32(ppi+4,idlls[ic]->iat - rvamin); + ppi += 8; + for (; *tarr; tarr++) + if (*tarr & 0x80000000) + { + if (idlls[ic]->isk32) + { + *ppi++ = 0xfe; // signed + odd parity + set_le32(ppi,ptr_diff(ordinals,oimpdlls)); + ordinals++; + ppi += 4; + } + else + { + *ppi++ = 0xff; + set_le16(ppi,*tarr & 0xffff); + ppi += 2; + } + } + else + { + *ppi++ = 1; + unsigned len = strlen(ibuf + *tarr + 2) + 1; + memcpy(ppi,ibuf + *tarr + 2,len); + ppi += len; + names.add(*tarr,len + 2 + 1); + } + ppi++; + + unsigned esize = ptr_diff(tarr,idlls[ic]->lookupt); + lookups.add(idlls[ic]->lookupt,esize); + if (ptr_diff(ibuf + idlls[ic]->iat,idlls[ic]->lookupt)) + { + memcpy(ibuf + idlls[ic]->iat,idlls[ic]->lookupt,esize); + iats.add(idlls[ic]->iat,esize); + } + names.add(idlls[ic]->name,strlen(idlls[ic]->name) + 1 + 1); + } + ppi += 4; + assert(ppi < oimport+soimport); + soimport = ptr_diff(ppi,oimport); + + if (soimport == 4) + soimport = 0; +#if 0 + FILE *f1=fopen("x0.imp","wb"); + fwrite(oimport,1,soimport,f1); + fclose(f1); + f1=fopen("x1.imp","wb"); + fwrite(oimpdlls,1,soimpdlls,f1); + fclose(f1); +#endif + + unsigned ilen = 0; + names.flatten(); + if (names.ivnum > 1) + { + // The area occupied by the dll and imported names is not continuous + // so to still support uncompression, I can't zero the iat area. + // This decreases compression ratio, so FIXME somehow. + infoWarning("can't remove unneeded imports"); + ilen += sizeof(import_desc) * dllnum; + if (opt->verbose > 3) + names.dump(); + // do some work for the unpacker + im = im_save; + for (ic = 0; ic < dllnum; ic++) + { + memset(im,FILLVAL,sizeof(*im)); + im++->dllname = ptr_diff(idlls[ic]->name,ibuf); // I only need this info + } + } + else + { + iats.add(im_save,sizeof(import_desc) * dllnum); + // zero unneeded data + iats.clear(); + lookups.clear(); + } + names.clear(); + + iats.add(&names); + iats.add(&lookups); + iats.flatten(); + for (ic = 0; ic < iats.ivnum; ic++) + ilen += iats.ivarr[ic].len; + + delete [] dlls; + delete [] idlls; + + info("Imports: original size: %u bytes, preprocessed size: %u bytes",ilen,soimport); + return names.ivnum == 1 ? names.ivarr[0].start : 0; +} + + +/************************************************************************* +// export handling +**************************************************************************/ + +class Export +{ + struct export_dir + { + char _[12]; // flags, timedate, version + LE32 name; + char __[4]; // ordinal base + LE32 functions; + LE32 names; + LE32 addrtable; + LE32 nameptrtable; + LE32 ordinaltable; + }; + + export_dir edir; + char *ename; + char *functionptrs; + char *ordinals; + char **names; + + char *base; + unsigned size; + Interval iv; + +public: + Export(char *base); + ~Export(); + + void convert(unsigned eoffs,unsigned esize); + void build(char *base,unsigned newoffs); + unsigned getsize() const { return size; } + +private: + Export(const Export&); + Export& operator=(const Export&); +}; + +Export::Export(char *_base) : base(_base), iv(_base) +{ + ename = functionptrs = ordinals = 0; + names = 0; + memset(&edir,0,sizeof(edir)); + size = 0; +} + +Export::~Export() +{ + free(ename); + delete [] functionptrs; + delete [] ordinals; + for (unsigned ic = 0; ic < edir.names + edir.functions; ic++) + free(names[ic]); + delete [] names; +} + +void Export::convert(unsigned eoffs,unsigned esize) +{ + memcpy(&edir,base + eoffs,sizeof(export_dir)); + size = sizeof(export_dir); + iv.add(eoffs,size); + + unsigned len = strlen(base + edir.name) + 1; + ename = strdup(base + edir.name); + size += len; + iv.add(edir.name,len); + + len = 4 * edir.functions; + functionptrs = new char[len + 1]; + memcpy(functionptrs,base + edir.addrtable,len); + size += len; + iv.add(edir.addrtable,len); + + unsigned ic; + names = new char* [edir.names + edir.functions + 1]; + for (ic = 0; ic < edir.names; ic++) + { + char *n = base + get_le32(base + edir.nameptrtable + ic * 4); + len = strlen(n) + 1; + names[ic] = strdup(n); + size += len; + iv.add(get_le32(base + edir.nameptrtable + ic * 4),len); + } + iv.add(edir.nameptrtable,4 * edir.names); + size += 4 * edir.names; + + LE32 *fp = (LE32*) functionptrs; + // export forwarders + for (ic = 0; ic < edir.functions; ic++) + if (fp[ic] >= eoffs && fp[ic] < eoffs + esize) + { + char *forw = base + fp[ic]; + len = strlen(forw) + 1; + iv.add(forw,len); + size += len; + names[ic + edir.names] = strdup(forw); + } + else + names[ic + edir.names] = 0; + + len = 2 * edir.names; + ordinals = new char[len + 1]; + memcpy(ordinals,base + edir.ordinaltable,len); + size += len; + iv.add(edir.ordinaltable,len); + iv.flatten(); + if (iv.ivnum == 1) + iv.clear(); + else + iv.dump(); +} + +void Export::build(char *newbase,unsigned newoffs) +{ + char *functionp = newbase + sizeof(edir); + char *namep = functionp + 4 * edir.functions; + char *ordinalp = namep + 4 * edir.names; + char *enamep = ordinalp + 2 * edir.names; + char *exports = enamep + strlen(ename) + 1; + + edir.addrtable = newoffs + functionp - newbase; + edir.ordinaltable = newoffs + ordinalp - newbase; + memcpy(ordinalp,ordinals,2 * edir.names); + + edir.name = newoffs + enamep - newbase; + strcpy(enamep,ename); + edir.nameptrtable = newoffs + namep - newbase; + unsigned ic; + for (ic = 0; ic < edir.names; ic++) + { + strcpy(exports,names[ic]); + set_le32(namep + 4 * ic,newoffs + exports - newbase); + exports += strlen(exports) + 1; + } + + memcpy(functionp,functionptrs,4 * edir.functions); + for (ic = 0; ic < edir.functions; ic++) + if (names[edir.names + ic]) + { + strcpy(exports,names[edir.names + ic]); + set_le32(functionp + 4 * ic,newoffs + exports - newbase); + exports += strlen(exports) + 1; + } + + memcpy(newbase,&edir,sizeof(edir)); + assert(exports - newbase == (int) size); +} + +void PackW32Pe::processExports(Export *xport) // pass1 +{ + soexport = ALIGN_UP(IDSIZE(PEDIR_EXPORT),4); + if (!isdll && opt->w32pe.compress_exports) + { + if (soexport) + infoWarning("exports compressed, --compress-exports=0 might be needed"); + soexport = 0; + return; + } + if (soexport == 0) + return; + xport->convert(IDADDR(PEDIR_EXPORT),IDSIZE(PEDIR_EXPORT)); + soexport = ALIGN_UP(xport->getsize(),4); + oexport = new upx_byte[soexport]; + memset(oexport, 0, soexport); +} + +void PackW32Pe::processExports(Export *xport,unsigned newoffs) // pass2 +{ + if (soexport) + xport->build((char*) oexport,newoffs); +} + + +/************************************************************************* +// TLS handling +**************************************************************************/ + +// thanks for theowl for providing me some docs, so that now I understand +// what I'm doing here :) + +// 1999-10-17: this was tricky to find: +// when the fixup records and the tls area are on the same page, then +// the tls area is not relocated, because the relocation is done by +// the virtual memory manager only for pages which are not yet loaded. +// of course it was impossible to debug this ;-) + +struct tls +{ + LE32 datastart; // VA tls init data start + LE32 dataend; // VA tls init data end + LE32 tlsindex; // VA tls index + LE32 callbacks; // VA tls callbacks + char _[8]; // zero init, characteristics +}; + +void PackW32Pe::processTls(Interval *iv) // pass 1 +{ + if ((sotls = ALIGN_UP(IDSIZE(PEDIR_TLS),4)) == 0) + return; + + const tls * const tlsp = (const tls*) (ibuf + IDADDR(PEDIR_TLS)); + unsigned tlsdatastart = tlsp->datastart - ih.imagebase; + unsigned tlsdataend = tlsp->dataend - ih.imagebase; + + // now some ugly stuff: find the relocation entries in the tls data area + unsigned pos,type; + Reloc rel(ibuf + IDADDR(PEDIR_RELOC),IDSIZE(PEDIR_RELOC)); + while (rel.next(pos,type)) + if (pos >= tlsdatastart && pos < tlsdataend) + iv->add(pos,type); + + sotls = sizeof(tls) + tlsdataend - tlsdatastart; + + // the PE loader wants this stuff uncompressed + otls = new upx_byte[sotls]; + memset(otls,0,sotls); + memcpy(otls,ibuf + IDADDR(PEDIR_TLS),0x18); + // WARNING: this can acces data in BSS + memcpy(otls + sizeof(tls),ibuf + tlsdatastart,sotls - sizeof(tls)); + tlsindex = tlsp->tlsindex - ih.imagebase; + info("TLS: %u bytes tls data and %u relocations added",sotls - (unsigned) sizeof(tls),iv->ivnum); +} + +void PackW32Pe::processTls(Reloc *rel,const Interval *iv,unsigned newaddr) // pass 2 +{ + if (sotls == 0) + return; + // add new relocation entries + unsigned ic; + for (ic = 0; ic < 12; ic += 4) + rel->add(newaddr + ic,3); + + tls * const tlsp = (tls*) otls; + // now the relocation entries in the tls data area + for (ic = 0; ic < iv->ivnum; ic += 4) + { + void *p = otls + iv->ivarr[ic].start - (tlsp->datastart - ih.imagebase) + sizeof(tls); + unsigned kc = get_le32(p); + if (kc < tlsp->dataend && kc >= tlsp->datastart) + { + kc += newaddr + sizeof(tls) - tlsp->datastart; + set_le32(p,kc + ih.imagebase); + rel->add(kc,iv->ivarr[ic].len); + } + else + rel->add(kc - ih.imagebase,iv->ivarr[ic].len); + } + + tlsp->datastart = newaddr + sizeof(tls) + ih.imagebase; + tlsp->dataend = newaddr + sotls + ih.imagebase; + tlsp->callbacks = 0; // note: callbacks are not implemented in windoze +} + + +/************************************************************************* +// resource handling +**************************************************************************/ + +class Resource +{ + struct res_dir_entry + { + LE32 tnl; // Type | Name | Language id - depending on level + LE32 child; + }; + struct res_dir + { + char _[12]; // flags, timedate, version + LE16 namedentr; + LE16 identr; + + unsigned Sizeof() const { return 16 + sizeof(res_dir_entry)*(namedentr + identr); } + res_dir_entry entries[1]; + // it's usually safe to assume that every res_dir contains + // at least one res_dir_entry - check() complains otherwise + }; + struct res_data + { + LE32 offset; + LE32 size; + char _[8]; // codepage, reserved + }; + // + struct upx_rnode + { + unsigned id; + upx_byte *name; + upx_rnode *parent; + }; + struct upx_rbranch : upx_rnode + { + unsigned nc; + upx_rnode **children; + res_dir data; + }; + struct upx_rleaf : upx_rnode + { + upx_rleaf *next; + unsigned newoffset; + res_data data; + }; + + const upx_byte *start; + upx_byte *newstart; + upx_rnode *root; + upx_rleaf *head; + upx_rleaf *current; + unsigned dsize; + unsigned ssize; + + void check(const res_dir*,unsigned); + upx_rnode *convert(const void *,upx_rnode *,unsigned); + void build(const upx_rnode *,unsigned &,unsigned &,unsigned); + void clear(upx_byte *,unsigned,Interval *); + void dump(const upx_rnode *,unsigned) const; + void destroy(upx_rnode *urd,unsigned level); + +public: + Resource() : root(0) {} + Resource(const upx_byte *p) {init(p);} + ~Resource() {if (root) destroy (root,0);} + void init(const upx_byte *); + + unsigned dirsize() const {return ALIGN_UP(dsize + ssize,4);} + bool next() {return (current = current ? current->next : head) != 0;} // wow, builtin autorewind... :-) + + unsigned itype() const {return current->parent->parent->id;} + const upx_byte *ntype() const {return current->parent->parent->name;} + unsigned size() const {return ALIGN_UP(current->data.size,4);} + unsigned offs() const {return current->data.offset;} + unsigned &newoffs() {return current->newoffset;} + + upx_byte *build(); + bool clear(); + + void dump() const {dump (root,0);} +/* + unsigned iname() const {return current->parent->id;} + const upx_byte *nname() const {return current->parent->name;} + + unsigned ilang() const {return current->id;} + const upx_byte *nlang() const {return current->name;} +*/ +}; + +void Resource::init(const upx_byte *res) +{ + start = res; + root = head = current = 0; + dsize = ssize = 0; + check((const res_dir*) start,0); + root = convert(start,0,0); +} + +void Resource::check(const res_dir *node,unsigned level) +{ + int ic = node->identr + node->namedentr; + if (ic == 0) + throwCantPack("unsupported resource structure"); + for (const res_dir_entry *rde = node->entries; --ic >= 0; rde++) + if (((rde->child & 0x80000000) == 0) ^ (level == 2)) + throwCantPack("unsupported resource structure"); + else if (level != 2) + check((const res_dir*) (start + (rde->child & 0x7fffffff)),level + 1); +} + +Resource::upx_rnode * Resource::convert(const void *rnode,upx_rnode *parent,unsigned level) +{ + if (level == 3) + { + const res_data *node = (const res_data *) rnode; + upx_rleaf *leaf = new upx_rleaf; + leaf->name = 0; + leaf->parent = parent; + leaf->next = head; + leaf->newoffset = 0; + leaf->data = *node; + + head = leaf; // append node to a linked list for traversal + dsize += sizeof(res_data); + return leaf; + } + + const res_dir *node = (const res_dir *) rnode; + upx_rbranch *branch = new upx_rbranch; + branch->name = 0; + branch->parent = parent; + int ic = branch->nc = node->identr + node->namedentr; + branch->children = new upx_rnode*[ic]; + branch->data = *node; + + for (const res_dir_entry *rde = node->entries + ic - 1; --ic >= 0; rde--) + { + upx_rnode *child = convert(start + (rde->child & 0x7fffffff),branch,level + 1); + branch->children[ic] = child; + child->id = rde->tnl; + if (child->id & 0x80000000) + { + const upx_byte *p = start + (child->id & 0x7fffffff); + const unsigned len = 2 + 2 * get_le16(p); + child->name = new upx_byte[len]; + memcpy(child->name,p,len); // copy unicode string + ssize += len; // size of unicode strings + } + } + dsize += node->Sizeof(); + return branch; +} + +void Resource::build(const upx_rnode *node,unsigned &bpos,unsigned &spos,unsigned level) +{ + if (level == 3) + { + res_data *l = (res_data*) (newstart + bpos); + const upx_rleaf *leaf = (const upx_rleaf*) node; + *l = leaf->data; + if (leaf->newoffset) + l->offset = leaf->newoffset; + bpos += sizeof(*l); + return; + } + res_dir * const b = (res_dir*) (newstart + bpos); + const upx_rbranch *branch = (const upx_rbranch*) node; + *b = branch->data; + bpos += b->Sizeof(); + res_dir_entry *be = b->entries; + for (unsigned ic = 0; ic < branch->nc; ic++, be++) + { + be->tnl = branch->children[ic]->id; + be->child = bpos + ((level < 2) ? 0x80000000 : 0); + + const upx_byte *p; + if ((p = branch->children[ic]->name) != 0) + { + be->tnl = spos + 0x80000000; + memcpy(newstart + spos,p,get_le16(p) * 2 + 2); + spos += get_le16(p) * 2 + 2; + } + + build(branch->children[ic],bpos,spos,level + 1); + } +} + +upx_byte *Resource::build() +{ + newstart = new upx_byte [dirsize()]; + unsigned bpos = 0,spos = dsize; + build(root,bpos,spos,0); + return newstart; +} + +void Resource::destroy(upx_rnode *node,unsigned level) +{ + delete [] node->name; + if (level == 3) + return; + const upx_rbranch *branch = (const upx_rbranch*) node; + for (int ic = branch->nc; --ic >= 0; ) + destroy(branch->children[ic],level + 1); + delete [] branch->children; +} + +static void lame_print_unicode (const upx_byte *p) +{ + for (unsigned ic = 0; ic < get_le16(p); ic++) + printf("%c",(char)p[ic * 2 + 2]); +} + +void Resource::dump(const upx_rnode *node,unsigned level) const +{ + if (level) + { + printf("\t\t\t\t" + 6 - level * 2); + if (node->name) + lame_print_unicode(node->name); + else + printf("%x",node->id); + printf("\n"); + } + if (level == 3) + return; + const upx_rbranch *branch = (const upx_rbranch*) node; + for (unsigned ic = 0; ic < branch->nc; ic++) + dump(branch->children[ic],level + 1); +} + +void Resource::clear(upx_byte *node,unsigned level,Interval *iv) +{ + if (level == 3) + iv->add(node,sizeof (res_data)); + else + { + const res_dir * const rd = (res_dir*) node; + const res_dir_entry *rde = rd->entries; + for (unsigned ic = 0; ic < rd->identr + rd->namedentr; ic++, rde++) + clear(newstart + (rde->child & 0x7fffffff),level + 1,iv); + iv->add(rd,rd->Sizeof()); + } +} + +bool Resource::clear() +{ + newstart = const_cast (start); + Interval iv(newstart); + clear(newstart,0,&iv); + iv.flatten(); + if (iv.ivnum == 1) + iv.clear(); + if (opt->verbose > 3) + iv.dump(); + return iv.ivnum == 1; +} + +void PackW32Pe::processResources(Resource* res,unsigned newaddr) +{ + if (IDSIZE(PEDIR_RESOURCE) == 0) + return; + while (res->next()) + if (res->newoffs()) + res->newoffs() += newaddr; + upx_byte *p = res->build(); + memcpy(oresources,p,res->dirsize()); + delete [] p; +} + +void PackW32Pe::processResources(Resource* res) +{ + const unsigned vaddr = IDADDR(PEDIR_RESOURCE); + if ((soresources = IDSIZE(PEDIR_RESOURCE)) == 0) + return; + res->init(ibuf + vaddr); + + for (soresources = res->dirsize(); res->next(); soresources += 4 + res->size()) + ; + oresources = new upx_byte[soresources]; + upx_byte *ores = oresources + res->dirsize(); + + unsigned iconsin1stdir = 0; + if (opt->w32pe.compress_icons == 2) + while (res->next()) // there is no rewind() in Resource + if (res->itype() == 14 && iconsin1stdir == 0) + iconsin1stdir = get_le16(ibuf + res->offs() + 4); + + bool compress_icon = false, compress_idir = false; + unsigned iconcnt = 0; + + // some statistics + unsigned usize = 0; + unsigned csize = 0; + unsigned unum = 0; + unsigned cnum = 0; + + while (res->next()) + { + const unsigned rtype = res->itype(); + bool do_compress = true; + if (rtype == 16) // version info + do_compress = false; + else if (rtype == 3) // icon + do_compress = compress_icon && opt->w32pe.compress_icons; + else if (rtype == 14) // icon directory + do_compress = compress_idir && opt->w32pe.compress_icons; + else if (res->ntype()) // named resource type + if (0 == memcmp(res->ntype(),"\x7\x0T\x0Y\x0P\x0""E\x0L\x0I\x0""B\x0",16) + || 0 == memcmp(res->ntype(),"\x8\x0R\x0""E\x0G\x0I\x0S\x0T\x0R\x0Y\x0",18)) + do_compress = false; // typelib or registry + + if (!opt->w32pe.compress_resources) + do_compress = false; + if (do_compress) + { + csize += res->size(); + cnum++; + continue; + } + usize += res->size(); + unum++; + + set_le32(ores,res->offs()); // save original offset + ores += 4; + memcpy(ores,ibuf + res->offs(),res->size()); + memset(ibuf + res->offs(),FILLVAL,res->size()); + res->newoffs() = ptr_diff(ores,oresources); + if (rtype == 3) + compress_icon = iconcnt++ >= iconsin1stdir || opt->w32pe.compress_icons == 1; + else if (rtype == 14) + { + if (opt->w32pe.compress_icons == 1) + { + icondir_offset = 4 + ptr_diff(ores,oresources); + icondir_count = get_le16 (oresources + icondir_offset); + set_le16 (oresources + icondir_offset,1); + } + compress_idir = true; + } + ores += res->size(); + } + soresources = ptr_diff(ores,oresources); + + if (!res->clear()) + { + // The area occupied by the resource directory is not continuous + // so to still support uncompression, I can't zero this area. + // This decreases compression ratio, so FIXME somehow. + infoWarning("can't remove unneeded resource directory"); + } + info("Resources: compressed %u (%u bytes), not compressed %u (%u bytes)",cnum,csize,unum,usize); +} + + +unsigned PackW32Pe::virta2objnum(unsigned addr,pe_section_t *sect,unsigned objs) +{ + unsigned ic; + for (ic = 0; ic < objs; ic++) + { + if (sect->vaddr <= addr && sect->vaddr + sect->vsize > addr) + return ic; + sect++; + } + //throwCantPack("virta2objnum() failed"); + return ic; +} + + +unsigned PackW32Pe::tryremove (unsigned vaddr,unsigned objs) +{ + unsigned ic = virta2objnum(vaddr,isection,objs); + if (ic && ic == objs - 1) + { + //fprintf(stderr,"removed section: %d size: %lx\n",ic,(long)isection[ic].size); + info("removed section: %d size: 0x%lx",ic,(long)isection[ic].size); + objs--; + } + return objs; +} + + +unsigned PackW32Pe::stripDebug(unsigned overlaystart) +{ + if (IDADDR(PEDIR_DEBUG) == 0) + return overlaystart; + + struct debug_dir_t + { + char _[16]; // flags, time/date, version, type + LE32 size; + char __[4]; // rva + LE32 fpos; + }; + + const debug_dir_t *dd = (const debug_dir_t*) (ibuf + IDADDR(PEDIR_DEBUG)); + for (unsigned ic = 0; ic < IDSIZE(PEDIR_DEBUG) / sizeof(debug_dir_t); ic++, dd++) + if (overlaystart == dd->fpos) + overlaystart += dd->size; + memset(ibuf + IDADDR(PEDIR_DEBUG),FILLVAL,IDSIZE(PEDIR_DEBUG)); + return overlaystart; +} + + +/************************************************************************* +// pack +**************************************************************************/ + +bool PackW32Pe::canPack() +{ + return readFileHeader(); +} + + +void PackW32Pe::pack(OutputFile *fo) +{ + unsigned objs = ih.objects; + isection = new pe_section_t[objs]; + fi->seek(pe_offset+sizeof(ih),SEEK_SET); + fi->readx(isection,sizeof(pe_section_t)*objs); + + rvamin = isection[0].vaddr; + + infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), objs); + + // check the PE header + // FIXME: add more checks + if (!opt->force + && (ih.cpu < 0x14c || ih.cpu > 0x150 + || ih.opthdrsize != 0xE0 + || (ih.flags & EXECUTABLE) == 0 + || (ih.subsystem != 2 && ih.subsystem != 3 && ih.subsystem != 1) + || (ih.entry == 0 && !isdll) + || ih.ddirsentries != 16 + || IDSIZE(PEDIR_EXCEPTION) // is this used on i386? +// || IDSIZE(PEDIR_COPYRIGHT) + || IDSIZE(PEDIR_LOADCONF) + || IDSIZE(PEDIR_COMRT) + )) + throwCantPack("unexpected value in PE header (try --force)"); + + if (IDSIZE(PEDIR_SEC)) + throwCantPack("compressing certificate info is not supported"); + //if (IDSIZE(PEDIR_DELAYIMP)) + // throwCantPack("delay load imports are not supported"); + if (isdll) + opt->w32pe.strip_relocs = 0; + else if (opt->w32pe.strip_relocs < 0) + opt->w32pe.strip_relocs = (ih.imagebase >= 0x400000); + if (opt->w32pe.strip_relocs) + if (ih.imagebase < 0x400000) + throwCantPack("--strip-relocs is not allowed when imagebase < 0x400000"); + else + ih.flags |= RELOCS_STRIPPED; + + if (memcmp(isection[0].name,"UPX",3) == 0) + throwAlreadyPacked(); + if (!opt->force && (IDSIZE(15) || ih.entry > isection[1].vaddr)) + throwCantPack("file is possibly packed/protected (try --force)"); + if (ih.entry && ih.entry < rvamin) + throwCantPack("run a virus scanner on this file first"); + if (!opt->force && ih.subsystem == 1) + throwCantPack("subsystem `native' is not supported (try --force)"); + if (ih.filealign < 0x200) + throwCantPack("filealign < 0x200 is not yet supported"); + + handleStub(fi,fo,pe_offset); + unsigned usize = ih.imagesize; + const unsigned xtrasize = 65536+IDSIZE(PEDIR_IMPORT)+IDSIZE(PEDIR_BOUNDIM)+IDSIZE(PEDIR_IAT)+IDSIZE(PEDIR_DELAYIMP)+IDSIZE(PEDIR_RELOC); + ibuf.alloc(usize+xtrasize); + + // BOUND IMPORT support. FIXME: is this ok? + fi->seek(0,SEEK_SET); + fi->readx(ibuf,isection[0].rawdataptr); + + Interval holes(ibuf); + + unsigned ic,jc,overlaystart = 0; + memset(ibuf,0,usize); + for (ic = 0; ic < objs; ic++) + { + if (isection[ic].rawdataptr && overlaystart < isection[ic].rawdataptr + isection[ic].size) + overlaystart = ALIGN_UP(isection[ic].rawdataptr + isection[ic].size,ih.filealign); + if (isection[ic].vsize == 0) + isection[ic].vsize = isection[ic].size; + if ((isection[ic].flags & PEFL_BSS) || isection[ic].rawdataptr == 0 + || (isection[ic].flags & PEFL_INFO)) + { + holes.add(isection[ic].vaddr,isection[ic].vsize); + continue; + } + if (!isrtm && ((isection[ic].flags & (PEFL_WRITE|PEFL_SHARED)) + == (PEFL_WRITE|PEFL_SHARED)) && !opt->force) + throwCantPack("writeable shared sections not supported (try --force)"); + fi->seek(isection[ic].rawdataptr,SEEK_SET); + if (isection[ic].vaddr + isection[ic].size > usize) + throwCantPack("section size problem"); + jc = isection[ic].size; + if (jc > isection[ic].vsize) + jc = isection[ic].vsize; + if (isection[ic].vsize == 0) // hack for some tricky programs - may this break other progs? + jc = isection[ic].vsize = isection[ic].size; + fi->readx(ibuf + isection[ic].vaddr,jc); + } + + // check for NeoLite + for (ic = 0; ic < 64; ic++) + if (memcmp(ibuf + ih.entry + ic,"NeoLite",7) == 0) + throwCantPack("file is already compressed with another packer"); + + unsigned overlay = file_size - stripDebug(overlaystart); + if (overlay >= (unsigned) file_size) + { +#if 0 + if (overlay < file_size + ih.filealign) + overlay = 0; + else if (!opt->force) + throwNotCompressible("overlay problem (try --force)"); +#endif + overlay = 0; + } + checkOverlay(overlay); + + Resource res; + Interval tlsiv(ibuf); + Export xport((char*)(unsigned char*)ibuf); + + const unsigned dllstrings = processImports(); + processTls(&tlsiv); // call before processRelocs!! + processResources(&res); + processExports(&xport); + const int big = processRelocs(); + + //FILE *f1=fopen("x1","wb"); + //fwrite(ibuf,1,usize,f1); + //fclose(f1); + + // some check for broken linkers - disable filter if neccessary + bool allow_filter = true; + if (ih.codebase == ih.database + || ih.codebase + ih.codesize > ih.imagesize + || (isection[virta2objnum(ih.codebase,isection,objs)].flags & PEFL_CODE) == 0) + allow_filter = false; + + // filter + Filter ft(opt->level); + if (allow_filter) + tryFilters(&ft, ibuf + ih.codebase, ih.codesize); + + const unsigned oam1 = ih.objectalign-1; + + // FIXME: disabled: the uncompressor would not allocate enough memory + //objs = tryremove(IDADDR(PEDIR_RELOC),objs); + + // FIXME: if the last object has a bss then this won't work + // newvsize = (isection[objs-1].vaddr + isection[objs-1].size + oam1) &~ oam1; + // temporary solution: + unsigned newvsize = (isection[objs-1].vaddr + isection[objs-1].vsize + oam1) &~ oam1; + + //fprintf(stderr,"newvsize=%x objs=%d\n",newvsize,objs); + memcpy(ibuf+newvsize,oimport,soimport); + memcpy(ibuf+newvsize+soimport,orelocs,sorelocs); + + const unsigned cimports = newvsize - rvamin; // rva of preprocessed imports + const unsigned crelocs = cimports + soimport; // rva of preprocessed fixups + + ph.filter = ft.id; + ph.filter_cto = ft.cto; + ph.u_len = newvsize+sorelocs+soimport; + + // some extra data for uncompression support + unsigned s = 0; + upx_byte *p1 = ibuf + ph.u_len; + memcpy(p1 + s,&ih,sizeof (ih)); + s += sizeof (ih); + memcpy(p1 + s,isection,ih.objects * sizeof(*isection)); + s += ih.objects * sizeof(*isection); + if (soimport) + { + set_le32(p1 + s,cimports); + set_le32(p1 + s + 4,dllstrings); + s += 8; + } + if (sorelocs) + { + set_le32(p1 + s,crelocs); + p1[s + 4] = (unsigned char) (big & 6); + s += 5; + } + if (soresources) + { + set_le16(p1 + s,icondir_count); + s += 2; + } + // end of extra data + set_le32(p1 + s,ptr_diff(p1,ibuf) - rvamin); + s += 4; + ph.u_len += s; + obuf.allocForCompression(ph.u_len); + + ph.u_len -= rvamin; + if (!compress(ibuf + rvamin,obuf)) + throwNotCompressible(); + const unsigned overlapoh = findOverlapOverhead(obuf, 2048); + + // verify filter + ft.verifyUnfilter(); + + newvsize = (ph.u_len + rvamin + overlapoh + oam1) &~ oam1; + if (tlsindex && ((newvsize - ph.c_len - 1024 + oam1) &~ oam1) > tlsindex + 4) + tlsindex = 0; + + // prepare loader + initLoader(nrv_loader,sizeof(nrv_loader)); + addLoader(isdll ? "PEISDLL1" : "", + "PEMAIN01", + icondir_count > 1 ? (icondir_count == 2 ? "PEICONS1" : "PEICONS2") : "", + tlsindex ? "PETLSHAK" : "", + "PEMAIN02", + getDecompressor(), + /*multipass ? "PEMULTIP" : */ "", + "PEMAIN10", + NULL + ); + const unsigned texv = ih.codebase - rvamin; + if (ft.id) + { + assert(ft.calls > 0); + addLoader(texv ? "PECTTPOS" : "PECTTNUL",NULL); + addFilter32(ft.id); + } + if (soimport) + addLoader("PEIMPORT", + importbyordinal ? "PEIBYORD" : "", + kernel32ordinal ? "PEK32ORD" : "", + importbyordinal ? "PEIMORD1" : "", + "PEIMPOR2", + isdll ? "PEIERDLL" : "PEIEREXE", + "PEIMDONE", + NULL + ); + if (sorelocs) + { + addLoader(soimport == 0 || soimport + cimports != crelocs ? "PERELOC1" : "PERELOC2", + "PERELOC3""RELOC320", + big ? "REL32BIG" : "", + "RELOC32J", + NULL + ); + //FIXME: the following should be moved out of the above if + addLoader(big&6 ? "PERLOHI0" : "", + big&4 ? "PERELLO0" : "", + big&2 ? "PERELHI0" : "", + NULL + ); + } + addLoader("PEMAIN20", + ih.entry ? "PEDOJUMP" : "PERETURN", + "IDENTSTR""UPX1HEAD", + NULL + ); + + const unsigned lsize = getLoaderSize(); + MemBuffer loader(lsize); + memcpy(loader,getLoader(),lsize); + + int identsize = 0; + const unsigned codesize = getLoaderSection("IDENTSTR",&identsize); + assert(identsize > 0); + getLoaderSection("UPX1HEAD",(int*)&ic); + identsize += ic; + + pe_section_t osection[3]; + // section 0 : bss + // 1 : [ident + header] + packed_data + unpacker + tls + // 2 : not compressed data + + // section 2 should start with the resource data, because lots of lame + // windoze codes assume that resources starts on the beginning of a section + + // identsplit - number of ident + (upx header) bytes to put into the PE heaader + int identsplit = pe_offset + sizeof(osection) + sizeof(oh); + if ((identsplit & 0x1ff) == 0) + identsplit = 0; + else if (((identsplit + identsize) ^ identsplit) < 0x200) + identsplit = identsize; + else + identsplit = ALIGN_UP(identsplit, 0x200) - identsplit; + ic = identsize - identsplit; + + const unsigned clen = ((ph.c_len + ic) & 15) == 0 ? ph.c_len : ph.c_len + 16 - ((ph.c_len + ic) & 15); + const unsigned s1size = ALIGN_UP(ic + clen + codesize,4) + sotls; + const unsigned s1addr = (newvsize - (ic + clen) + oam1) &~ oam1; + + const unsigned ncsection = (s1addr + s1size + oam1) &~ oam1; + const unsigned upxsection = s1addr + ic + clen; + const unsigned myimport = ncsection + soresources - rvamin; + + // patch loader + unsigned jmp_pos; + if (ih.entry) + { + jmp_pos = ptr_diff(find_le32(loader,codesize + 4,get_le32("JMPO")),loader); + patch_le32(loader,codesize + 4,"JMPO",ih.entry - upxsection - jmp_pos - 4); + } + if (big & 6) + patch_le32(loader,codesize,"DELT", 0u -ih.imagebase - rvamin); + if (sorelocs && (soimport == 0 || soimport + cimports != crelocs)) + patch_le32(loader,codesize,"BREL",crelocs); + if (soimport) + { + if (!isdll) + patch_le32(loader,codesize,"EXIT",myimport + get_le32(oimpdlls + 16) + 8); + patch_le32(loader,codesize,"GETP",myimport + get_le32(oimpdlls + 16) + 4); + if (kernel32ordinal) + patch_le32(loader,codesize,"K32O",myimport); + patch_le32(loader,codesize,"LOAD",myimport + get_le32(oimpdlls + 16)); + patch_le32(loader,codesize,"IMPS",myimport); + patch_le32(loader,codesize,"BIMP",cimports); + } + if (ft.id) + { + assert(ft.calls > 0); + if (ft.id > 0x20) + patch_le16(loader,codesize,"??",'?' + (ft.cto << 8)); + patch_le32(loader,lsize,"TEXL",(ft.id & 0xf) % 3 == 0 ? ft.calls : + ft.lastcall - ft.calls * 4); + if (texv) + patch_le32(loader,codesize,"TEXV",texv); + } + if (tlsindex) + { + // in case of overlapping decompression, this hack is needed, + // because windoze zeroes the word pointed by tlsindex before + // it starts programs + if (tlsindex + 4 > s1addr) + patch_le32(loader,codesize,"TLSV",get_le32(obuf + tlsindex - s1addr - ic)); + else + patch_le32(loader,codesize,"TLSV",0); // bad guess + patch_le32(loader,codesize,"TLSA",tlsindex - rvamin); + } + if (icondir_count > 1) + { + if (icondir_count > 2) + patch_le16(loader,codesize,"DR",icondir_count - 1); + patch_le32(loader,codesize,"ICON",ncsection + icondir_offset - rvamin); + } + + const unsigned esi0 = s1addr + ic; + patch_le32(loader,codesize,"EDI0", 0u-esi0 + rvamin); + patch_le32(loader,codesize,"ESI0",esi0 + ih.imagebase); + ic = getLoaderSection("PEMAIN01") + 2 + upxsection; + + Reloc rel(1024); // new relocations are put here + rel.add(ic,3); + + putPackHeader(loader,lsize); + + // new PE header + memcpy(&oh,&ih,sizeof(oh)); + oh.filealign = 0x200; // identsplit depends on this + memset(osection,0,sizeof(osection)); + + oh.entry = upxsection; + oh.objects = HIGH(osection); + oh.chksum = 0; + + // fill the data directory + ODADDR(PEDIR_DEBUG) = 0; + ODSIZE(PEDIR_DEBUG) = 0; + ODADDR(PEDIR_IAT) = 0; + ODSIZE(PEDIR_IAT) = 0; + ODADDR(PEDIR_BOUNDIM) = 0; + ODSIZE(PEDIR_BOUNDIM) = 0; + + // tls is put into section 1 + + ic = s1addr + s1size - sotls; + processTls(&rel,&tlsiv,ic); + ODADDR(PEDIR_TLS) = sotls ? ic : 0; + ODSIZE(PEDIR_TLS) = sotls ? 0x18 : 0; + ic += sotls; + + // these are put into section 2 + + ic = ncsection; + if (soresources) + processResources(&res,ic); + ODADDR(PEDIR_RESOURCE) = soresources ? ic : 0; + ODSIZE(PEDIR_RESOURCE) = soresources; + ic += soresources; + processImports(ic); + ODADDR(PEDIR_IMPORT) = ic; + ODSIZE(PEDIR_IMPORT) = soimpdlls; + ic += soimpdlls; + processExports(&xport,ic); + ODADDR(PEDIR_EXPORT) = soexport ? ic : 0; + ODSIZE(PEDIR_EXPORT) = soexport; + if (!isdll && opt->w32pe.compress_exports) + { + ODADDR(PEDIR_EXPORT) = IDADDR(PEDIR_EXPORT); + ODSIZE(PEDIR_EXPORT) = IDSIZE(PEDIR_EXPORT); + } + ic += soexport; + processRelocs(&rel); + ODADDR(PEDIR_RELOC) = soxrelocs ? ic : 0; + ODSIZE(PEDIR_RELOC) = soxrelocs; + ic += soxrelocs; + + // this is here, because soxrelocs changes some lines above + const unsigned ncsize = soresources + soimpdlls + soexport + soxrelocs; + ic = oh.filealign - 1; + + // fill the sections + strcpy(osection[0].name,"UPX0"); + strcpy(osection[1].name,"UPX1"); + // after some windoze debugging I found that the name of the sections + // DOES matter :( .rsrc is used by oleaut32.dll (TYPELIBS) + // and because of this lame dll, the resource stuff must be the + // first in the 3rd section - the author of this dll seems to be + // too idiot to use the data directories... M$ suxx 4 ever! + // ... even worse: exploder.exe in NiceTry also depends on this to + // locate version info + + strcpy(osection[2].name,soresources ? ".rsrc" : "UPX2"); + + osection[0].vaddr = rvamin; + osection[1].vaddr = s1addr; + osection[2].vaddr = ncsection; + + osection[0].size = 0; + osection[1].size = (s1size + ic) &~ ic; + osection[2].size = (ncsize + ic) &~ ic; + + osection[0].vsize = osection[1].vaddr - osection[0].vaddr; + osection[1].vsize = (osection[1].size + oam1) &~ oam1; + osection[2].vsize = (osection[2].size + oam1) &~ oam1; + + osection[0].rawdataptr = (pe_offset + sizeof(oh) + sizeof(osection) + ic) &~ ic; + osection[1].rawdataptr = osection[0].rawdataptr; + osection[2].rawdataptr = osection[1].rawdataptr + osection[1].size; + + osection[0].flags = (unsigned) (PEFL_BSS|PEFL_EXEC|PEFL_WRITE|PEFL_READ); + osection[1].flags = (unsigned) (PEFL_DATA|PEFL_EXEC|PEFL_WRITE|PEFL_READ); + osection[2].flags = (unsigned) (PEFL_DATA|PEFL_WRITE|PEFL_READ); + + oh.imagesize = osection[2].vaddr + osection[2].vsize; + oh.bsssize = osection[0].vsize; + oh.datasize = osection[2].vsize; + oh.database = osection[2].vaddr; + oh.codesize = osection[1].vsize; + oh.codebase = osection[1].vaddr; + oh.headersize = osection[0].rawdataptr; + + if (opt->w32pe.strip_relocs && !isdll) + oh.flags |= RELOCS_STRIPPED; + + //for (ic = 0; ic < oh.filealign; ic += 4) + // set_le32(ibuf + ic,get_le32("UPX ")); + memset(ibuf,0,oh.filealign); + + infoHeader("[Writing compressed file]"); + + // write loader + compressed file + fo->write(&oh,sizeof(oh)); + fo->write(osection,sizeof(osection)); + // some alignment + if (identsplit == identsize) + fo->write(ibuf,osection[0].rawdataptr - fo->getBytesWritten() - identsize); + fo->write(loader + codesize,identsize); + infoWriting("loader", fo->getBytesWritten()); + fo->write(obuf,clen); + infoWriting("compressed data", clen); + fo->write(loader,codesize); + if ((ic = fo->getBytesWritten() & 3) != 0) + fo->write(ibuf,4 - ic); + fo->write(otls,sotls); + if ((ic = fo->getBytesWritten() & (oh.filealign-1)) != 0) + fo->write(ibuf,oh.filealign - ic); + fo->write(oresources,soresources); + fo->write(oimpdlls,soimpdlls); + fo->write(oexport,soexport); + fo->write(oxrelocs,soxrelocs); + + if ((ic = fo->getBytesWritten() & (oh.filealign-1)) != 0) + fo->write(ibuf,oh.filealign - ic); + + // copy the overlay + copyOverlay(fo, overlay, &obuf); + + // finally check compresion ratio + if (!checkCompressionRatio(fo->getBytesWritten(), file_size)) + throwNotCompressible(); +} + + +/************************************************************************* +// unpack +**************************************************************************/ + +bool PackW32Pe::canUnpack() +{ + if (!readFileHeader()) + return false; + + unsigned objs = ih.objects; + isection = new pe_section_t[objs]; + fi->seek(pe_offset+sizeof(ih),SEEK_SET); + fi->readx(isection,sizeof(pe_section_t)*objs); + if (ih.objects < 3 || memcmp(isection[0].name,"UPX",3)) + return false; + return super::readPackHeader(1024, isection[1].rawdataptr - 64) // current version + || super::readPackHeader(1024, isection[2].rawdataptr); // old versions +} + + +void PackW32Pe::rebuildImports(upx_byte *& extrainfo) +{ + if (ODADDR(PEDIR_IMPORT) == 0) + return; + + const upx_byte * const idata = obuf + get_le32(extrainfo); + const unsigned inamespos = get_le32(extrainfo + 4); + extrainfo += 8; + + unsigned sdllnames = 0; + + const upx_byte *import = ibuf + IDADDR(PEDIR_IMPORT) - isection[2].vaddr; + const upx_byte *p = idata; + + while (get_le32(p)) + { + sdllnames += strlen(get_le32(p) + import) + 1; + for (p += 8; *p;) + if (*p == 1) + p += strlen(++p) + 1; + else if (*p == 0xff) + p += 3; // ordinal + else + p += 5; + + p++; + } + sdllnames = ALIGN_UP(sdllnames,2); + + upx_byte * const Obuf = obuf - rvamin; + import_desc * const im0 = (import_desc*) (Obuf + ODADDR(PEDIR_IMPORT)); + import_desc *im = im0; + upx_byte *dllnames = Obuf + inamespos; + upx_byte *importednames = dllnames + sdllnames; + + for (p = idata; get_le32(p); p++) + { + // restore the name of the dll + const unsigned iatoffs = get_le32(p + 4) + rvamin; + if (inamespos) + { + // now I rebuild the dll names + im->dllname = ptr_diff(dllnames,Obuf); + strcpy(dllnames,get_le32(p) + import); + //;;;printf("\ndll: %s:",dllnames); + dllnames += strlen(dllnames) + 1; + } + else + strcpy(Obuf + im->dllname,get_le32(p) + import); + im->iat = iatoffs; + LE32 *newiat = (LE32 *) (Obuf + iatoffs); + + // restore the imported names+ordinals + for (p += 8; *p; newiat++) + if (*p == 1) + { + unsigned len = strlen(++p) + 1; + if (inamespos) + { + if (ptr_diff(importednames,oimpdlls) & 1) + importednames--; + memcpy(importednames + 2,p,len); + //;;;printf(" %s",importednames+2); + *newiat = ptr_diff(importednames,Obuf); + importednames += 2 + len; + } + else + strcpy(Obuf + *newiat + 2,p); + p += len; + } + else if (*p == 0xff) + { + *newiat = get_le16(p + 1) + 0x80000000; + //;;;printf(" %x",(unsigned)*newiat); + p += 3; + } + else + { + *newiat = get_le32(get_le32(p + 1) + import); + assert(*newiat & 0x80000000); + p += 5; + } + *newiat = 0; + im++; + } + //memset(idata,0,p - idata); +} + +void PackW32Pe::rebuildRelocs(upx_byte *& extrainfo) +{ + if (!ODADDR(PEDIR_RELOC) || !ODSIZE(PEDIR_RELOC) || (oh.flags & RELOCS_STRIPPED)) + return; + + if (ODSIZE(PEDIR_RELOC) == 8) // some tricky dlls use this + { + memcpy(obuf + ODADDR(PEDIR_RELOC) - rvamin, "\x0\x0\x0\x0\x8\x0\x0\x0", 8); + return; + } + + upx_byte *rdata = obuf + get_le32(extrainfo); + const upx_byte big = extrainfo[4]; + extrainfo += 5; + + upx_byte *p = rdata; + MemBuffer wrkmem; + unsigned relocn = unoptimizeReloc32(&rdata,obuf,&wrkmem,1); + unsigned r16 = 0; + if (big & 6) // 16 bit relocations + { + LE32 *q = (LE32*) rdata; + while (*q++) + r16++; + if ((big & 6) == 6) + while (*++q) + r16++; + } + Reloc rel(relocn + r16); + + if (big & 6) + { + LE32 *q = (LE32*) rdata; + while (*q) + rel.add(*q++ + rvamin,(big & 4) ? 2 : 1); + if ((big & 6) == 6) + while (*++q) + rel.add(*q + rvamin,1); + rdata = (upx_byte*) q; + } + + //memset(p,0,rdata - p); + + for (unsigned ic = 0; ic < relocn; ic++) + { + p = obuf + get_le32(wrkmem + 4 * ic); + set_le32(p,get_le32(p) + oh.imagebase + rvamin); + rel.add(rvamin + get_le32(wrkmem + 4 * ic),3); + } + rel.finish (oxrelocs,soxrelocs); + + if (opt->w32pe.strip_relocs && !isdll) + { + memset(obuf + ODADDR(PEDIR_RELOC) - rvamin,0,ODSIZE(PEDIR_RELOC)); + ODADDR(PEDIR_RELOC) = 0; + soxrelocs = 0; + // FIXME: try to remove the original relocation section somehow + } + else + memcpy (obuf + ODADDR(PEDIR_RELOC) - rvamin,oxrelocs,soxrelocs); + delete [] oxrelocs; oxrelocs = 0; + wrkmem.free(); + + ODSIZE(PEDIR_RELOC) = soxrelocs; +} + +void PackW32Pe::rebuildExports() +{ + if (ODSIZE(PEDIR_EXPORT) == 0 || ODADDR(PEDIR_EXPORT) == IDADDR(PEDIR_EXPORT)) + return; // nothing to do + + opt->w32pe.compress_exports = 0; + Export xport((char*)(unsigned char*) ibuf - isection[2].vaddr); + processExports(&xport); + processExports(&xport,ODADDR(PEDIR_EXPORT)); + memcpy(obuf + ODADDR(PEDIR_EXPORT) - rvamin,oexport,soexport); +} + +void PackW32Pe::rebuildTls() +{ + // this is an easy one : just do nothing ;-) +} + +void PackW32Pe::rebuildResources(upx_byte *& extrainfo) +{ + if (ODSIZE(PEDIR_RESOURCE) == 0) + return; + + icondir_count = get_le16(extrainfo); + extrainfo += 2; + + const unsigned vaddr = IDADDR(PEDIR_RESOURCE); + const upx_byte *r = ibuf - isection[2].vaddr; + Resource res(r + vaddr); + while (res.next()) + if (res.offs() > vaddr) + { + unsigned origoffs = get_le32(r + res.offs() - 4); + res.newoffs() = origoffs; + memcpy(obuf + origoffs - rvamin,r + res.offs(),res.size()); + if (icondir_count && res.itype() == 14) + { + set_le16(obuf + origoffs - rvamin + 4,icondir_count); + icondir_count = 0; + } + } + upx_byte *p = res.build(); + // write back when the original is zeroed + if (get_le32(obuf + ODADDR(PEDIR_RESOURCE) - rvamin + 12) == 0) + memcpy(obuf + ODADDR(PEDIR_RESOURCE) - rvamin,p,res.dirsize()); + delete [] p; +} + +void PackW32Pe::unpack(OutputFile *fo) +{ + //infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), objs); + + handleStub(fi,fo,pe_offset); + + const unsigned overlay = file_size - ALIGN_UP(isection[2].rawdataptr + isection[2].size,ih.filealign); + checkOverlay(overlay); + + ibuf.alloc(ph.c_len); + obuf.allocForUncompression(ph.u_len); + fi->seek(isection[1].rawdataptr - 64 + ph.buf_offset + ph.getPackHeaderSize(),SEEK_SET); + fi->readx(ibuf,ph.c_len); + + // decompress + decompress(ibuf,obuf); + upx_byte *extrainfo = obuf + get_le32(obuf + ph.u_len - 4); + //upx_byte * const eistart = extrainfo; + + memcpy(&oh, extrainfo, sizeof (oh)); + extrainfo += sizeof (oh); + unsigned objs = oh.objects; + + pe_section_t *osection = new pe_section_t[objs]; // FIXME: this might leak + memcpy(osection,extrainfo,sizeof(pe_section_t) * objs); + rvamin = osection[0].vaddr; + extrainfo += sizeof(pe_section_t) * objs; + + // read the noncompressed section + ibuf.free(); + ibuf.alloc(isection[2].size); + fi->seek(isection[2].rawdataptr,SEEK_SET); + fi->readx(ibuf,isection[2].size); + + // unfilter + if (ph.filter) + { + Filter ft(ph.level); + ft.init(ph.filter,oh.codebase - rvamin); + ft.cto = (unsigned char) ph.filter_cto; + ft.unfilter(obuf + oh.codebase - rvamin, oh.codesize); + } + + rebuildImports(extrainfo); + rebuildRelocs(extrainfo); + rebuildTls(); + rebuildExports(); + rebuildResources(extrainfo); + + //FIXME: this does bad things if the relocation section got removed + // during compression ... + //memset(eistart,0,extrainfo - eistart + 4); + + // fill the data directory + ODADDR(PEDIR_DEBUG) = 0; + ODSIZE(PEDIR_DEBUG) = 0; + ODADDR(PEDIR_IAT) = 0; + ODSIZE(PEDIR_IAT) = 0; + ODADDR(PEDIR_BOUNDIM) = 0; + ODSIZE(PEDIR_BOUNDIM) = 0; + //oh.headersize = osection[0].rawdataptr; + oh.headersize = ALIGN_UP(pe_offset + sizeof(oh) + sizeof(pe_section_t) * objs, oh.filealign); + oh.chksum = 0; + + // FIXME: ih.flags is checked here because of a bug in 0.92 + if ((opt->w32pe.strip_relocs && !isdll) || (ih.flags & RELOCS_STRIPPED)) + { + oh.flags |= RELOCS_STRIPPED; + ODADDR(PEDIR_RELOC) = 0; + ODSIZE(PEDIR_RELOC) = 0; + } + + // write decompressed file + if (fo) + { + ibuf.free(); + ibuf.alloc(osection[0].rawdataptr); + memset(ibuf,0,osection[0].rawdataptr); + infoHeader("[Writing uncompressed file]"); + + // write loader + compressed file + fo->write(&oh,sizeof(oh)); + fo->write(osection,objs * sizeof(pe_section_t)); + fo->write(ibuf,osection[0].rawdataptr - fo->getBytesWritten()); + for(unsigned ic = 0; ic < objs; ic++) + if (osection[ic].rawdataptr) + fo->write(obuf + osection[ic].vaddr - rvamin,ALIGN_UP(osection[ic].size,oh.filealign)); + copyOverlay(fo, overlay, &obuf); + } + delete [] osection; +} + +/* + extra info added to help uncompression: + + + + - optional \ + - opt / + - optional \ + - optional / + - optional + +*/ + +/* +vi:ts=4:et +*/ + diff --git a/src/p_w32pe.h b/src/p_w32pe.h new file mode 100644 index 0000000000..10f48c6327 --- /dev/null +++ b/src/p_w32pe.h @@ -0,0 +1,229 @@ +/* p_w32pe.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_W32PE_H +#define __UPX_P_W32PE_H + + +class PackW32Pe_Interval; +class PackW32Pe_Reloc; +class PackW32Pe_Resource; +class PackW32Pe_Export; + + +/************************************************************************* +// w32/pe +**************************************************************************/ + +class PackW32Pe : public Packer +{ + typedef Packer super; + +public: + PackW32Pe(InputFile *f); + ~PackW32Pe(); + virtual int getVersion() const { return 12; } + virtual int getFormat() const { return UPX_F_W32_PE; } + virtual const char *getName() const { return isrtm ? "rtm32/pe" : "win32/pe"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + + // unpacker capabilities + virtual bool canUnpackVersion(int version) const + { + return (version == 12); + } + +protected: + unsigned pe_offset; + bool isrtm; + bool readFileHeader(); + + unsigned processImports(); + void processImports(unsigned); + void rebuildImports(upx_byte *&); + upx_byte *oimport; + unsigned soimport; + upx_byte *oimpdlls; + unsigned soimpdlls; + + int processRelocs(); + void processRelocs(PackW32Pe_Reloc *); + void rebuildRelocs(upx_byte *&); + upx_byte *orelocs; + unsigned sorelocs; + upx_byte *oxrelocs; + unsigned soxrelocs; + + void processExports(PackW32Pe_Export *); + void processExports(PackW32Pe_Export *,unsigned); + void rebuildExports(); + upx_byte *oexport; + unsigned soexport; + + void processResources(PackW32Pe_Resource *); + void processResources(PackW32Pe_Resource *, unsigned); + void rebuildResources(upx_byte *&); + upx_byte *oresources; + unsigned soresources; + + void processTls(PackW32Pe_Interval *); + void processTls(PackW32Pe_Reloc *,const PackW32Pe_Interval *,unsigned); + void rebuildTls(); + upx_byte *otls; + unsigned sotls; + + unsigned stripDebug(unsigned); + + unsigned icondir_offset; + int icondir_count; + + bool importbyordinal; + bool kernel32ordinal; + unsigned tlsindex; + unsigned rvamin; + + struct pe_header_t + { + // 0x0 + char _[4]; // pemagic + LE16 cpu; + LE16 objects; + char __[12]; // timestamp + reserved + LE16 opthdrsize; + LE16 flags; + // optional header + char ___[4]; // coffmagic + linkerversion + LE32 codesize; + // 0x20 + LE32 datasize; + LE32 bsssize; + LE32 entry; + LE32 codebase; + // 0x30 + LE32 database; + // nt specific fields + LE32 imagebase; + LE32 objectalign; + LE32 filealign; // should set to 0x200 ? + // 0x40 + char ____[16]; // versions + // 0x50 + LE32 imagesize; + LE32 headersize; + LE32 chksum; // should set to 0 + LE16 subsystem; + LE16 dllflags; + // 0x60 + char _____[20]; // stack + heap sizes + // 0x74 + LE32 ddirsentries; // usually 16 + + struct ddirs_t + { + LE32 vaddr; + LE32 size; + } ddirs[16]; + } ih, oh; + + struct pe_section_t + { + char name[8]; + LE32 vsize; + LE32 vaddr; + LE32 size; + LE32 rawdataptr; + char _[12]; + LE32 flags; + } *isection; + + static unsigned virta2objnum (unsigned, pe_section_t *, unsigned); + unsigned tryremove (unsigned, unsigned); + + enum { + PEDIR_EXPORT = 0, + PEDIR_IMPORT = 1, + PEDIR_RESOURCE = 2, + PEDIR_EXCEPTION = 3, // Exception table + PEDIR_SEC = 4, // Certificate table (file pointer) + PEDIR_RELOC = 5, + PEDIR_DEBUG = 6, + PEDIR_COPYRIGHT = 7, // Architecture-specific data + PEDIR_GLOBALPTR = 8, // Global pointer + PEDIR_TLS = 9, + PEDIR_LOADCONF = 10, // Load Config Table + PEDIR_BOUNDIM = 11, + PEDIR_IAT = 12, + PEDIR_DELAYIMP = 13, // Delay Import Descriptor + PEDIR_COMRT = 14 // Com+ Runtime Header + }; + + enum { + PEFL_CODE = 0x20, + PEFL_DATA = 0x40, + PEFL_BSS = 0x80, + PEFL_INFO = 0x200, + PEFL_EXTRELS = 0x01000000, // extended relocations + PEFL_DISCARD = 0x02000000, + PEFL_NOCACHE = 0x04000000, + PEFL_NOPAGE = 0x08000000, + PEFL_SHARED = 0x10000000, + PEFL_EXEC = 0x20000000, + PEFL_READ = 0x40000000, + PEFL_WRITE = 0x80000000 + }; + + enum { + RELOCS_STRIPPED = 0x0001, + EXECUTABLE = 0x0002, + LNUM_STRIPPED = 0x0004, + LSYMS_STRIPPED = 0x0008, + AGGRESSIVE_TRIM = 0x0010, + TWO_GIGS_AWARE = 0x0020, + FLITTLE_ENDIAN = 0x0080, + BITS_32_MACHINE = 0x0100, + DEBUG_STRIPPED = 0x0200, + REMOVABLE_SWAP = 0x0400, + SYSTEM_PROGRAM = 0x1000, + DLL_FLAG = 0x2000, + FBIG_ENDIAN = 0x8000 + }; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ diff --git a/src/p_wcle.cpp b/src/p_wcle.cpp new file mode 100644 index 0000000000..c50a38ae63 --- /dev/null +++ b/src/p_wcle.cpp @@ -0,0 +1,827 @@ +/* p_wcle.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "filter.h" +#include "packer.h" +#include "lefile.h" +#include "p_wcle.h" + +static const +#include "stub/l_wcle.h" + +#define LEOF_READ (1<<0) +#define LEOF_WRITE (1<<1) +#define LEOF_EXEC (1<<2) +#define LEOF_PRELOAD (1<<6) +#define LEOF_HUGE32 (1<<13) + +#define IOT(x,y) iobject_table[x].y +#define OOT(x,y) oobject_table[x].y + +#define LE_STUB_EDI (1) + +#ifdef TESTING +# define dputc(x,y) do { if (opt->debug) putc(x,y); } while (0) +# define Opt_debug opt->debug +#else +# define dputc(x,y) ((void)0) +# define Opt_debug 0 +#endif + +#define my_base_address reserved +#define objects ih.object_table_entries +#define pages ih.memory_pages +#define mps ih.memory_page_size +#define opages oh.memory_pages + + +/************************************************************************* +// +**************************************************************************/ + +int PackWcle::getCompressionMethod() const +{ + if (M_IS_NRV2B(opt->method)) + return M_NRV2B_LE32; + if (M_IS_NRV2D(opt->method)) + return M_NRV2D_LE32; + return opt->level > 1 && file_size >= 512*1024 ? M_NRV2D_LE32 : M_NRV2B_LE32; +} + + +const int *PackWcle::getFilters() const +{ + static const int filters[] = { 0x26, 0x24, 0x14, 0x11, 0x16, 0x13, + 0x25, 0x12, 0x15, -1 }; + return filters; +} + + +/************************************************************************* +// util +**************************************************************************/ + +void PackWcle::handleStub(OutputFile *fo) +{ + if (fo && !opt->wcle.le) + Packer::handleStub(fi,fo,le_offset); +} + + +bool PackWcle::canPack() +{ + return LeFile::readFileHeader(); +} + + +/************************************************************************* +// +**************************************************************************/ + +// IDEA: as all the entries go into object #1, I could create bundles with 255 +// elements (of course I still have to handle empty bundles) + +void PackWcle::encodeEntryTable() +{ + unsigned count,object,n; + upx_byte *p = ientries; + n = 0; + while (*p) + { + count = *p; + n += count; + if (p[1] == 0) // unused bundle + p += 2; + else if (p[1] == 3) // 32-bit bundle + { + object = get_le16(p+2)-1; + set_le16(p+2,1); + p += 4; + for (; count; count--, p += 5) + set_le32(p+1,IOT(object,my_base_address) + get_le32(p+1)); + } + else + throwCantPack("unsupported bundle type in entry table"); + } + + //if (Opt_debug) printf("%d entries encoded.\n",n); + + soentries = p - ientries + 1; + oentries = ientries; + ientries = NULL; +} + + +void PackWcle::readObjectTable() +{ + LeFile::readObjectTable(); + + // temporary copy of the object descriptors + iobject_desc.alloc(objects*sizeof(*iobject_table)); + memcpy(iobject_desc,iobject_table,objects*sizeof(*iobject_table)); + + unsigned ic,jc,virtual_size; + + for (ic = jc = virtual_size = 0; ic < objects; ic++) + { + jc += IOT(ic,npages); + IOT(ic,my_base_address) = virtual_size; + virtual_size += (IOT(ic,virtual_size)+mps-1) &~ (mps-1); + } + if (pages != jc) + throwCantPack("bad page number"); +} + + +void PackWcle::encodeObjectTable() +{ + unsigned ic,jc; + + oobject_table = new le_object_table_entry_t[soobject_table = 2]; + memset(oobject_table,0,soobject_table * sizeof(*oobject_table)); + + // object #1: + OOT(0,base_address) = IOT(0,base_address); + + ic = IOT(objects-1,my_base_address)+IOT(objects-1,virtual_size); + jc = pages*mps+sofixups+1024; + if (ic < jc) + ic = jc; + + unsigned csection = (ic + overlapoh + mps-1) &~ (mps-1); + + OOT(0,virtual_size) = csection + mps; + OOT(0,flags) = LEOF_READ|LEOF_EXEC|LEOF_HUGE32|LEOF_PRELOAD; + OOT(0,pagemap_index) = 1; + OOT(0,npages) = opages; + + // object #2: stack + OOT(1,base_address) = (OOT(0,base_address) + +OOT(0,virtual_size)+mps-1) & ~(mps-1); + OOT(1,virtual_size) = mps; + OOT(1,flags) = LEOF_READ|LEOF_HUGE32|LEOF_WRITE; + OOT(1,pagemap_index) = 1; + + oh.init_cs_object = 1; + oh.init_eip_offset = neweip; + oh.init_ss_object = 2; + oh.init_esp_offset = OOT(1,virtual_size); +} + + +void PackWcle::encodePageMap() +{ + opm_entries = new le_pagemap_entry_t[sopm_entries = opages]; + for (unsigned ic = 0; ic < sopm_entries; ic++) + { + opm_entries[ic].l = (unsigned char) (ic+1); + opm_entries[ic].m = (unsigned char) ((ic+1)>>8); + opm_entries[ic].h = 0; + opm_entries[ic].type = 0; + } +} + + +void PackWcle::encodeFixupPageTable() +{ + unsigned ic; + ofpage_table = new unsigned[sofpage_table = 1 + opages]; + for (ofpage_table[0] = ic = 0; ic < opages; ic++) + set_le32(ofpage_table+ic+1,sofixups-FIXUP_EXTRA); +} + + +void PackWcle::encodeFixups() +{ + ofixups = new upx_byte[sofixups = 1*7 + FIXUP_EXTRA]; + memset(ofixups,0,sofixups); + ofixups[0] = 7; + set_le16(ofixups+2,(LE_STUB_EDI + neweip) & (mps-1)); + ofixups[4] = 1; +} + + +int PackWcle::preprocessFixups() +{ + unsigned ic,jc; + int big; + + MemBuffer counts_buf((objects+2)*sizeof(unsigned)); + unsigned *counts = (unsigned *) (unsigned char *) counts_buf; + countFixups(counts); + + for (ic = jc = 0; ic < objects; ic++) + jc += counts[ic]; + + MemBuffer rl(jc); + MemBuffer srf(counts[objects+0]+1); + MemBuffer slf(counts[objects+1]+1); + + upx_byte *selector_fixups = srf; + upx_byte *selfrel_fixups = slf; + + unsigned rc = 0; + + upx_byte *fix = ifixups; + for (ic = jc = 0; ic < pages; ic++) + { + while ((unsigned)(fix - ifixups) < get_le32(ifpage_table+ic+1)) + { + const short fixp2 = get_le16(fix+2); + unsigned value; + + switch (*fix) + { + case 2: // selector fixup + if (fixp2 < 0) + { + // cross page selector fixup + dputc('S',stdout); + fix += 5; + break; + } + dputc('s',stdout); + memcpy(selector_fixups,"\x8C\xCB\x66\x89\x9D",5); // mov bx, cs ; mov [xxx+ebp], bx + if (IOT(fix[4]-1,flags) & LEOF_WRITE) + selector_fixups[1] = 0xDB; // ds + set_le32(selector_fixups+5,jc+fixp2); + selector_fixups += 9; + fix += 5; + break; + case 5: // 16-bit offset + if ((unsigned)fixp2 < 4096 && IOT(fix[4]-1,my_base_address) == jc) + dputc('6',stdout); + else + throwCantPack("unsupported 16-bit offset relocation"); + fix += (fix[1] & 0x10) ? 9 : 7; + break; + case 6: // 16:32 pointer + if (fixp2 < 0) + { + // cross page pointer fixup + dputc('P',stdout); + fix += (fix[1] & 0x10) ? 9 : 7; + break; + } + dputc('p',stdout); + memcpy(iimage+jc+fixp2,fix+5,(fix[1] & 0x10) ? 4 : 2); + set_le32(rl+4*rc++,jc+fixp2); + set_le32(iimage+jc+fixp2,get_le32(iimage+jc+fixp2)+IOT(fix[4]-1,my_base_address)); + + memcpy(selector_fixups,"\x8C\xCA\x66\x89\x95",5); + if (IOT(fix[4]-1,flags) & LEOF_WRITE) + selector_fixups[1] = 0xDA; // ds + set_le32(selector_fixups+5,jc+fixp2+4); + selector_fixups += 9; + fix += (fix[1] & 0x10) ? 9 : 7; + break; + case 7: // 32-bit offset + if (fixp2 < 0) + { + fix += (fix[1] & 0x10) ? 9 : 7; + break; + } + //if (memcmp(iimage+jc+fixp2,fix+5,(fix[1] & 0x10) ? 4 : 2)) + // throwCantPack("illegal fixup offset"); + + // work around an pmwunlite bug: remove duplicated fixups + // FIXME: fix the other cases too + if (rc == 0 || get_le32(rl+4*rc-4) != jc+fixp2) + { + set_le32(rl+4*rc++,jc+fixp2); + set_le32(iimage+jc+fixp2,get_le32(iimage+jc+fixp2)+IOT(fix[4]-1,my_base_address)); + } + fix += (fix[1] & 0x10) ? 9 : 7; + break; + case 8: // 32-bit self relative fixup + if (fixp2 < 0) + { + // cross page self relative fixup + dputc('R',stdout); + fix += (fix[1] & 0x10) ? 9 : 7; + break; + } + value = get_le32(fix+5); + if (fix[1] == 0) + value &= 0xffff; + set_le32(iimage+jc+fixp2,(value+IOT(fix[4]-1,my_base_address))-jc-fixp2-4); + set_le32(selfrel_fixups,jc+fixp2); + selfrel_fixups += 4; + dputc('r',stdout); + fix += (fix[1] & 0x10) ? 9 : 7; + break; + default: + throwCantPack("unsupported fixup record"); + } + } + jc += mps; + } + + // resize ifixups if it's too small + if (sofixups < 1000) + { + delete[] ifixups; + ifixups = new upx_byte[1000]; + } + fix = optimizeReloc32 (rl,rc,ifixups,iimage,1,&big); + has_extra_code = srf != selector_fixups; + // FIXME: this could be removed if has_extra_code = false + // but then we'll need a flag + *selector_fixups++ = 0xC3; // ret + memcpy(fix,srf,selector_fixups-srf); // copy selector fixup code + fix += selector_fixups-srf; + + memcpy(fix,slf,selfrel_fixups-slf); // copy self-relative fixup positions + fix += selfrel_fixups-slf; + set_le32(fix,0xFFFFFFFFUL); + fix += 4; + + sofixups = fix-ifixups; + return big; +} + +#define RESERVED 0x1000 +void PackWcle::encodeImage(const Filter *ft) +{ + // concatenate image & preprocessed fixups + unsigned isize = soimage + sofixups; + ibuf.alloc(isize); + memcpy(ibuf,iimage,soimage); + memcpy(ibuf+soimage,ifixups,sofixups); + + delete[] ifixups; ifixups = NULL; + + // compress + oimage.allocForCompression(isize+RESERVED+512); + ph.filter = ft->id; + ph.filter_cto = ft->cto; + ph.u_len = isize; + // reserve RESERVED bytes for the decompressor + if (!compress(ibuf,oimage+RESERVED)) + throwNotCompressible(); + overlapoh = findOverlapOverhead(oimage+RESERVED, 512); + ibuf.free(); + soimage = (ph.c_len + 3) &~ 3; +} + + +void PackWcle::pack(OutputFile *fo) +{ + handleStub(fo); + + if (ih.byte_order || ih.word_order + || ih.exe_format_level + || ih.cpu_type < 2 || ih.cpu_type > 5 + || ih.target_os != 1 + || ih.module_type != 0x200 + || ih.object_iterate_data_map_offset + || ih.resource_entries + || ih.module_directives_entries + || ih.imported_modules_count + || ih.object_table_entries > 255) + throwCantPack("unexpected value in header"); + + readObjectTable(); + readPageMap(); + readResidentNames(); + readEntryTable(); + readFixupPageTable(); + readFixups(); + readImage(); + readNonResidentNames(); + + if (find_le32(iimage,20,get_le32("UPX "))) + throwAlreadyPacked(); + + if (ih.init_ss_object != objects) + throwCantPack("the stack is not in the last object"); + + int big = preprocessFixups(); + + const unsigned text_size = IOT(ih.init_cs_object-1,npages) * mps; + const unsigned text_vaddr = IOT(ih.init_cs_object-1,my_base_address); + + // filter + Filter ft(opt->level); + tryFilters(&ft, iimage+text_vaddr, text_size, text_vaddr); + const unsigned calltrickoffset = ft.cto << 24; + + // attach some useful data at the end of preprocessed fixups + unsigned ic = objects*sizeof(*iobject_table); + memcpy(ifixups+sofixups,iobject_desc,ic); + iobject_desc.free(); + + sofixups += ic; + set_le32(ifixups+sofixups,ih.init_esp_offset+IOT(ih.init_ss_object-1,my_base_address)); // old stack pointer + set_le32(ifixups+sofixups+4,ih.init_eip_offset+text_vaddr); // real entry point + set_le32(ifixups+sofixups+8,mps*pages); // virtual address of unpacked relocations + ifixups[sofixups+12] = (unsigned char) objects; + sofixups += 13; + + encodeImage(&ft); + + // verify filter + ft.verifyUnfilter(); + + // prepare loader + initLoader(nrv_loader,sizeof(nrv_loader)); + addLoader("IDENTSTR""WCLEMAIN""UPX1HEAD""WCLECUTP""+0000000", + getDecompressor(), + "WCLEMAI2", + NULL + ); + if (ft.id) + { + assert(ft.calls > 0); + addLoader(text_vaddr ? "WCCTTPOS" : "WCCTTNUL",NULL); + addFilter32(ft.id); + } +#if 1 + // FIXME: if (has_relocation) + { + addLoader("WCRELOC1""RELOC320", + big ? "REL32BIG" : "", + "RELOC32J", + NULL + ); + } +#endif + addLoader(has_extra_code ? "WCRELSEL" : "", + "WCLEMAI4", + NULL + ); + + const unsigned lsize = getLoaderSize(); + neweip = getLoaderSection("WCLEMAIN"); + int e_len = getLoaderSection("WCLECUTP"); + const unsigned d_len = lsize - e_len; + assert(e_len > 0); + + getLoader(); + + memcpy(oimage,getLoader(),e_len); + memmove(oimage+e_len,oimage+RESERVED,soimage); + soimage = (soimage + e_len); + + memcpy(oimage+soimage,getLoader() + e_len,d_len); + soimage += d_len; + + opages = (soimage+mps-1)/mps; + oh.bytes_on_last_page = soimage%mps; + + encodeObjectTable(); + encodeFixups(); + encodeFixupPageTable(); + encodePageMap(); + encodeEntryTable(); + + encodeResidentNames(); + encodeNonResidentNames(); + + // patch loader + ic = (OOT(0,virtual_size) - d_len) &~ 15; + assert(ic > ((ph.u_len + overlapoh + 31) &~ 15)); + + upx_byte *p = oimage+soimage-d_len; + patch_le32(p,d_len,"JMPO",ih.init_eip_offset+text_vaddr-(ic+d_len)); + patch_le32(p,d_len,"ESP0",ih.init_esp_offset+IOT(ih.init_ss_object-1,my_base_address)); + if (ft.id) + { + assert(ft.calls > 0); + if (ft.id > 0x20) + patch_le16(p,d_len,"??",'?'+(calltrickoffset>>16)); + patch_le32(p,d_len,"TEXL",(ft.id & 0xf) % 3 == 0 ? ft.calls : + ft.lastcall - ft.calls * 4); + if (text_vaddr) + patch_le32(p,d_len,"TEXV",text_vaddr); + } + patch_le32(p,d_len,"RELO",mps*pages); + + unsigned jpos = find_le32(oimage,e_len,get_le32("JMPD")) - oimage; + patch_le32(oimage,e_len,"JMPD",ic-jpos-4); + + jpos = (((ph.c_len+3)&~3) + d_len+3)/4; + patch_le32(oimage,e_len,"ECX0",jpos); + patch_le32(oimage,e_len,"EDI0",((ic+d_len+3)&~3)-4); + patch_le32(oimage,e_len,"ESI0",e_len+jpos*4-4); + putPackHeader(oimage,e_len); + + writeFile(fo, opt->wcle.le); + + // copy the overlay + const unsigned overlaystart = ih.data_pages_offset + exe_offset + + mps * (pages - 1) + ih.bytes_on_last_page; + const unsigned overlay = file_size - overlaystart - ih.non_resident_name_table_length; + checkOverlay(overlay); + copyOverlay(fo, overlay, &oimage); +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackWcle::decodeFixups() +{ + upx_byte *p = oimage + soimage; + + iimage.free(); + + MemBuffer tmpbuf; + unsigned fixupn = unoptimizeReloc32(&p,oimage,&tmpbuf,1); + + MemBuffer wrkmem(8*fixupn+8); + unsigned ic,jc,o,r; + for (ic=0; ic 0xFFFF ? 0x10 : 0); + fp += fp[1] ? 9 : 7; + selfrel_fixups += 4; + dputc('r',stdout); + } + // selector fixups + while (selectlen && (r = get_le32(selector_fixups+5))/mps == ic-1) + { + fp[0] = 2; + fp[1] = 0; + set_le16(fp+2,r & (mps-1)); + unsigned x = selector_fixups[1] > 0xD0 ? oh.init_ss_object : oh.init_cs_object; + fp[4] = (unsigned char) x; + fp += 5; + selector_fixups += 9; + selectlen--; + dputc('s',stdout); + } + // 32 bit offset fixups + while (get_le32(wrkmem+4*jc) < ic*mps) + { + if (ic > 1 && ((get_le32(wrkmem+4*(jc-2))+3) & (mps-1)) < 3) // cross page fixup? + { + r = get_le32(oimage+get_le32(wrkmem+4*(jc-2))); + fp[0] = 7; + fp[1] = (unsigned char) (r > 0xFFFF ? 0x10 : 0); + set_le16(fp+2,get_le32(wrkmem+4*(jc-2)) | ~3); + set_le32(fp+5,r); + o = soobject_table; + r = get_le32(wrkmem+4*(jc-1)); + virt2rela(oobject_table,&o,&r); + fp[4] = (unsigned char) o; + fp += fp[1] ? 9 : 7; + dputc('0',stdout); + } + o = soobject_table; + r = get_le32(wrkmem+4*(jc+1)); + virt2rela(oobject_table,&o,&r); + r = get_le32(oimage+get_le32(wrkmem+4*jc)); + fp[0] = 7; + fp[1] = (unsigned char) (r > 0xFFFF ? 0x10 : 0); + set_le16(fp+2,get_le32(wrkmem+4*jc) & (mps-1)); + fp[4] = (unsigned char) o; + set_le32(fp+5,r); + fp += fp[1] ? 9 : 7; + jc += 2; + } + set_le32(ofpage_table+ic,fp-ofixups); + } + for (ic=0; ic < FIXUP_EXTRA; ic++) + *fp++ = 0; + sofixups = fp-ofixups; +} + + +void PackWcle::decodeFixupPageTable() +{ + ofpage_table = new unsigned[sofpage_table = 1 + opages]; + set_le32(ofpage_table,0); + // the rest of ofpage_table is filled by decodeFixups() +} + + +void PackWcle::decodeObjectTable() +{ + soobject_table = oimage[ph.u_len - 1]; + oobject_table = new le_object_table_entry_t[soobject_table]; + unsigned jc, ic = soobject_table * sizeof(*oobject_table); + + const unsigned extradata = ph.version == 10 ? 17 : 13; + memcpy(oobject_table,oimage + ph.u_len - extradata - ic,ic); + + for (ic = jc = 0; ic < soobject_table; ic++) + { + OOT(ic,my_base_address) = jc; + jc += (OOT(ic,virtual_size)+mps-1) &~ (mps-1); + } + + // restore original cs:eip & ss:esp + ic = soobject_table; + jc = get_le32(oimage + ph.u_len - (ph.version < 11 ? 13 : 9)); + virt2rela(oobject_table,&ic,&jc); + oh.init_cs_object = ic; + oh.init_eip_offset = jc; + + ic = soobject_table; + if (ph.version < 10) + jc = ih.init_esp_offset; + else + jc = get_le32(oimage + ph.u_len - (ph.version == 10 ? 17 : 13)); + virt2rela(oobject_table,&ic,&jc); + oh.init_ss_object = ic; + oh.init_esp_offset = jc; +} + + +void PackWcle::decodeImage() +{ + oimage.allocForUncompression(ph.u_len); + + decompress(iimage + ph.buf_offset + ph.getPackHeaderSize(),oimage); + soimage = get_le32(oimage + ph.u_len - 5); + opages = soimage / mps; + oh.memory_page_size = mps; +} + + +void PackWcle::decodeEntryTable() +{ + unsigned count,object,n,r; + upx_byte *p = ientries; + n = 0; + while (*p) + { + count = *p; + n += count; + if (p[1] == 0) // unused bundle + p += 2; + else if (p[1] == 3) // 32-bit offset bundle + { + object = get_le16(p+2); + if (object != 1) + throwCantUnpack("corrupted entry found"); + object = soobject_table; + r = get_le32(p+5); + virt2rela(oobject_table,&object,&r); + set_le16(p+2,object--); + p += 4; + for (; count; count--, p += 5) + set_le32(p+1,get_le32(p+1) - OOT(object,my_base_address)); + } + else + throwCantUnpack("unsupported bundle type in entry table"); + } + + //if (Opt_debug) printf("\n%d entries decoded.\n",n); + + soentries = p - ientries + 1; + oentries = ientries; + ientries = NULL; +} + + +bool PackWcle::canUnpack() +{ + if (!LeFile::readFileHeader()) + return false; + // FIXME: 1024 could be too large for some files + return super::readPackHeader(1024, ih.data_pages_offset+exe_offset); +} + + +void PackWcle::virt2rela(const le_object_table_entry_t *entr,unsigned *objn,unsigned *addr) +{ + for (; *objn > 1; objn[0]--) + { + if (entr[*objn-1].my_base_address > *addr) + continue; + *addr -= entr[*objn-1].my_base_address; + break; + } +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackWcle::unpack(OutputFile *fo) +{ + handleStub(fo); + + readObjectTable(); + iobject_desc.free(); + readPageMap(); + readResidentNames(); + readEntryTable(); + readFixupPageTable(); + readFixups(); + readImage(); + readNonResidentNames(); + + decodeImage(); + decodeObjectTable(); + + // unfilter + if (ph.filter) + { + const unsigned text_size = OOT(oh.init_cs_object-1,npages) * mps; + const unsigned text_vaddr = OOT(oh.init_cs_object-1,my_base_address); + + Filter ft(ph.level); + ft.init(ph.filter, text_vaddr); + ft.cto = (unsigned char) (ph.version < 11 ? (get_le32(oimage+ph.u_len-9) >> 24) : ph.filter_cto); + ft.unfilter(oimage+text_vaddr, text_size); + } + + decodeFixupPageTable(); + decodeFixups(); + decodeEntryTable(); + decodePageMap(); + decodeResidentNames(); + decodeNonResidentNames(); + + for (unsigned ic = 0; ic < soobject_table; ic++) + OOT(ic,my_base_address) = 0; + + while (oimage[soimage-1] == 0) + soimage--; + oh.bytes_on_last_page = soimage % mps; + + // write decompressed file + if (fo) + writeFile(fo, opt->wcle.le); + + // copy the overlay + const unsigned overlaystart = ih.data_pages_offset + exe_offset + + mps * (pages - 1) + ih.bytes_on_last_page; + const unsigned overlay = file_size - overlaystart - ih.non_resident_name_table_length; + checkOverlay(overlay); + copyOverlay(fo, overlay, &oimage); +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/p_wcle.h b/src/p_wcle.h new file mode 100644 index 0000000000..342b8e8ddd --- /dev/null +++ b/src/p_wcle.h @@ -0,0 +1,92 @@ +/* p_wcle.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_P_WCLE_H +#define __UPX_P_WCLE_H + + +/************************************************************************* +// watcom/le +**************************************************************************/ + +class PackWcle : public Packer, public LeFile +{ + typedef Packer super; +public: + PackWcle(InputFile *f) : super(f), LeFile(f){}; + virtual int getVersion() const { return 11; } + virtual int getFormat() const { return UPX_F_WC_LE; } + virtual const char *getName() const { return "watcom/le"; } + virtual int getCompressionMethod() const; + virtual const int *getFilters() const; + + virtual void pack(OutputFile *fo); + virtual void unpack(OutputFile *fo); + + virtual bool canPack(); + virtual bool canUnpack(); + +protected: + virtual void handleStub(OutputFile *fo); + + virtual void readObjectTable(); + virtual void encodeObjectTable(); + virtual void decodeObjectTable(); + + virtual void encodeFixupPageTable(); + virtual void decodeFixupPageTable(); + + virtual void encodePageMap(); + + virtual void encodeEntryTable(); + virtual void decodeEntryTable(); + + virtual int preprocessFixups(); + virtual void encodeFixups(); + virtual void decodeFixups(); + + virtual void encodeImage(const Filter *ft); + virtual void decodeImage(); + + static void virt2rela(const le_object_table_entry_t *, unsigned *objn, unsigned *addr); + + // temporary copy of the object descriptors + MemBuffer iobject_desc; + + bool has_extra_code; + unsigned overlapoh; + unsigned neweip; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/packer.cpp b/src/packer.cpp new file mode 100644 index 0000000000..f2bd95906d --- /dev/null +++ b/src/packer.cpp @@ -0,0 +1,1193 @@ +/* packer.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +//#define WANT_STL +#include "conf.h" +#include "file.h" +#include "packer.h" +#include "filter.h" +#include "linker.h" +#include "ui.h" + + +/************************************************************************* +// +**************************************************************************/ + +Packer::Packer(InputFile *f) : + fi(f), file_size(-1), + uip(NULL), pass(0), total_passes(0), linker(NULL), + last_patch(NULL), last_patch_offset(0) +{ + file_size = f->st.st_size; + uip = new UiPacker(this); + memset(&ph,0,sizeof(ph)); +} + + +Packer::~Packer() +{ + delete uip; uip = NULL; + delete linker; linker = NULL; +} + + +/************************************************************************* +// public entries called from class PackMaster +**************************************************************************/ + +void Packer::doPack(OutputFile *fo) +{ + uip->uiPackStart(fo); + pack(fo); + uip->uiPackEnd(fo); +} + +void Packer::doUnpack(OutputFile *fo) +{ + uip->uiUnpackStart(fo); + unpack(fo); + uip->uiUnpackEnd(fo); +} + +void Packer::doTest() +{ + uip->uiTestStart(); + test(); + uip->uiTestEnd(); +} + +void Packer::doList() +{ + uip->uiListStart(); + list(); + uip->uiListEnd(); +} + +void Packer::doFileInfo() +{ + uip->uiFileInfoStart(); + fileInfo(); + uip->uiFileInfoEnd(); +} + + +/************************************************************************* +// default actions +**************************************************************************/ + +void Packer::test() +{ + unpack(NULL); +} + + +void Packer::list() +{ + uip->uiList(); +} + + +void Packer::fileInfo() +{ + // FIXME: subclasses should list their sections here + // We also should try to get a nice layout... +} + + +/************************************************************************* +// compress +**************************************************************************/ + +bool Packer::compress(upx_bytep in, upx_bytep out, + unsigned max_offset, unsigned max_match) +{ +#if defined(UNUPX) + throwInternalError("compression failed"); + return false; +#else + ph.c_len = 0; + + const int level = ph.level; + assert(level >= 1); assert(level <= 10); + + // update checksum of uncompressed data + unsigned saved_u_adler = ph.u_adler; + ph.u_adler = upx_adler32(ph.u_adler,in,ph.u_len); + + // set compression paramters + upx_compress_config_t conf; + memset(&conf, 0xff, sizeof(conf)); + // arguments + if (max_offset != 0) + conf.max_offset = max_offset; + if (max_match != 0) + conf.max_match = max_match; + // options + if (opt->crp.c_flags != -1) + conf.c_flags = opt->crp.c_flags; +#if 0 + else + // this is based on experimentation + conf.c_flags = (level >= 7) ? 0 : 1 | 2; +#endif + if (opt->crp.p_level != -1) + conf.p_level = opt->crp.p_level; + if (opt->crp.h_level != -1) + conf.h_level = opt->crp.h_level; + else if (level >= 7) + { + static const int h_level[9] = + { 0, 17, 18, 19, 20, 4369, 4626, 4883, 5140 }; + int ml = opt->mem_level; + if (ml < 0) + ml = 9; + if (ml < 1 || ml > 9) + ml = 1; + conf.h_level = h_level[ml - 1]; + } + if (opt->crp.max_offset != UPX_UINT_MAX && opt->crp.max_offset < conf.max_offset) + conf.max_offset = opt->crp.max_offset; + if (opt->crp.max_match != UPX_UINT_MAX && opt->crp.max_match < conf.max_match) + conf.max_match = opt->crp.max_match; + + // Avoid too many progress bar updates. 64 is s->bar_len in ui.cpp. + unsigned step = (ph.u_len < 64*1024) ? 0 : ph.u_len / 64; +#if defined(WITH_NRV) + if ((level >= 7) || (level >= 4 && ph.u_len >= 512*1024)) + step = 0; +#endif + if (this->pass >= 0) + this->pass++; + uip->startCallback(ph.u_len, step, this->pass, this->total_passes); + uip->firstCallback(); + + //OutputFile::dump("data.raw", in, ph.u_len); + + // compress + unsigned result[16]; + memset(result, 0, sizeof(result)); + int r = upx_compress(in, ph.u_len, out, &ph.c_len, + uip->getCallback(), + ph.method, level, &conf, result); + if (r == UPX_E_OUT_OF_MEMORY) + throwCantPack("out of memory"); + if (r != UPX_E_OK) + throwInternalError("compression failed"); + + //ph.min_offset_found = result[0]; + ph.max_offset_found = result[1]; + //ph.min_match_found = result[2]; + ph.max_match_found = result[3]; + //ph.min_run_found = result[4]; + ph.max_run_found = result[5]; + ph.first_offset_found = result[6]; + //ph.same_match_offsets_found = result[7]; + assert(max_offset == 0 || max_offset >= ph.max_offset_found); + assert(max_match == 0 || max_match >= ph.max_match_found); + + uip->endCallback(); + + //printf("Packer::compress: %d/%d: %7d -> %7d\n", ph.method, ph.level, ph.u_len, ph.c_len); + if (!checkCompressionRatio(ph.c_len, ph.u_len)) + return false; + // return in any case if not compressible + if (ph.c_len >= ph.u_len) + return false; + + // update checksum of compressed data + ph.c_adler = upx_adler32(ph.c_adler,out,ph.c_len); + // Decompress and verify. Skip this when using the fastest level. + if (level > 1) + { + // decompress + unsigned new_len = ph.u_len; + r = upx_decompress(out,ph.c_len,in,&new_len,ph.method); + //printf("%d %d: %d %d %d\n", ph.method, r, ph.c_len, ph.u_len, new_len); + if (r != UPX_E_OK || new_len != ph.u_len) + throwInternalError("decompression failed"); + + // verify decompression + if (ph.u_adler != upx_adler32(saved_u_adler,in,ph.u_len)) + throwInternalError("decompression failed (checksum error)"); + } + return true; +#endif /* UNUPX */ +} + + +bool Packer::checkCompressionRatio(unsigned c_len, unsigned u_len) const +{ +#if 1 + if (c_len >= u_len - u_len / 8) // min. 12.5% gain + return false; + if (c_len + 512 >= u_len) // min. 512 bytes gain + return false; +#else + if (c_len >= u_len) + return false; +#endif + return true; +} + + +/************************************************************************* +// +**************************************************************************/ + +void Packer::decompress(const upx_bytep in, upx_bytep out, + bool verify_checksum) +{ + unsigned adler; + + // verify checksum of compressed data + if (verify_checksum) + { + adler = upx_adler32(0,NULL,0); + adler = upx_adler32(adler,in,ph.c_len); + if (adler != ph.c_adler) + throwChecksumError(); + } + + // decompress + unsigned new_len = ph.u_len; + int r = upx_decompress(in,ph.c_len,out,&new_len,ph.method); + if (r != UPX_E_OK || new_len != ph.u_len) + throwCompressedDataViolation(); + + // verify checksum of decompressed data + if (verify_checksum) + { + adler = upx_adler32(0,NULL,0); + adler = upx_adler32(adler,out,ph.u_len); + if (adler != ph.u_adler) + throwChecksumError(); + } +} + + +/************************************************************************* +// +**************************************************************************/ + +bool Packer::testOverlappingDecompression(const upx_bytep buf, + unsigned overlap_overhead) const +{ +#if defined(UNUPX) + UNUSED(buf); + UNUSED(overlap_overhead); + return false; +#else + if (ph.c_len >= ph.u_len) + return false; + + assert((int)overlap_overhead >= 0); + // Because we are not using the asm_fast decompressor here + // we must account for extra 3 bytes or else we may fail + // at UPX decompression time. + if (overlap_overhead <= 4 + 3) // don't waste time here + return false; + overlap_overhead -= 3; + + unsigned src_off = ph.u_len + overlap_overhead - ph.c_len; + unsigned new_len = ph.u_len; + int r = upx_test_overlap(buf - src_off, src_off, + ph.c_len, &new_len, ph.method); + return (r == UPX_E_OK && new_len == ph.u_len); +#endif /* UNUPX */ +} + + +void Packer::verifyOverlappingDecompression(MemBuffer *buf, + unsigned overlap_overhead) +{ +#if defined(UNUPX) +#else + // FIXME (well, this is complete paranoia anyway) + // + // Idea: + // buf was allocated with MemBuffer.allocForCompression(), and + // its contents are no longer needed, i.e. the compressed data + // must have been already written. + // We now could performa a real overlapping decompression and + // verify the checksum. + // + // Note: + // This verify is just because of complete paranoia that there + // could be a hidden bug in the upx_test_overlap implementation, + // and it should not be necessary at all. + // + // See also: + // Filter::verifyUnfilter() +#endif + UNUSED(buf); + UNUSED(overlap_overhead); +} + + +/************************************************************************* +// Find overhead for in-place decompression in an heuristic way +// (using a binary search). Return 0 on error. +// +// To speed up things: +// - you can pass the range of an acceptable interval (so that +// we can succeed early) +// - you can enforce an upper_limit (so that we can fail early) +**************************************************************************/ + +unsigned Packer::findOverlapOverhead(const upx_bytep buf, + unsigned range, + unsigned upper_limit) const +{ +#if defined(UNUPX) + throwInternalError("not implemented"); + return 0; +#else + assert((int) range >= 0); + + // prepare to deal with very pessimistic values + unsigned low = 1; + unsigned high = UPX_MIN(ph.u_len / 4 + 512, upper_limit); + // but be optimistic for first try (speedup) + unsigned m = UPX_MIN(16, high); + // + unsigned overhead = 0; + unsigned nr = 0; // statistics + + while (high >= low) + { + assert(m >= low); assert(m <= high); + assert(m < overhead || overhead == 0); + nr++; + if (testOverlappingDecompression(buf, m)) + { + overhead = m; + // Succeed early if m lies in [low .. low+range-1], i.e. if + // if the range of the current interval is <= range. + //if (m <= low + range - 1) + if (m + 1 <= low + range) // avoid underflow + break; + high = m - 1; + } + else + low = m + 1; + m = (low + high) / 2; + } + + //printf("findOverlapOverhead: %d (%d tries)\n", overhead, nr); + if (overhead == 0) + throwInternalError("this is an oo bug"); + + UNUSED(nr); + return overhead; +#endif /* UNUPX */ +} + + +/************************************************************************* +// file io utils +**************************************************************************/ + +void Packer::handleStub(InputFile *fif, OutputFile *fo, long size) +{ + if (fo) + { + if (size > 0) + { + // copy stub from exe + info("Copying original stub: %ld bytes", size); + autoheap_array(char, stub, size); + fif->seek(0,SEEK_SET); + fif->readx(stub,size); + fo->write(stub,size); + } + else + { + // no stub + } + } +} + + +void Packer::checkOverlay(unsigned overlay) +{ + assert((int)overlay >= 0); + assert((off_t)overlay < file_size); + if (overlay == 0) + return; + info("Found overlay: %d bytes", overlay); + if (opt->overlay == opt->SKIP_OVERLAY) + throw OverlayException("file has overlay -- skipped; try `--overlay=copy'"); +} + + +void Packer::copyOverlay(OutputFile *fo, unsigned overlay, + MemBuffer *buf, + bool do_seek) +{ + assert((int)overlay >= 0); + assert((off_t)overlay < file_size); + if (!fo || overlay == 0) + return; + if (opt->overlay != opt->COPY_OVERLAY) + { + assert(opt->overlay == opt->STRIP_OVERLAY); + infoWarning("stripping overlay: %d bytes", overlay); + return; + } + info("Copying overlay: %d bytes", overlay); + if (do_seek) + fi->seek(-(long)overlay, SEEK_END); + + // get buffer size, align to improve i/o speed + unsigned buf_size = buf->getSize(); + if (buf_size >= 65536) + buf_size = ALIGN_DOWN(buf_size, 4096); + assert((int)buf_size > 0); + + do { + unsigned len = overlay < buf_size ? overlay : buf_size; + fi->readx(buf, len); + fo->write(buf, len); + overlay -= len; + } while (overlay > 0); +} + + +// Create a pseudo-unique program id. +unsigned Packer::getRandomId() const +{ + unsigned id = 0; +#if 0 && defined(__unix__) + // Don't consume precious bytes from /dev/urandom. + int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + fd = open("/dev/random", O_RDONLY); + if (fd >= 0) + { + if (read(fd, &id, 4) != 4) + id = 0; + close(fd); + } +#endif + while (id == 0) + { +#if !defined(HAVE_GETTIMEOFDAY) || defined(__DJGPP__) + id ^= (unsigned) time(NULL); + id ^= ((unsigned) clock()) << 12; +#else + struct timeval tv; + gettimeofday(&tv, 0); + id ^= (unsigned) tv.tv_sec; + id ^= ((unsigned) tv.tv_usec) << 12; // shift into high-bits +#endif +#if defined(HAVE_GETPID) + id ^= (unsigned) getpid(); +#endif + id ^= (unsigned) fi->st.st_ino; + id ^= (unsigned) fi->st.st_atime; + id ^= (unsigned) rand(); + } + return id; +} + + +/************************************************************************* +// packheader util +**************************************************************************/ + +// this is called directly after the constructor from class PackMaster +void Packer::initPackHeader() +{ + memset(&ph, 0, sizeof(ph)); + ph.magic = UPX_MAGIC_LE32; // "UPX!" + ph.version = getVersion(); + ph.format = getFormat(); + ph.method = -1; + ph.level = -1; + ph.c_adler = ph.u_adler = upx_adler32(0,NULL,0); + ph.buf_offset = -1; + ph.u_file_size = file_size; +} + +// this is called directly after canPack() from class PackMaster +void Packer::updatePackHeader() +{ + ph.method = getCompressionMethod(); + ph.level = getCompressionLevel(); + // update our local options + opt->method = ph.method; + opt->level = ph.level; +} + + +void Packer::putPackHeader(upx_bytep buf, unsigned len) +{ + assert(isValidFilter(ph.filter)); + ph.putPackHeader(buf, len); +} + + +bool Packer::readPackHeader(unsigned len, off_t seek_offset, upx_byte *buf) +{ + unsigned char hbuf[1024]; + if (buf == NULL) + { + assert(len <= sizeof(hbuf)); + buf = hbuf; + } + + if (seek_offset >= 0) + fi->seek(seek_offset, SEEK_SET); + len = fi->read(buf,len); + + if (!ph.fillPackHeader(buf, len)) + return false; + if (!ph.checkPackHeader(buf + ph.buf_offset, len - ph.buf_offset)) + return false; + + if (ph.version > getVersion()) + throwCantUnpack("need a newer version of UPX"); + // Some formats might be able to unpack old versions because + // their implementation hasn't changed. Ask them. + if (opt->cmd != CMD_FILEINFO) + if (!canUnpackVersion(ph.version)) + throwCantUnpack("I am not compatible with older versions of UPX"); + + if (ph.c_len >= ph.u_len || (off_t)ph.c_len >= file_size + || ph.version <= 0 || ph.version >= 0xff) + throwCantUnpack("header corrupted"); + else if ((off_t)ph.u_len > ph.u_file_size) + { +#if 0 + // FIXME: does this check make sense w.r.t. overlays ??? + if (ph.format == UPX_F_W32_PE || ph.format == UPX_F_DOS_EXE) + // may get longer + ((void)0); + else + throwCantUnpack("header size corrupted"); +#endif + } + if (ph.method < M_NRV2B_LE32 || ph.method > M_NRV2D_LE16) + throwCantUnpack("unknown compression method"); + + // Some formats might be able to unpack "subformats". Ask them. + if (!canUnpackFormat(ph.format)) + return false; + + return true; +} + + +/************************************************************************* +// patch util for loader +**************************************************************************/ + +void Packer::checkPatch(void *l, void *p, int size) +{ + if (l == NULL && p == NULL && size == 0) + { + // reset + last_patch = NULL; + last_patch_offset = 0; + return; + } + if (l == NULL || p == NULL || p < l || size <= 0) + throwBadLoader(); + long offset = (upx_bytep) p - (upx_bytep) l; + //printf("checkPatch: %p %5ld %d\n", l, offset, size); + if (l == last_patch) + { + if (offset + size > last_patch_offset) + throwInternalError("invalid patch order"); + } + else + last_patch = l; + last_patch_offset = offset; +} + + +void Packer::patch_be16(void *l, int llen, unsigned old, unsigned new_) +{ + void *p = find_be16(l,llen,old); + checkPatch(l,p,2); + set_be16(p,new_); +} + + +void Packer::patch_be16(void *l, int llen, const void * old, unsigned new_) +{ + void *p = find(l,llen,old,2); + checkPatch(l,p,2); + set_be16(p,new_); +} + + +void Packer::patch_be32(void *l, int llen, unsigned old, unsigned new_) +{ + void *p = find_be32(l,llen,old); + checkPatch(l,p,4); + set_be32(p,new_); +} + + +void Packer::patch_be32(void *l, int llen, const void * old, unsigned new_) +{ + void *p = find(l,llen,old,4); + checkPatch(l,p,4); + set_be32(p,new_); +} + + +void Packer::patch_le16(void *l, int llen, unsigned old, unsigned new_) +{ + void *p = find_le16(l,llen,old); + checkPatch(l,p,2); + set_le16(p,new_); +} + + +void Packer::patch_le16(void *l, int llen, const void * old, unsigned new_) +{ + void *p = find(l,llen,old,2); + checkPatch(l,p,2); + set_le16(p,new_); +} + + +void Packer::patch_le32(void *l, int llen, unsigned old, unsigned new_) +{ + void *p = find_le32(l,llen,old); + checkPatch(l,p,4); + set_le32(p,new_); +} + + +void Packer::patch_le32(void *l, int llen, const void * old, unsigned new_) +{ + void *p = find(l,llen,old,4); + checkPatch(l,p,4); + set_le32(p,new_); +} + + +// patch version into stub/ident_n.ash +void Packer::patchVersion(void *l, int llen) +{ + upx_byte *p = find(l,llen,"$Id: UPX UPXV ",14); + checkPatch(l,p,14); + unsigned char buf[4+1]; + memset(buf, ' ', 4); + size_t len = UPX_MIN(strlen(UPX_VERSION_STRING), 4); + memcpy(buf, UPX_VERSION_STRING, len); + memcpy(p + 9, buf, 4); +} + + +/************************************************************************* +// relocation util +**************************************************************************/ + +upx_byte *Packer::optimizeReloc32(upx_byte *in, unsigned relocnum, + upx_byte *out, upx_byte *image, + int bswap, int *big) +{ +#if defined(UNUPX) + return out; +#else + *big = 0; + if (relocnum == 0) + return out; + qsort(in,relocnum,4,le32_compare); + + unsigned jc,pc,oc; + upx_byte *fix = out; + + pc = (unsigned) -4; + for (jc = 0; jc>16)); + *fix++ = (unsigned char) oc; + *fix++ = (unsigned char) (oc>>8); + } + else + { + *big = 1; + *fix++ = 0xf0; + *fix++ = 0; + *fix++ = 0; + set_le32(fix,oc); + fix += 4; + } + pc += oc; + if (bswap) + set_be32(image+pc,get_le32(image+pc)); + } + *fix++ = 0; + return fix; +#endif /* UNUPX */ +} + + +unsigned Packer::unoptimizeReloc32(upx_byte **in, upx_byte *image, + MemBuffer *out, int bswap) +{ + upx_byte *p; + unsigned relocn = 0; + for (p = *in; *p; p++, relocn++) + if (*p >= 0xF0) + { + if (*p == 0xF0 && get_le16(p+1) == 0) + p += 4; + p += 2; + } + //fprintf(stderr,"relocnum=%x\n",relocn); + out->alloc(4*relocn+4); // one extra data + LE32 *outp = (LE32*) (unsigned char *) out; + LE32 *relocs = outp; + unsigned jc = (unsigned) -4; + for (p = *in; *p; p++) + { + if (*p < 0xF0) + jc += *p; + else + { + unsigned dif = (*p & 0x0F)*0x10000 + get_le16(p+1); + p += 2; + if (dif == 0) + { + dif = get_le32(p+1); + p += 4; + } + jc += dif; + } + *relocs++ = jc; + if (bswap && image) + set_be32(image+jc,get_le32(image+jc)); + } + //fprintf(stderr,"relocnum=%x\n",relocn); + *in = p+1; + return relocs - outp; +} + + +/************************************************************************* +// loader util +**************************************************************************/ + +void Packer::initLoader(const void *pdata, int plen, int pinfo) +{ + delete linker; + if (pinfo < 0) + pinfo = ~3 & (3 + get_le16(pdata, plen - 2)); + linker = new Linker(pdata,plen,pinfo); + + static const char identbig[] = + "\n\0" + "$Info: This file is packed with the UPX executable packer http://upx.tsx.org $" + "\n\0" + "$Id: packer.cpp,v 1.1 2000/05/10 04:58:23 jreiser Exp jreiser $" + "\n"; + + static const char identsmall[] = + "\n" + "$Id: packer.cpp,v 1.1 2000/05/10 04:58:23 jreiser Exp jreiser $" + "\n"; + + if (opt->small) + addSection("IDENTSTR",identsmall,sizeof(identsmall)); + else + addSection("IDENTSTR",identbig,sizeof(identbig)); +} + + +void Packer::addLoader(const char *s, ...) +{ + const char *p; + va_list ap; + + linker->addSection(s); + va_start(ap, s); + while((p = va_arg(ap, const char *)) != NULL) + linker->addSection(p); + va_end(ap); +} + +void Packer::addSection(const char *sname, const char *sdata, unsigned len) +{ + linker->addSection(sname,sdata,len); +} + +int Packer::getLoaderSection(const char *name, int *slen) +{ + return linker->getSection(name,slen); +} + + +const upx_byte *Packer::getLoader() const +{ + return (const upx_byte *) linker->getLoader(NULL); +} + + +int Packer::getLoaderSize() const +{ + int l; + (void) linker->getLoader(&l); + return l; +} + + +const char *Packer::getDecompressor() const +{ + static const char nrv2b_le32_small[] = + "N2BSMA10""N2BDEC10""N2BSMA20""N2BDEC20""N2BSMA30" + "N2BDEC30""N2BSMA40""N2BSMA50""N2BDEC50""N2BSMA60" + "N2BDEC60"; + + static const char nrv2b_le32_fast[] = + "N2BFAS10""+80CXXXX""N2BFAS11""N2BDEC10""N2BFAS20" + "N2BDEC20""N2BFAS30""N2BDEC30""N2BFAS40""N2BFAS50" + "N2BDEC50""N2BFAS60""+40CXXXX""N2BFAS61""N2BDEC60"; + + static const char nrv2d_le32_small[] = + "N2DSMA10""N2DDEC10""N2DSMA20""N2DDEC20""N2DSMA30" + "N2DDEC30""N2DSMA40""N2DSMA50""N2DDEC50""N2DSMA60" + "N2DDEC60"; + + static const char nrv2d_le32_fast[] = + "N2DFAS10""+80CXXXX""N2DFAS11""N2DDEC10""N2DFAS20" + "N2DDEC20""N2DFAS30""N2DDEC30""N2DFAS40""N2DFAS50" + "N2DDEC50""N2DFAS60""+40CXXXX""N2DFAS61""N2DDEC60"; + + if (ph.method == M_NRV2B_LE32) + return opt->small ? nrv2b_le32_small : nrv2b_le32_fast; + if (ph.method == M_NRV2D_LE32) + return opt->small ? nrv2d_le32_small : nrv2d_le32_fast; + return NULL; +} + + +void Packer::addFilter32(int filter_id) +{ + assert(filter_id > 0); + assert(isValidFilter(filter_id)); + + if ((filter_id & 0xf) % 3 == 0) + addLoader("CALLTR00", + (filter_id > 0x20) ? "CTCLEVE1" : "", + "CALLTR01", + (filter_id & 0xf) > 3 ? (filter_id > 0x20 ? "CTBSHR01""CTBSWA01" : "CTBROR01""CTBSWA01") : "", + "CALLTR02", + NULL + ); + else + addLoader("CALLTR10", + (filter_id & 0xf) % 3 == 1 ? "CALLTRE8" : "CALLTRE9", + "CALLTR11", + (filter_id > 0x20) ? "CTCLEVE2" : "", + "CALLTR12", + (filter_id & 0xf) > 3 ? (filter_id > 0x20 ? "CTBSHR11""CTBSWA11" : "CTBROR11""CTBSWA11") : "", + "CALLTR13", + NULL + ); +} + + +/************************************************************************* +// Try compression with several filters, choose the best or first one. +// Needs buildLoader(). +// +// Required inputs: +// this->ph +// ulen +// parm_ft +// clevel +// addvalue +// buf_len (optional) +// +// - updates this->ph +// - updates *ft +// - ibuf[] is restored to unfiltered version +// - obuf[] contains the best compressed version +// +// strategy: +// 0: try all filters, use best one +// n: try the first N filters in parm_filters[], use best one +// -1: try all filters, use first working one +// -2: try only the opt->filter filter +// +// This is prepared for generalization into class Packer so that +// opt->all_filters is available for all executable formats. +// +// It will replace the tryFilters() / compress() call sequence. +**************************************************************************/ + +void Packer::compressWithFilters(Filter *parm_ft, unsigned *parm_overlapoh, + const unsigned overlap_range, + int strategy, const int *parm_filters, + unsigned max_offset, unsigned max_match) +{ + const int *f; + // + const PackHeader orig_ph = this->ph; + PackHeader best_ph = this->ph; + // + const Filter orig_ft = *parm_ft; + Filter best_ft = *parm_ft; + // + const unsigned buf_len = orig_ph.u_len; + const unsigned filter_len = orig_ft.buf_len ? orig_ft.buf_len : buf_len; + // + best_ph.c_len = orig_ph.u_len; + unsigned best_ph_lsize = 0; + unsigned best_overlapoh = 0; + + // preconditions + assert(orig_ph.filter == 0); + assert(orig_ft.id == 0); + assert(filter_len <= buf_len); + + // setup raw_filters + static const int no_filters[] = { 0, -1 }; + int strategy_filters[] = { 0, -1 }; + + const int *raw_filters = parm_filters; + if (raw_filters == NULL) + raw_filters = getFilters(); + if (raw_filters == NULL) + raw_filters = no_filters; + if (strategy == -2) + { + assert(opt->filter >= 0); + strategy_filters[0] = opt->filter; + raw_filters = strategy_filters; + } + + // first pass - count number of filters + int raw_nfilters = 0; + for (f = raw_filters; *f >= 0; f++) + { + assert(isValidFilter(*f)); + raw_nfilters++; + } + + // copy filters, eliminate duplicates, add a 0 + int nfilters = 0; + bool zero_seen = false; + autoheap_array(int, filters, raw_nfilters + 2); + for (f = raw_filters; *f >= 0; f++) + { + if (*f == 0) + zero_seen = true; + bool duplicate = false; + for (int i = 0; i < nfilters; i++) + if (filters[i] == *f) + duplicate = true; + if (!duplicate) + filters[nfilters++] = *f; + } + if (!zero_seen) + filters[nfilters++] = 0; + filters[nfilters] = -1; + + // update + if (strategy < 0) + this->total_passes += 1; + else + { + if (strategy > nfilters) + nfilters = strategy; + this->total_passes += nfilters; + } + + // Working buffer for compressed data. Don't waste memory. + upx_byte *otemp = obuf; + MemBuffer otemp_buf; + if (nfilters > 1 && strategy >= 0) + { + otemp_buf.allocForCompression(buf_len); + otemp = otemp_buf; + } + + // compress + int nfilters_success = 0; + for (int i = 0; i < nfilters; i++) + { + // get fresh packheader + ph = orig_ph; + ph.filter = filters[i]; + // get fresh filter + Filter ft = orig_ft; + ft.init(filters[i], orig_ft.addvalue); + // filter + optimizeFilter(&ft, ibuf, filter_len); + bool success = ft.filter(ibuf, filter_len); + if (ft.id != 0 && ft.calls == 0) + { + // filter did not do anything - no need to call ft.unfilter() + success = false; + } + if (!success) + { + // filter failed or was usesless + if (strategy >= 0) + this->total_passes -= 1; + continue; + } + // filter success + nfilters_success++; + ph.filter_cto = ft.cto; + // compress + if (compress(ibuf, otemp, max_offset, max_match)) + { + // get results + const unsigned lsize = buildLoader(&ft); +#if 0 + printf("\n%02x: %d + %d = %d (best: %d + %d = %d)\n", ft.id, + ph.c_len, getLoaderSize(), ph.c_len + getLoaderSize(), + best_ph.c_len, best_ph_lsize, best_ph.c_len + best_ph_lsize); +#endif + if (ph.c_len + lsize < best_ph.c_len + best_ph_lsize) + { + // update obuf[] with best version + if (otemp != obuf) + memcpy(obuf, otemp, ph.c_len); + // save compression results + best_ph = ph; + best_ph_lsize = lsize; + best_ft = ft; + best_overlapoh = findOverlapOverhead(obuf, overlap_range); + } + } + // restore ibuf[] - unfilter with verify + ft.unfilter(ibuf, filter_len, true); + // + if (strategy < 0) + break; + } + + // postconditions + assert(nfilters_success > 0); + assert(best_ph.u_len == orig_ph.u_len); + assert(best_ph.filter == best_ft.id); + assert(best_ph.filter_cto == best_ft.cto); + + // copy back results + this->ph = best_ph; + *parm_ft = best_ft; + *parm_overlapoh = best_overlapoh; + + // finally check compression ratio + if (best_ph.c_len + best_ph_lsize >= best_ph.u_len) + throwNotCompressible(); + if (!checkCompressionRatio(best_ph.c_len, best_ph.u_len)) + throwNotCompressible(); + + // convenience + buildLoader(&best_ft); +} + + +/************************************************************************* +// filter util +**************************************************************************/ + +bool Packer::isValidFilter(int filter_id) const +{ + if (filter_id == 0) + return true; + for (const int *f = getFilters(); f && *f >= 0; f++) + { + if (*f == filter_id) + return true; + } + return false; +} + + +void Packer::tryFilters(Filter *ft, upx_byte *buf, unsigned buf_len, + unsigned addvalue) const +{ + // debug + //scanFilters(ft, buf, buf_len, addvalue); + + ft->init(); + if (opt->filter == 0) + return; + for (const int *f = getFilters(); f && *f >= 0; f++) + { + if (*f == 0) // skip no-filter + continue; + if (opt->filter < 0 || *f == opt->filter) + { + ft->init(*f, addvalue); + optimizeFilter(ft, buf, buf_len); + if (ft->filter(buf, buf_len) && ft->calls > 0) + break; // success + ft->init(); + } + } +} + + +void Packer::scanFilters(Filter *ft, const upx_byte *buf, unsigned buf_len, + unsigned addvalue) const +{ + ft->init(); + if (opt->filter == 0) + return; + for (const int *f = getFilters(); f && *f >= 0; f++) + { + if (*f == 0) // skip no-filter + continue; + ft->init(*f, addvalue); + //static const int pc[] = { 0xff, 0xfe, 0x80, 0x22, -1 }; + //ft->preferred_ctos = pc; + if (ft->scan(buf, buf_len)) + { + printf("scanFilters: id 0x%02x size: %6d: calls %5d/%5d/%3d, cto 0x%02x\n", + ft->id, ft->buf_len, ft->calls, ft->noncalls, ft->wrongcalls, ft->cto); + } + ft->init(); + } +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/packer.h b/src/packer.h new file mode 100644 index 0000000000..ebe7120423 --- /dev/null +++ b/src/packer.h @@ -0,0 +1,244 @@ +/* packer.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_PACKER_H +#define __UPX_PACKER_H + +#include "mem.h" + +class InputFile; +class OutputFile; +class Packer; +class PackMaster; +class UiPacker; +class Linker; +class Filter; + + +/************************************************************************* +// +**************************************************************************/ + +// see stub/header.ash +class PackHeader +{ +public: + bool fillPackHeader(upx_bytep buf, unsigned len); + bool checkPackHeader(const upx_bytep hbuf, int hlen) const; + void putPackHeader(upx_bytep buf, unsigned len); + int getPackHeaderSize() const; + +public: + // fields stored in compressed file + unsigned magic; // UPX_MAGIC_LE32 + int version; + int format; // executable format + int method; // compresison method + int level; // compresison level 1..10 + unsigned u_len; + unsigned c_len; + unsigned u_adler; + unsigned c_adler; + off_t u_file_size; + int filter; + int filter_cto; + int header_checksum; + + // info fields set by fillPackHeader() + long buf_offset; + + // info fields set by Packer::compress() + //unsigned min_offset_found; + unsigned max_offset_found; + //unsigned min_match_found; + unsigned max_match_found; + //unsigned min_run_found; + unsigned max_run_found; + unsigned first_offset_found; + //unsigned same_match_offsets_found; +}; + + +/************************************************************************* +// abstract base class for packers +**************************************************************************/ + +class Packer +{ + //friend class PackMaster; + friend class UiPacker; +protected: + Packer(InputFile *f); +public: + virtual ~Packer(); + + virtual int getVersion() const = 0; + // A unique integer ID for this executable format. See conf.h. + virtual int getFormat() const = 0; + virtual const char *getName() const = 0; + virtual int getCompressionMethod() const = 0; + virtual int getCompressionLevel() const { return opt->level; } + virtual const int *getFilters() const = 0; + + // PackMaster entries + void initPackHeader(); + void updatePackHeader(); + void doPack(OutputFile *fo); + void doUnpack(OutputFile *fo); + void doTest(); + void doList(); + void doFileInfo(); + + // unpacker capabilities + virtual bool canUnpackVersion(int version) const + { return (version >= 8); } + virtual bool canUnpackFormat(int format) const + { return (format == getFormat()); } + +protected: + virtual void pack(OutputFile *fo) = 0; + virtual void unpack(OutputFile *fo) = 0; + virtual void test(); + virtual void list(); + virtual void fileInfo(); + +public: + virtual bool canPack() = 0; + virtual bool canUnpack() = 0; + virtual bool canTest() { return canUnpack(); } + virtual bool canList() { return canUnpack(); } + +protected: + // main compression drivers + virtual bool compress(upx_bytep in, upx_bytep out, + unsigned max_offset = 0, unsigned max_match = 0); + virtual void decompress(const upx_bytep in, + upx_bytep out, bool verify_checksum=true); + virtual bool checkCompressionRatio(unsigned c_len, unsigned u_len) const; + + // high-level compression drivers + void compressWithFilters(Filter *ft, unsigned *overlapoh, + const unsigned overlap_range, + int strategy=-1, const int *filters=NULL, + unsigned max_offset = 0, unsigned max_match = 0); + + // util for verifying overlapping decompresion + // non-destructive test + bool testOverlappingDecompression(const upx_bytep buf, + unsigned overlap_overhead) const; + // non-destructive find + unsigned findOverlapOverhead(const upx_bytep buf, + unsigned range = 0, + unsigned upper_limit = ~0u) const; + // destructive decompress + verify + void verifyOverlappingDecompression(MemBuffer *buf, + unsigned overlap_overhead); + + + // packheader handling + virtual void putPackHeader(upx_byte *buf, unsigned len); + virtual bool readPackHeader(unsigned len, off_t seek_offset, + upx_byte *buf=NULL); + + // filter handling + virtual bool isValidFilter(int filter_id) const; + virtual void tryFilters(Filter *ft, upx_byte *buf, unsigned buf_len, + unsigned addvalue=0) const; + virtual void scanFilters(Filter *ft, const upx_byte *buf, unsigned buf_len, + unsigned addvalue=0) const; + virtual void optimizeFilter(Filter *, const upx_byte *, unsigned) const + { } + + // loader util + virtual int buildLoader(const Filter *) { return getLoaderSize(); } + virtual const upx_byte *getLoader() const; + virtual int getLoaderSize() const; + virtual void initLoader(const void *pdata, int plen, int pinfo=-1); + virtual void addLoader(const char *s, ...); + virtual void addSection(const char *sname, const char *sdata, unsigned len); + virtual int getLoaderSection(const char *name, int *slen = NULL); + virtual void addFilter32(int filter_id); + virtual const char *getDecompressor() const; + + // stub and overlay util + static void handleStub(InputFile *fi, OutputFile *fo, long size); + virtual void checkOverlay(unsigned overlay); + virtual void copyOverlay(OutputFile *fo, unsigned overlay, + MemBuffer *buf, bool do_seek=true); + + // misc util + virtual unsigned getRandomId() const; + + // patch util + void patch_be16(void *l, int llen, unsigned old, unsigned new_); + void patch_be16(void *l, int llen, const void * old, unsigned new_); + void patch_be32(void *l, int llen, unsigned old, unsigned new_); + void patch_be32(void *l, int llen, const void * old, unsigned new_); + void patch_le16(void *l, int llen, unsigned old, unsigned new_); + void patch_le16(void *l, int llen, const void * old, unsigned new_); + void patch_le32(void *l, int llen, unsigned old, unsigned new_); + void patch_le32(void *l, int llen, const void * old, unsigned new_); + void patchVersion(void *l, int llen); + void checkPatch(void *l, void *p, int size); + +protected: + // relocation util + virtual upx_byte *optimizeReloc32(upx_byte *in,unsigned relocnum,upx_byte *out,upx_byte *image,int bs,int *big); + virtual unsigned unoptimizeReloc32(upx_byte **in,upx_byte *image,MemBuffer *out,int bs); + + +protected: + InputFile *fi; + off_t file_size; // will get set by constructor + PackHeader ph; // must be filled by canUnpack() + + // compression buffers + MemBuffer ibuf; // input + MemBuffer obuf; // output + + // UI handler + UiPacker *uip; + int pass; + int total_passes; + + // linker + Linker *linker; + +private: + // private to checkPatch() + void *last_patch; + long last_patch_offset; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/packhead.cpp b/src/packhead.cpp new file mode 100644 index 0000000000..08f7443645 --- /dev/null +++ b/src/packhead.cpp @@ -0,0 +1,256 @@ +/* packhead.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "packer.h" + + +/************************************************************************* +// packheader +// +// We try to be able to unpack UPX 0.7x (versions 8 & 9) and at +// least to detect older versions, so this is a little bit messy. +**************************************************************************/ + +// simple checksum for the header itself (since version 10) +static unsigned char get_packheader_checksum(const upx_bytep buf, int len) +{ + assert(get_le32(buf) == UPX_MAGIC_LE32); + //printf("1 %d\n", len); + buf += 4; + len -= 4; + unsigned c = 0; + while (len-- > 0) + c += *buf++; + c %= 251; + //printf("2 %d\n", c); + return (unsigned char) c; +} + + +/************************************************************************* +// +**************************************************************************/ + +static int get_packheader_size(int version, int format) +{ + int n = 0; + if (version <= 3) + n = 24; + else if (version <= 9) + { + if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) + n = 20; + else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH) + n = 25; + else + n = 28; + } + else + { + if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) + n = 22; + else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH) + n = 27; + else + n = 32; + } + if (n == 0) + throwCantUnpack("unknown header version"); + return n; +} + + +int PackHeader::getPackHeaderSize() const +{ + return get_packheader_size(version, format); +} + + +/************************************************************************* +// +**************************************************************************/ + +void PackHeader::putPackHeader(upx_bytep buf, unsigned len) +{ +#if defined(UNUPX) + throwBadLoader(); +#else + upx_bytep l = find_le32(buf,len,magic); + if (l == 0) + throwBadLoader(); + + l[4] = (unsigned char) version; + l[5] = (unsigned char) format; + l[6] = (unsigned char) method; + l[7] = (unsigned char) level; + + // the new variable length header + if (format < 128) + { + set_le32(l+8,u_adler); + set_le32(l+12,c_adler); + if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) + { + set_le16(l+16,u_len); + set_le16(l+18,c_len); + l[20] = (unsigned char) filter; + } + else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH) + { + set_le24(l+16,u_len); + set_le24(l+19,c_len); + set_le24(l+22,u_file_size); + l[25] = (unsigned char) filter; + } + else + { + set_le32(l+16,u_len); + set_le32(l+20,c_len); + set_le32(l+24,u_file_size); + l[28] = (unsigned char) filter; + l[29] = (unsigned char) filter_cto; + l[30] = 0; + } + } + else + { + set_be32(l+8,u_len); + set_be32(l+12,c_len); + set_be32(l+16,u_adler); + set_be32(l+20,c_adler); + set_be32(l+24,u_file_size); + l[28] = (unsigned char) filter; + l[29] = (unsigned char) filter_cto; + l[30] = 0; + } + + // store header_checksum + const int hs = getPackHeaderSize(); + l[hs - 1] = get_packheader_checksum(l, hs - 1); +#endif /* UNUPX */ +} + + +/************************************************************************* +// +**************************************************************************/ + +bool PackHeader::fillPackHeader(upx_bytep buf, unsigned len) +{ + upx_bytep l = find_le32(buf,len,magic); + if (l == 0) + return false; + buf_offset = l - buf; + + version = l[4]; + format = l[5]; + method = l[6]; + level = l[7]; + filter_cto = 0; + + // the new variable length header + int off_filter = 0; + if (format < 128) + { + u_adler = get_le32(l+8); + c_adler = get_le32(l+12); + if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) + { + u_len = get_le16(l+16); + c_len = get_le16(l+18); + u_file_size = u_len; + off_filter = 20; + } + else if (format == UPX_F_DOS_EXE || format == UPX_F_DOS_EXEH) + { + u_len = get_le24(l+16); + c_len = get_le24(l+19); + u_file_size = get_le24(l+22); + off_filter = 25; + } + else + { + u_len = get_le32(l+16); + c_len = get_le32(l+20); + u_file_size = get_le32(l+24); + off_filter = 28; + filter_cto = l[29]; + } + } + else + { + u_len = get_be32(l+8); + c_len = get_be32(l+12); + u_adler = get_be32(l+16); + c_adler = get_be32(l+20); + u_file_size = get_be32(l+24); + off_filter = 28; + filter_cto = l[29]; + } + + if (version >= 10) + filter = l[off_filter]; + else if ((level & 128) == 0) + filter = 0; + else + { + // convert old flags to new filter id + level &= 127; + if (format == UPX_F_DOS_COM || format == UPX_F_DOS_SYS) + filter = 0x06; + else + filter = 0x26; + } + level &= 15; + + return true; +} + + +bool PackHeader::checkPackHeader(const upx_bytep hbuf, int hlen) const +{ + if (version == 0xff) + throwCantUnpack("cannot unpack UPX ;-)"); + + const int hs = getPackHeaderSize(); + if (hlen <= 0 || hs > hlen) + throwCantUnpack("header corrupted"); + + // check header_checksum + if (version > 9) + if (hbuf[hs - 1] != get_packheader_checksum(hbuf, hs - 1)) + throwCantUnpack("header corrupted"); + + return true; +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/packmast.cpp b/src/packmast.cpp new file mode 100644 index 0000000000..765a80bc22 --- /dev/null +++ b/src/packmast.cpp @@ -0,0 +1,258 @@ +/* packmast.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "packmast.h" +#include "packer.h" +#include "lefile.h" +#include "p_com.h" +#include "p_djgpp2.h" +#include "p_exe.h" +#include "p_elf.h" +#include "p_unix.h" +#include "p_lx_elf.h" +#include "p_lx_sep.h" +#include "p_lx_sh.h" +#include "p_sys.h" +#include "p_tos.h" +#include "p_wcle.h" +#include "p_tmt.h" +#include "p_vxd.h" +#include "p_w32pe.h" + + +/************************************************************************* +// +**************************************************************************/ + +PackMaster::PackMaster(InputFile *f, struct options_t *o) : + fi(f), p(NULL) +{ + // replace options with local options + saved_opt = o; + if (o) + { + this->local_options = *o; // struct copy + opt = &this->local_options; + } +} + + +PackMaster::~PackMaster() +{ + fi = NULL; + delete p; p = NULL; + // restore options + if (saved_opt) + opt = saved_opt; + saved_opt = NULL; +} + + +/************************************************************************* +// +**************************************************************************/ + +typedef Packer* (*try_function)(Packer *p, InputFile *f); + +static Packer* try_pack(Packer *p, InputFile *f) +{ + if (p == NULL) + return NULL; +#if !defined(UNUPX) + try { + p->initPackHeader(); + f->seek(0,SEEK_SET); + if (p->canPack()) + { + p->updatePackHeader(); + f->seek(0,SEEK_SET); + return p; + } + } catch (IOException&) { + } catch (...) { + delete p; + throw; + } +#endif /* UNUPX */ + delete p; + return NULL; +} + + +static Packer* try_unpack(Packer *p, InputFile *f) +{ + if (p == NULL) + return NULL; + try { + p->initPackHeader(); + f->seek(0,SEEK_SET); + if (p->canUnpack()) + { + f->seek(0,SEEK_SET); + return p; + } + } catch (IOException&) { + } catch (...) { + delete p; + throw; + } + delete p; + return NULL; +} + + +/************************************************************************* +// +**************************************************************************/ + +static Packer* try_packers(InputFile *f, try_function func) +{ + Packer *p = NULL; + + // note: order of tries is important ! + if (!opt->dos.force_stub) + { + if ((p = func(new PackDjgpp2(f),f)) != NULL) + return p; + if ((p = func(new PackTmt(f),f)) != NULL) + return p; + if ((p = func(new PackWcle(f),f)) != NULL) + return p; +#if 0 + if ((p = func(new PackVxd(f),f)) != NULL) + return p; +#endif + if ((p = func(new PackW32Pe(f),f)) != NULL) + return p; + } + if ((p = func(new PackExe(f),f)) != NULL) + return p; + if ((p = func(new PackTos(f),f)) != NULL) + return p; + if (opt->script_name) { + if ((p = func(new PackLinuxI386sep(f),f)) != NULL) + return p; + } + if ((p = func(new PackLinuxI386elf(f),f)) != NULL) + return p; + if ((p = func(new PackLinuxI386sh(f),f)) != NULL) + return p; + if ((p = func(new PackBvmlinuxI386(f),f)) != NULL) + return p; + if ((p = func(new PackLinuxI386(f),f)) != NULL) + return p; + if ((p = func(new PackSys(f),f)) != NULL) + return p; + if ((p = func(new PackCom(f),f)) != NULL) + return p; + return NULL; +} + + +static Packer *getPacker(InputFile *f) +{ + Packer *p = try_packers(f, try_pack); + if (!p) + throwUnknownExecutableFormat(); + return p; +} + + +static Packer *getUnpacker(InputFile *f) +{ + Packer *p = try_packers(f, try_unpack); + if (!p) + throwNotPacked(); + return p; +} + + +static void assertPacker(const Packer *p) +{ + assert(strlen(p->getName()) <= 13); +} + + +/************************************************************************* +// delegation +**************************************************************************/ + +void PackMaster::pack(OutputFile *fo) +{ + p = getPacker(fi); + assertPacker(p); + fi = NULL; + p->doPack(fo); +} + + +void PackMaster::unpack(OutputFile *fo) +{ + p = getUnpacker(fi); + assertPacker(p); + fi = NULL; + p->doUnpack(fo); +} + + +void PackMaster::test() +{ + p = getUnpacker(fi); + assertPacker(p); + fi = NULL; + p->doTest(); +} + + +void PackMaster::list() +{ + p = getUnpacker(fi); + assertPacker(p); + fi = NULL; + p->doList(); +} + + +void PackMaster::fileInfo() +{ + p = try_packers(fi, try_unpack); + if (!p) + p = try_packers(fi, try_pack); + if (!p) + throwUnknownExecutableFormat(NULL, 1); // make a warning here + assertPacker(p); + fi = NULL; + p->doFileInfo(); +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/packmast.h b/src/packmast.h new file mode 100644 index 0000000000..3b76fb3f5c --- /dev/null +++ b/src/packmast.h @@ -0,0 +1,68 @@ +/* packmast.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_PACKMASTER_H +#define __UPX_PACKMASTER_H + +class Packer; +class InputFile; +class OutputFile; + + +/************************************************************************* +// interface for work.cpp +**************************************************************************/ + +class PackMaster +{ +public: + PackMaster(InputFile *f, struct options_t *o = NULL); + virtual ~PackMaster(); + + void pack(OutputFile *fo); + void unpack(OutputFile *fo); + void test(); + void list(); + void fileInfo(); + +private: + InputFile *fi; + Packer *p; + + // setup local options for each file + struct options_t local_options; + struct options_t *saved_opt; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/s_djgpp2.cpp b/src/s_djgpp2.cpp new file mode 100644 index 0000000000..dba60464c8 --- /dev/null +++ b/src/s_djgpp2.cpp @@ -0,0 +1,546 @@ +/* s_djgpp2.cpp -- djggp2 DOS screen driver + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(USE_SCREEN) && defined(__DJGPP__) + +#include "screen.h" + +#define this local_this + +#define mask_fg 0x0f +#define mask_bg 0xf0 + +/* #define USE_SCROLLBACK */ + + +/************************************************************************* +// direct screen access +**************************************************************************/ + +#include +#if 0 +#include +#endif +#include +#include +#include +#include +#include +#define dossel _go32_info_block.selector_for_linear_memory +#define co80 _go32_info_block.linear_address_of_primary_screen +#undef kbhit + + +struct screen_data_t +{ + int mode; + int cols; + int rows; + int cursor_x; + int cursor_y; + unsigned char attr; + unsigned char init_attr; + unsigned char empty_attr; + unsigned short empty_cell; +#ifdef USE_SCROLLBACK + /* scrollback buffer */ + unsigned short sb_buf[32][256]; + int sb_size; + int sb_base; + int sb_sp; +#endif /* USE_SCROLLBACK */ +}; + + +/* atExit information */ +static struct +{ + int cursor_shape; +} ae = { + -1 +}; + + +#ifdef USE_SCROLLBACK +static __inline__ void sb_add(screen_t *this, int *val, int inc) +{ + *val = (*val + inc) & (this->data->sb_size - 1); +} + +static void sb_push(screen_t *this, const unsigned short *line, int len) +{ + memcpy(this->data->sb_buf[this->data->sb_sp],line,len); + sb_add(this,&this->data->sb_sp,1); + if (this->data->sb_sp == this->data->sb_base) + sb_add(this,&this->data->sb_base,1); +} + +static const unsigned short *sb_pop(screen_t *this) +{ + if (this->data->sb_sp == this->data->sb_base) + return NULL; + sb_add(this,&this->data->sb_sp,-1); + return this->data->sb_buf[this->data->sb_sp]; +} +#endif /* USE_SCROLLBACK */ + + +static void refresh(screen_t *this) +{ + UNUSED(this); +} + + +static __inline__ +unsigned short make_cell(screen_t *this, int ch, int attr) +{ + UNUSED(this); + return ((attr & 0xff) << 8) | (ch & 0xff); +} + + +static int getMode(const screen_t *this) +{ + UNUSED(this); + return ScreenMode(); +} + + +static int getPage(const screen_t *this) +{ + UNUSED(this); + return _farpeekb(dossel, 0x462); +} + + +static int getRows(const screen_t *this) +{ + return this->data->rows; +} + + +static int getCols(const screen_t *this) +{ + return this->data->cols; +} + + +static int isMono(const screen_t *this) +{ + if (this->data->mode == 7) + return 1; + if ((_farpeekb(dossel, 0x465) & (4 | 16)) != 0) + return 1; + return 0; +} + + +static int getFg(const screen_t *this) +{ + return this->data->attr & mask_fg; +} + + +static int getBg(const screen_t *this) +{ + return this->data->attr & mask_bg; +} + + +static void setFg(screen_t *this, int fg) +{ + this->data->attr = (this->data->attr & mask_bg) | (fg & mask_fg); +} + + +static void setBg(screen_t *this, int bg) +{ + this->data->attr = (this->data->attr & mask_fg) | (bg & mask_bg); +} + + +static void setCursor(screen_t *this, int x, int y) +{ + if (x >= 0 && y >= 0 && x < this->data->cols && y < this->data->rows) + { + ScreenSetCursor(y,x); + this->data->cursor_x = x; + this->data->cursor_y = y; + } +} + + +// I added ScreenGetCursor, because when upx prints something longer than +// 1 line (an error message for example), the this->data->cursor_y can +// have a bad value - ml1050 + +// FIXME: +// Laszlo: when does this happen ? This probably indicates a +// bug in c_screen.cpp(print0) I've introduced with +// the 2 passes implementation. + +static void getCursor(const screen_t *this, int *x, int *y) +{ + int cx = this->data->cursor_x; + int cy = this->data->cursor_y; +#if 1 + ScreenGetCursor(&cy,&cx); +#endif + if (x) *x = cx; + if (y) *y = cy; +} + + +static void putCharAttr(screen_t *this, int ch, int attr, int x, int y) +{ + UNUSED(this); + ScreenPutChar(ch,attr,x,y); +} + + +static void putChar(screen_t *this, int ch, int x, int y) +{ + ScreenPutChar(ch,this->data->attr,x,y); +} + + +static void putStringAttr(screen_t *this, const char *s, int attr, int x, int y) +{ + UNUSED(this); + assert((int)strlen(s) <= 256); + assert(x + (int)strlen(s) <= this->data->cols); + ScreenPutString(s,attr,x,y); +} + + +static void putString(screen_t *this, const char *s, int x, int y) +{ + assert((int)strlen(s) <= 256); + assert(x + (int)strlen(s) <= this->data->cols); + ScreenPutString(s,this->data->attr,x,y); +} + + +/* private */ +static void getChar(screen_t *this, int *ch, int *attr, int x, int y) +{ + UNUSED(this); + ScreenGetChar(ch,attr,x,y); +} + + +static int getCursorShape(const screen_t *this) +{ + UNUSED(this); + return _farpeekw(dossel, 0x460); +} + + +static void setCursorShape(screen_t *this, int shape) +{ + __dpmi_regs r; + + memset(&r,0,sizeof(r)); /* just in case... */ + r.x.ax = 0x0103; +#if 1 + if (this) + r.h.al = getMode(this); /* required for buggy BIOSes */ +#endif + r.x.cx = shape & 0x7f1f; + __dpmi_int(0x10, &r); +} + + +static int hideCursor(screen_t *this) +{ + int shape = getCursorShape(this); + setCursorShape(this,0x2000); + return shape; +} + + +static int init(screen_t *this, int fd) +{ + int mode; + int cols, rows; + int attr; + +#if 0 + /* force linkage of conio.o */ + (void) _conio_kbhit(); +#endif + + if (!this || !this->data) + return -1; + + this->data->mode = -1; +#ifdef USE_SCROLLBACK + this->data->sb_size = 32; + this->data->sb_base = 0; + this->data->sb_sp = 0; +#endif + if (fd < 0 || !isatty(fd)) + return -1; + if (getPage(this) != 0) + return -1; + + cols = ScreenCols(); + rows = ScreenRows(); + mode = getMode(this); + if (mode > 0x13) + { + /* assume this is some SVGA/VESA text mode */ + __dpmi_regs r; + + memset(&r,0,sizeof(r)); /* just in case... */ + r.x.ax = 0x4f03; /* VESA - get current video mode */ + __dpmi_int(0x10, &r); + if (r.h.ah == 0) + mode = r.x.bx; + } + else + { + if (mode != 2 && mode != 3 && mode != 7) + return -1; + } + ScreenGetCursor(&this->data->cursor_y,&this->data->cursor_x); + getChar(this,NULL,&attr,this->data->cursor_x,this->data->cursor_y); + this->data->init_attr = attr; + if (mode != 7) + { + /* Does it normally blink when bg has its 3rd bit set? */ + int b_mask = (_farpeekb(dossel, 0x465) & 0x20) ? 0x70 : 0xf0; + attr = attr & (mask_fg | b_mask); + } + this->data->mode = mode; + this->data->cols = cols; + this->data->rows = rows; + this->data->attr = attr; + this->data->empty_attr = attr; + this->data->empty_cell = make_cell(this,' ',attr); + + ae.cursor_shape = getCursorShape(this); + + return 0; +} + + +static void updateLineN(screen_t *this, const void *line, int y, int len) +{ + if (y >= 0 && y < this->data->rows && len > 0 && len <= 2*this->data->cols) + movedata(_my_ds(),(unsigned)line,dossel,co80+y*this->data->cols*2,len); +} + + +static void clearLine(screen_t *this, int y) +{ + if (y >= 0 && y < this->data->rows) + { + unsigned sp = co80 + y * this->data->cols * 2; + unsigned short a = this->data->empty_cell; + int i = this->data->cols; + + _farsetsel(dossel); + do { + _farnspokew(sp, a); + sp += 2; + } while (--i); + } +} + + +static void clear(screen_t *this) +{ + unsigned char attr = ScreenAttrib; + ScreenAttrib = this->data->empty_attr; + ScreenClear(); + ScreenAttrib = attr; +} + + +static int scrollUp(screen_t *this, int lines) +{ + int sr = this->data->rows; + int sc = this->data->cols; + int y; + + + if (lines <= 0 || lines > sr) + return 0; + +#ifdef USE_SCROLLBACK + /* copy to scrollback buffer */ + for (y = 0; y < lines; y++) + { + unsigned short buf[ sc ]; + movedata(dossel,co80+y*this->data->cols*2,_my_ds(),(unsigned)buf,sizeof(buf)); + sb_push(this,buf,sizeof(buf)); + } +#endif + + /* move screen up */ + if (lines < sr) + movedata(dossel,co80+lines*sc*2,dossel,co80,(sr-lines)*sc*2); + + /* fill in blank lines at bottom */ + for (y = sr - lines; y < sr; y++) + clearLine(this,y); + + return lines; +} + + +static int scrollDown(screen_t *this, int lines) +{ + int sr = this->data->rows; + int sc = this->data->cols; + int y; + + if (lines <= 0 || lines > sr) + return 0; + + /* move screen down */ + if (lines < sr) + { + /* !@#% movedata can't handle overlapping regions... */ + /* movedata(dossel,co80,dossel,co80+lines*sc*2,(sr-lines)*sc*2); */ + unsigned short buf[ (sr-lines)*sc ]; + movedata(dossel,co80,_my_ds(),(unsigned)buf,sizeof(buf)); + movedata(_my_ds(),(unsigned)buf,dossel,co80+lines*sc*2,sizeof(buf)); + } + + /* copy top lines from scrollback buffer */ + for (y = lines; --y >= 0; ) + { +#ifdef USE_SCROLLBACK + const unsigned short *buf = sb_pop(this); + if (buf == NULL) + clearLine(this,y); + else + updateLineN(this,buf,y,sc*2); +#else + clearLine(this,y); +#endif + } + + return lines; +} + + +static int s_kbhit(screen_t *this) +{ + UNUSED(this); + return kbhit(); +} + + +static int intro(screen_t *this, void (*show_frames)(screen_t *) ) +{ + int shape; + unsigned short old_flags = __djgpp_hwint_flags; + + if ((this->data->init_attr & mask_bg) != BG_BLACK) + return 0; + + __djgpp_hwint_flags |= 3; + while (kbhit()) + (void) getkey(); + + shape = hideCursor(this); + show_frames(this); + setCursorShape(this,shape); + + while (kbhit()) + (void) getkey(); + __djgpp_hwint_flags = old_flags; + + return 1; +} + + +static void atExit(void) +{ + static int done = 0; + if (done) return; + done = 1; + if (ae.cursor_shape >= 0) + setCursorShape(NULL,ae.cursor_shape); +} + + +static const screen_t driver = +{ + sobject_destroy, + 0, /* finalize, */ + atExit, + init, + refresh, + getMode, + getPage, + getRows, + getCols, + isMono, + getFg, + getBg, + getCursor, + getCursorShape, + setFg, + setBg, + setCursor, + setCursorShape, + hideCursor, + putChar, + putCharAttr, + putString, + putStringAttr, + clear, + clearLine, + updateLineN, + scrollUp, + scrollDown, + s_kbhit, + intro, + (struct screen_data_t *) 0 +}; + + +/* public constructor */ +screen_t *screen_djgpp2_construct(void) +{ + return sobject_construct(&driver,sizeof(*driver.data)); +} + + +#endif /* defined(USE_SCREEN) && defined(__DJGPP__) */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/s_object.cpp b/src/s_object.cpp new file mode 100644 index 0000000000..0aff2575a5 --- /dev/null +++ b/src/s_object.cpp @@ -0,0 +1,100 @@ +/* s_object.cpp -- base of all screen drivers + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(USE_SCREEN) + +#define this local_this + +#include "screen.h" + + +/************************************************************************* +// +**************************************************************************/ + +// ugly hacks +static screen_t *last_screen = NULL; + +screen_t *sobject_get_screen(void) +{ + return last_screen; +} + + +void sobject_destroy(screen_t *this) +{ + last_screen = NULL; + if (!this) + return; + if (this->data) + { + if (this->finalize) + this->finalize(this); + free(this->data); + this->data = NULL; + } + free(this); +} + + +screen_t *sobject_construct(const screen_t *c, size_t data_size) +{ + screen_t *this; + + last_screen = NULL; + + /* allocate object */ + this = (screen_t *) malloc(sizeof(*this)); + if (!this) + return NULL; + + /* copy function table */ + *this = *c; + + /* initialize instance variables */ + this->data = (struct screen_data_t *) malloc(data_size); + if (!this->data) + { + free(this); + return NULL; + } + memset(this->data,0,data_size); + + last_screen = this; + return this; +} + + +#endif /* defined(USE_SCREEN) */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/s_vcsa.cpp b/src/s_vcsa.cpp new file mode 100644 index 0000000000..c2173a891d --- /dev/null +++ b/src/s_vcsa.cpp @@ -0,0 +1,601 @@ +/* s_vcsa.cpp -- Linux /dev/vcsa screen driver + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(USE_SCREEN) && defined(USE_SCREEN_VCSA) + +#include "screen.h" + +#define this local_this + +#define mask_fg 0x0f +#define mask_bg 0xf0 + +/* #define USE_SCROLLBACK */ + + +/************************************************************************* +// direct screen access ( /dev/vcsaNN ) +**************************************************************************/ + +#include +#include +#include +#if defined(__linux__) +# include +# include +# include +#endif + + +struct screen_data_t +{ + int fd; + int mode; + int page; + int cols; + int rows; + int cursor_x; + int cursor_y; + unsigned char attr; + unsigned char init_attr; + unsigned char map[256]; + unsigned short empty_line[256]; +#ifdef USE_SCROLLBACK + /* scrollback buffer */ + unsigned short sb_buf[32][256]; + int sb_size; + int sb_base; + int sb_sp; +#endif /* USE_SCROLLBACK */ +}; + + +#ifdef USE_SCROLLBACK +static __inline__ void sb_add(screen_t *this, int *val, int inc) +{ + *val = (*val + inc) & (this->data->sb_size - 1); +} + +static void sb_push(screen_t *this, const unsigned short *line, int len) +{ + memcpy(this->data->sb_buf[this->data->sb_sp],line,len); + sb_add(this,&this->data->sb_sp,1); + if (this->data->sb_sp == this->data->sb_base) + sb_add(this,&this->data->sb_base,1); +} + +static const unsigned short *sb_pop(screen_t *this) +{ + if (this->data->sb_sp == this->data->sb_base) + return NULL; + sb_add(this,&this->data->sb_sp,-1); + return this->data->sb_buf[this->data->sb_sp]; +} +#endif /* USE_SCROLLBACK */ + + +static void refresh(screen_t *this) +{ + UNUSED(this); +} + + +static __inline__ +unsigned short make_cell(screen_t *this, int ch, int attr) +{ + return ((attr & 0xff) << 8) | (this->data->map[ch & 0xff] & 0xff); +} + + +static int getMode(const screen_t *this) +{ + return this->data->mode; +} + + +static int getPage(const screen_t *this) +{ + return this->data->page; +} + + +static int getRows(const screen_t *this) +{ + return this->data->rows; +} + + +static int getCols(const screen_t *this) +{ + return this->data->cols; +} + + +static int isMono(const screen_t *this) +{ + /* FIXME */ + UNUSED(this); + return 0; +} + + +static int getFg(const screen_t *this) +{ + return this->data->attr & mask_fg; +} + + +static int getBg(const screen_t *this) +{ + return this->data->attr & mask_bg; +} + + +static void setFg(screen_t *this, int fg) +{ + this->data->attr = (this->data->attr & mask_bg) | (fg & mask_fg); +} + + +static void setBg(screen_t *this, int bg) +{ + this->data->attr = (this->data->attr & mask_fg) | (bg & mask_bg); +} + + +/* private */ +static int gotoxy(screen_t *this, int x, int y) +{ + if (x >= 0 && y >= 0 && x < this->data->cols && y < this->data->rows) + { + if (lseek(this->data->fd, 4 + (x + y * this->data->cols) * 2, SEEK_SET) != -1) + { + return 0; + } + } + return -1; +} + + +static void setCursor(screen_t *this, int x, int y) +{ + if (gotoxy(this,x,y) == 0) + { + unsigned char b[2] = { x, y }; + if (lseek(this->data->fd, 2, SEEK_SET) != -1) + write(this->data->fd, b, 2); + this->data->cursor_x = x; + this->data->cursor_y = y; + } +} + + +static void getCursor(const screen_t *this, int *x, int *y) +{ + int cx = this->data->cursor_x; + int cy = this->data->cursor_y; +#if 1 + if (lseek(this->data->fd, 2, SEEK_SET) != -1) + { + unsigned char b[2]; + if (read(this->data->fd, b, 2) == 2) + { + if (b[0] < this->data->cols && b[1] < this->data->rows) + { + cx = b[0]; + cy = b[1]; + } + } + } +#endif + if (x) *x = cx; + if (y) *y = cy; +} + + +static void putCharAttr(screen_t *this, int ch, int attr, int x, int y) +{ + unsigned short a = make_cell(this,ch,attr); + + if (gotoxy(this,x,y) == 0) + write(this->data->fd, &a, 2); +} + + +static void putChar(screen_t *this, int ch, int x, int y) +{ + putCharAttr(this,ch,this->data->attr,x,y); +} + + +static void putStringAttr(screen_t *this, const char *s, int attr, int x, int y) +{ + assert((int)strlen(s) <= 256); + assert(x + (int)strlen(s) <= this->data->cols); + while (*s) + putCharAttr(this,*s++,attr,x++,y); +} + + +static void putString(screen_t *this, const char *s, int x, int y) +{ + putStringAttr(this,s,this->data->attr,x,y); +} + + +/* private */ +static void getChar(screen_t *this, int *ch, int *attr, int x, int y) +{ + unsigned short a; + + if (gotoxy(this,x,y) == 0 && read(this->data->fd, &a, 2) == 2) + { + if (ch) + *ch = a & 0xff; + if (attr) + *attr = (a >> 8) & 0xff; + } +} + + +/* private */ +static int init_scrnmap(screen_t *this, int fd) +{ + int scrnmap_done = 0; + int i; + +#if 1 && defined(GIO_UNISCRNMAP) && defined(E_TABSZ) + if (!scrnmap_done) + { + unsigned short scrnmap[E_TABSZ]; + if (ioctl(fd, GIO_UNISCRNMAP, scrnmap) == 0) + { + for (i = 0; i < E_TABSZ; i++) + this->data->map[scrnmap[i] & 0xff] = i; + scrnmap_done = 1; + } + } +#endif +#if 1 && defined(GIO_SCRNMAP) && defined(E_TABSZ) + if (!scrnmap_done) + { + unsigned char scrnmap[E_TABSZ]; + if (ioctl(fd, GIO_SCRNMAP, scrnmap) == 0) + { + for (i = 0; i < E_TABSZ; i++) + this->data->map[scrnmap[i] & 0xff] = i; + scrnmap_done = 1; + } + } +#endif + + return scrnmap_done; +} + + +static int init(screen_t *this, int fd) +{ + struct stat st; + + if (!this || !this->data) + return -1; + + this->data->fd = -1; + this->data->mode = -1; + this->data->page = 0; +#ifdef USE_SCROLLBACK + this->data->sb_size = 32; + this->data->sb_base = 0; + this->data->sb_sp = 0; +#endif + if (fd < 0 || !isatty(fd)) + return -1; + if (fstat(fd,&st) != 0) + return -1; + + /* check if we are running in a virtual console */ +#if defined(MINOR) && defined(MAJOR) && defined(TTY_MAJOR) + if (MAJOR(st.st_rdev) == TTY_MAJOR) + { + char vc_name[32]; + unsigned char vc_data[4]; + int i; + int attr; + unsigned short a; + + sprintf(vc_name, "/dev/vcsa%d", (int) MINOR(st.st_rdev)); + this->data->fd = open(vc_name, O_RDWR); + if (this->data->fd != -1) + { + if (read(this->data->fd, vc_data, 4) == 4) + { + this->data->mode = 3; + this->data->rows = vc_data[0]; + this->data->cols = vc_data[1]; + this->data->cursor_x = vc_data[2]; + this->data->cursor_y = vc_data[3]; + + for (i = 0; i < 256; i++) + this->data->map[i] = i; + i = init_scrnmap(this,this->data->fd) || + init_scrnmap(this,STDIN_FILENO); + + getChar(this,NULL,&attr,this->data->cursor_x,this->data->cursor_y); + this->data->init_attr = attr; + this->data->attr = attr; + a = make_cell(this,' ',attr); + for (i = 0; i < 256; i++) + this->data->empty_line[i] = a; + } + else + { + close(this->data->fd); + this->data->fd = -1; + } + } + } +#endif + + if (this->data->mode < 0) + return -1; + + return 0; +} + + +static void finalize(screen_t *this) +{ + if (this->data->fd != -1) + (void) close(this->data->fd); +} + + +static void updateLineN(screen_t *this, const void *line, int y, int len) +{ + if (len > 0 && len <= 2*this->data->cols && gotoxy(this,0,y) == 0) + { + int i; + unsigned char new_line[len]; + unsigned char *l1 = new_line; + const unsigned char *l2 = (const unsigned char *) line; + + for (i = 0; i < len; i += 2) + { + *l1++ = *l2++; + *l1++ = this->data->map[*l2++]; + } + write(this->data->fd, new_line, len); + } +} + + +static void clearLine(screen_t *this, int y) +{ + if (gotoxy(this,0,y) == 0) + write(this->data->fd, this->data->empty_line, 2*this->data->cols); +} + + +static void clear(screen_t *this) +{ + int y; + + for (y = 0; y < this->data->rows; y++) + clearLine(this,y); +} + + +static int scrollUp(screen_t *this, int lines) +{ + int sr = this->data->rows; + int sc = this->data->cols; + int y; + + if (lines <= 0 || lines > sr) + return 0; + +#ifdef USE_SCROLLBACK + /* copy to scrollback buffer */ + for (y = 0; y < lines; y++) + { + unsigned short buf[ sc ]; + gotoxy(this,0,y); + read(this->data->fd, buf, sizeof(buf)); + sb_push(this,buf,sizeof(buf)); + } +#endif + + /* move screen up */ + if (lines < sr) + { + unsigned short buf[ (sr-lines)*sc ]; + gotoxy(this,0,lines); + read(this->data->fd, buf, sizeof(buf)); + gotoxy(this,0,0); + write(this->data->fd, buf, sizeof(buf)); + } + + /* fill in blank lines at bottom */ + for (y = sr - lines; y < sr; y++) + clearLine(this,y); + + return lines; +} + + +static int scrollDown(screen_t *this, int lines) +{ + int sr = this->data->rows; + int sc = this->data->cols; + int y; + + if (lines <= 0 || lines > sr) + return 0; + + /* move screen down */ + if (lines < sr) + { + unsigned short buf[ (sr-lines)*sc ]; + gotoxy(this,0,0); + read(this->data->fd, buf, sizeof(buf)); + gotoxy(this,0,lines); + write(this->data->fd, buf, sizeof(buf)); + } + + /* copy top lines from scrollback buffer */ + for (y = lines; --y >= 0; ) + { +#ifdef USE_SCROLLBACK + const unsigned short *buf = sb_pop(this); + if (buf == NULL) + clearLine(this,y); + else + updateLineN(this,buf,y,sc*2); +#else + clearLine(this,y); +#endif + } + + return lines; +} + + +static int getCursorShape(const screen_t *this) +{ + UNUSED(this); + return 0; +} + + +static void setCursorShape(screen_t *this, int shape) +{ + UNUSED(this); + UNUSED(shape); +} + + +static int kbhit(screen_t *this) +{ + const int fd = STDIN_FILENO; + const unsigned long usec = 0; + struct timeval tv; + fd_set fds; + + UNUSED(this); + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + return (select(fd + 1, &fds, NULL, NULL, &tv) > 0); +} + + +static int intro(screen_t *this, void (*show_frames)(screen_t *) ) +{ + int shape; + struct termios term_old, term_new; + int term_r; + + if ((this->data->init_attr & mask_bg) != BG_BLACK) + return 0; + + term_r = tcgetattr(STDIN_FILENO, &term_old); + if (term_r == 0) + { + term_new = term_old; + term_new.c_lflag &= ~(ISIG | ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &term_new); + } + + shape = getCursorShape(this); + setCursorShape(this,0x2000); + show_frames(this); + if (this->data->rows > 24) + setCursor(this,this->data->cursor_x,this->data->cursor_y+1); + setCursorShape(this,shape); + + while (kbhit(this)) + (void) fgetc(stdin); + if (term_r == 0) + tcsetattr(STDIN_FILENO, TCSANOW, &term_old); + + return 1; +} + + +static const screen_t driver = +{ + sobject_destroy, + finalize, + 0, /* atExit */ + init, + refresh, + getMode, + getPage, + getRows, + getCols, + isMono, + getFg, + getBg, + getCursor, + getCursorShape, + setFg, + setBg, + setCursor, + setCursorShape, + 0, /* hideCursor */ + putChar, + putCharAttr, + putString, + putStringAttr, + clear, + clearLine, + updateLineN, + scrollUp, + scrollDown, + kbhit, + intro, + (struct screen_data_t *) 0 +}; + + +/* public constructor */ +screen_t *screen_vcsa_construct(void) +{ + return sobject_construct(&driver,sizeof(*driver.data)); +} + + +#endif /* defined(USE_SCREEN) && defined(USE_SCREEN_VCSA) */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/s_win32.cpp b/src/s_win32.cpp new file mode 100644 index 0000000000..ef4e5cd678 --- /dev/null +++ b/src/s_win32.cpp @@ -0,0 +1,489 @@ +/* s_win32.cpp -- Win32 console screen driver + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" + +#if defined(USE_SCREEN) && defined(__MFX_WIN32) + +#include "screen.h" + +#define this local_this + +#define mask_fg 0x0f +#define mask_bg 0xf0 + + +/************************************************************************* +// direct screen access +**************************************************************************/ + +#include +#if defined(HAVE_CONIO_H) +# include +#endif + + +struct screen_data_t +{ + HANDLE hi; + HANDLE ho; + CONSOLE_SCREEN_BUFFER_INFO csbi; + char title[512]; + + int mode; + int cols; + int rows; + int cursor_x; + int cursor_y; + + WORD attr; + WORD init_attr; + + CHAR_INFO empty_cell; + CHAR_INFO empty_line[256]; +}; + + +#define P(x) ((SHORT) (x)) + +static const COORD pos00 = { 0, 0 }; +static const COORD size11 = { 1, 1 }; + + +/* atExit information */ +static struct +{ + int is_valid; + HANDLE ho; + CONSOLE_CURSOR_INFO cci; +} ae; + + +static void refresh(screen_t *this) +{ + UNUSED(this); +} + + +static int getMode(const screen_t *this) +{ + return this->data->mode; +} + + +static int getPage(const screen_t *this) +{ + UNUSED(this); + return 0; +} + + +static int getRows(const screen_t *this) +{ + return this->data->rows; +} + + +static int getCols(const screen_t *this) +{ + return this->data->cols; +} + + +static int isMono(const screen_t *this) +{ + UNUSED(this); + return 0; +} + + +static int getFg(const screen_t *this) +{ + return this->data->attr & mask_fg; +} + + +static int getBg(const screen_t *this) +{ + return this->data->attr & mask_bg; +} + + +static void setFg(screen_t *this, int fg) +{ + this->data->attr = (WORD) ((this->data->attr & mask_bg) | (fg & mask_fg)); + SetConsoleTextAttribute(this->data->ho, this->data->attr); +} + + +static void setBg(screen_t *this, int bg) +{ + this->data->attr = (WORD) ((this->data->attr & mask_fg) | (bg & mask_bg)); + SetConsoleTextAttribute(this->data->ho, this->data->attr); +} + + +static void setCursor(screen_t *this, int x, int y) +{ + if (x >= 0 && y >= 0 && x < this->data->cols && y < this->data->rows) + { + COORD coord = { P(x), P(y) }; + SetConsoleCursorPosition(this->data->ho, coord); + this->data->cursor_x = x; + this->data->cursor_y = y; + } +} + + +static void getCursor(const screen_t *this, int *x, int *y) +{ + int cx = this->data->cursor_x; + int cy = this->data->cursor_y; +#if 1 + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(this->data->ho, &csbi)) + { + cx = csbi.dwCursorPosition.X; + cy = csbi.dwCursorPosition.Y; + } +#endif + if (x) *x = cx; + if (y) *y = cy; +} + + +static void putCharAttr(screen_t *this, int ch, int attr, int x, int y) +{ + CHAR_INFO ci; + SMALL_RECT region = { P(x), P(y), P(x), P(y) }; + ci.Char.UnicodeChar = 0; + ci.Char.AsciiChar = (CHAR) ch; + ci.Attributes = (WORD) attr; + WriteConsoleOutput(this->data->ho, &ci, size11, pos00, ®ion); +} + + +static void putChar(screen_t *this, int ch, int x, int y) +{ + this->putCharAttr(this, ch, this->data->attr, x, y); +} + + +static void putStringAttr(screen_t *this, const char *s, int attr, int x, int y) +{ + int i; + int l = (int) strlen(s); + if (l <= 0) + return; + assert(l <= 256); + assert(x + l <= this->data->cols); + CHAR_INFO ci[256]; + COORD size = { P(l), 1 }; + SMALL_RECT region = { P(x), P(y), P(x + l - 1), P(y) }; + for (i = 0; i < l; i++) + { + ci[i].Char.UnicodeChar = 0; + ci[i].Char.AsciiChar = *s++; + ci[i].Attributes = (WORD) attr; + } + WriteConsoleOutput(this->data->ho, &ci[0], size, pos00, ®ion); +} + + +static void putString(screen_t *this, const char *s, int x, int y) +{ + this->putStringAttr(this, s, this->data->attr, x, y); +} + + +/* private */ +static int cci2shape(CONSOLE_CURSOR_INFO *cci) +{ + int shape = cci->dwSize & 255; + if (!cci->bVisible) + shape |= 0x2000; + return shape; +} + + +static int getCursorShape(const screen_t *this) +{ + CONSOLE_CURSOR_INFO cci; + GetConsoleCursorInfo(this->data->ho, &cci); + return cci2shape(&cci); +} + + +static void setCursorShape(screen_t *this, int shape) +{ + CONSOLE_CURSOR_INFO cci; + cci.dwSize = shape & 255; + cci.bVisible = (shape & 0x2000) ? 0 : 1; + SetConsoleCursorInfo(this->data->ho, &cci); +} + + +static int hideCursor(screen_t *this) +{ + CONSOLE_CURSOR_INFO cci; + int shape; + + GetConsoleCursorInfo(this->data->ho, &cci); + shape = cci2shape(&cci); + if (cci.bVisible) + { + cci.bVisible = 0; + SetConsoleCursorInfo(this->data->ho, &cci); + } + return shape; +} + + +static int init(screen_t *this, int fd) +{ + HANDLE hi, ho; + CONSOLE_SCREEN_BUFFER_INFO *csbi; + DWORD mode; + WORD attr; + int i; + + if (!this || !this->data) + return -1; + + this->data->hi = INVALID_HANDLE_VALUE; + this->data->ho = INVALID_HANDLE_VALUE; + this->data->mode = -1; + if (fd < 0 || !isatty(fd)) + return -1; + + hi = GetStdHandle(STD_INPUT_HANDLE); + ho = GetStdHandle(STD_OUTPUT_HANDLE); + if (hi == INVALID_HANDLE_VALUE || ho == INVALID_HANDLE_VALUE) + return -1; + if (!GetConsoleMode(ho, &mode)) + return -1; + csbi = &this->data->csbi; + if (!GetConsoleScreenBufferInfo(ho, csbi)) + return -1; + if (csbi->srWindow.Left != 0 || csbi->srWindow.Top != 0) + return -1; + if (!GetConsoleCursorInfo(ho, &ae.cci)) + return -1; + if (!GetConsoleTitle(this->data->title, sizeof(this->data->title))) + return -1; + + this->data->cols = csbi->srWindow.Right - csbi->srWindow.Left + 1; + this->data->rows = csbi->srWindow.Bottom - csbi->srWindow.Top + 1; + this->data->cursor_x = csbi->dwCursorPosition.X; + this->data->cursor_y = csbi->dwCursorPosition.Y; + + ae.ho = ho; + ae.is_valid = 1; + + attr = csbi->wAttributes; + this->data->hi = hi; + this->data->ho = ho; + this->data->mode = 3; // ??? + this->data->attr = attr; + this->data->init_attr = attr; + this->data->empty_cell.Char.UnicodeChar = 0; + this->data->empty_cell.Char.AsciiChar = ' '; + this->data->empty_cell.Attributes = attr; + for (i = 0; i < 256; i++) + this->data->empty_line[i] = this->data->empty_cell; + + return 0; +} + + +static void updateLineN(screen_t *this, const void *line, int y, int len) +{ + if (y >= 0 && y < this->data->rows && len > 0 && len <= 2*this->data->cols) + { +#if 0 + const char *s = (const char *) line; + int l = len / 2; + int i; + + assert(l <= 256); + CHAR_INFO ci[256]; + COORD size = { P(l), 1 }; + SMALL_RECT region = { 0, P(y), P(0 + l - 1), P(y) }; + for (i = 0; i < l; i++) + { + ci[i].Char.UnicodeChar = 0; + ci[i].Char.AsciiChar = *s++; + ci[i].Attributes = *s++; + } + WriteConsoleOutput(this->data->ho, &ci[0], size, pos00, ®ion); +#endif + UNUSED(line); + } +} + + +static void clearLine(screen_t *this, int y) +{ + if (y >= 0 && y < this->data->rows) + { + COORD size = { P(this->data->cols), 1 }; + SMALL_RECT region = { 0, P(y), P(this->data->cols-1), P(y) }; + WriteConsoleOutput(this->data->ho, this->data->empty_line, size, pos00, ®ion); + } +} + + +static void clear(screen_t *this) +{ + int y; + for (y = 0; y < this->data->rows; y++) + this->clearLine(this, y); +} + + +/* private */ +static int do_scroll(screen_t *this, int lines, int way) +{ + if (lines <= 0 || lines > this->data->rows) + return 0; + if (lines == this->data->rows) + { + this->clear(this); + return lines; + } + + SMALL_RECT rect = { 0, 0, P(this->data->cols-1), P(this->data->rows-1) }; + //SMALL_RECT clip = rect; + COORD dest = { 0, 0 }; + switch (way) + { + case 0: + rect.Top = P(rect.Top + lines); + break; + case 1: + rect.Bottom = P(rect.Bottom - lines); + dest.Y = P(dest.Y + lines); + break; + } + //ScrollConsoleScreenBuffer(this->data->ho, &rect, &clip, dest, &this->data->empty_cell); + ScrollConsoleScreenBuffer(this->data->ho, &rect, NULL, dest, &this->data->empty_cell); + + return lines; +} + +static int scrollUp(screen_t *this, int lines) +{ + return do_scroll(this, lines, 0); +} + +static int scrollDown(screen_t *this, int lines) +{ + return do_scroll(this, lines, 1); +} + + +static int s_kbhit(screen_t *this) +{ +#if defined(HAVE_CONIO_H) + UNUSED(this); + return _kbhit(); +#else + UNUSED(this); + return 0; +#endif +} + + +static int intro(screen_t *this, void (*show_frames)(screen_t *) ) +{ + UNUSED(this); + UNUSED(show_frames); + return 0; +} + + +static void atExit(void) +{ + static int done = 0; + if (done) return; + done = 1; + if (ae.is_valid) + { + } +} + + +static const screen_t driver = +{ + sobject_destroy, + 0, /* finalize, */ + atExit, + init, + refresh, + getMode, + getPage, + getRows, + getCols, + isMono, + getFg, + getBg, + getCursor, + getCursorShape, + setFg, + setBg, + setCursor, + setCursorShape, + hideCursor, + putChar, + putCharAttr, + putString, + putStringAttr, + clear, + clearLine, + updateLineN, + scrollUp, + scrollDown, + s_kbhit, + intro, + (struct screen_data_t *) 0 +}; + + +/* public constructor */ +screen_t *screen_win32_construct(void) +{ + return sobject_construct(&driver,sizeof(*driver.data)); +} + + +#endif /* defined(USE_SCREEN) && defined(__MFX_WIN32) */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/screen.h b/src/screen.h new file mode 100644 index 0000000000..6bd4f6dd1f --- /dev/null +++ b/src/screen.h @@ -0,0 +1,111 @@ +/* screen.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_SCREEN_H +#define __UPX_SCREEN_H + +#if defined(USE_SCREEN) + + +/************************************************************************* +// +**************************************************************************/ + +struct screen_data_t; +struct screen_t; +typedef struct screen_t screen_t; + +struct screen_t +{ +/* public: */ + void (*destroy)(screen_t *s); + void (*finalize)(screen_t *s); + void (*atExit)(void); /* atexit/signal handler */ + + int (*init)(screen_t *s, int fd); + + void (*refresh)(screen_t *s); + + int (*getMode)(const screen_t *s); + int (*getPage)(const screen_t *s); + int (*getRows)(const screen_t *s); + int (*getCols)(const screen_t *s); + int (*isMono)(const screen_t *s); + + int (*getFg)(const screen_t *s); + int (*getBg)(const screen_t *s); + void (*getCursor)(const screen_t *s, int *x, int *y); + int (*getCursorShape)(const screen_t *s); + + void (*setFg)(screen_t *s, int); + void (*setBg)(screen_t *s, int); + void (*setCursor)(screen_t *s, int x, int y); + void (*setCursorShape)(screen_t *s, int shape); + int (*hideCursor)(screen_t *s); + + void (*putChar)(screen_t *s, int c, int x, int y); + void (*putCharAttr)(screen_t *s, int c, int attr, int x, int y); + void (*putString)(screen_t *s, const char *, int x, int y); + void (*putStringAttr)(screen_t *s, const char *, int attr, int x, int y); + + void (*clear)(screen_t *s); + void (*clearLine)(screen_t *s, int); + void (*updateLineN)(screen_t *s, const void *, int y, int len); + + int (*scrollUp)(screen_t *s, int); + int (*scrollDown)(screen_t *s, int); + + int (*kbhit)(screen_t *s); + + int (*intro)(screen_t *s, void (*)(screen_t*) ); + +/* private: */ + struct screen_data_t *data; +}; + + +screen_t *sobject_construct(const screen_t *c, size_t data_size); +void sobject_destroy(screen_t *); +screen_t *sobject_get_screen(void); + +screen_t *screen_curses_construct(void); +screen_t *screen_djgpp2_construct(void); +screen_t *screen_vcsa_construct(void); +screen_t *screen_win32_construct(void); + +void screen_show_frames(screen_t *); + + +#endif + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/stdcxx.cpp b/src/stdcxx.cpp new file mode 100644 index 0000000000..7219db8523 --- /dev/null +++ b/src/stdcxx.cpp @@ -0,0 +1,49 @@ +/* stdcxx.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +//#define WANT_STL +#include "conf.h" +#include "stdcxx.h" + + +#ifdef WANT_STL + +#if defined(__DJGPP__) || defined(__MINGW32__) || defined(__sparc__) +void (*__malloc_alloc_template<0>::__malloc_alloc_oom_handler)() = 0; +# if !defined(__USE_MALLOC) +template class __default_alloc_template; +# endif +#endif + +#endif + + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/stdcxx.h b/src/stdcxx.h new file mode 100644 index 0000000000..2fb34a047d --- /dev/null +++ b/src/stdcxx.h @@ -0,0 +1,83 @@ +/* stdcxx.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_STDCXX_H +#define __UPX_STDCXX_H + +#ifdef __cplusplus + + +/************************************************************************* +// exceptions, RTTI +**************************************************************************/ + +#include +//#include + +#include +#include + + +/************************************************************************* +// STL +**************************************************************************/ + +#ifdef WANT_STL + +#if defined(__linux__) +# define _NOTHREADS +#endif +#if defined(__DJGPP__) || defined(__MINGW32__) || defined(__sparc__) +# define __THROW_BAD_ALLOC throw bad_alloc() +# define __USE_MALLOC +# define enable upx_stl_enable +#endif +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4018 4100 4663) +#endif + +#include + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif /* WANT_STL */ + + +using namespace std; + +#endif /* __cplusplus */ + +#endif /* already included */ + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/stub/Makefile b/src/stub/Makefile new file mode 100644 index 0000000000..5addfb8772 --- /dev/null +++ b/src/stub/Makefile @@ -0,0 +1,322 @@ +# +# UPX stub Makefile (GNU make) +# + +ifeq ($(strip $(UCLDIR)),) +# change this to reflect where the UCL library is +UCLDIR = $(HOME)/local/src/ucl-0.91 +endif + + +# ------------------------------------------------------- +# You should not have to change anything below this line. +# ------------------------------------------------------- + +SHELL = /bin/sh + +top_srcdir = ../.. + + +# These are the files we want to create. +STUBS = \ + l_com.h \ + l_djgpp2.h stubify.h \ + l_exe.h \ + l_sys.h \ + l_t_n2b.h l_t_n2bs.h l_t_n2d.h l_t_n2ds.h \ + l_tmt.h \ + l_wcle.h \ + l_w32pe.h \ + l_lx_n2b.h l_lx_n2d.h \ + l_le_n2b.h l_le_n2d.h \ + l_sh_n2b.h l_sh_n2d.h + + +# util var for use in the rules - basename of the current target +override T = $(basename $@) + + +# /*********************************************************************** +# // source directories +# ************************************************************************/ + +UCL_UPX = $(UCLDIR)/upx +UCL_I386 = $(UCLDIR)/upx/i386 +UCL_M68K = $(UCLDIR)/upx/m68k + +.SUFFIXES: +.SUFFIXES: .asm .ash .asx .asy .bin .c .h .s + +vpath %.ash $(UCL_I386) +vpath %.ash $(UCL_M68K) + + +# /*********************************************************************** +# // tools +# ************************************************************************/ + +NASM = nasm -w+macro-params -w+orphan-labels + +APP = perl -w scripts/app.pl +BIN2H = perl -w scripts/bin2h.pl +BRANDELF = perl -w scripts/brandelf.pl +O2BIN = perl -w scripts/o2bin.pl +SETFOLD = perl -w scripts/setfold.pl +##STRIPELF = perl -w scripts/stripelf.pl +STRIPELF = ./util/sstrip/sstrip + +# Preprocessor for a68k assembler. +CPP_M68K = gcc -I$(UCL_UPX) -E -x assembler-with-cpp -Wall -Wp,-P,-C,-traditional + +# Use gcc 2.95.2 for smallest code. +CC_LINUX_CFLAGS = -Wall -W -Wcast-align -Wcast-qual -Wwrite-strings +CC_LINUX_CFLAGS += -funsigned-char +###CC_LINUX_CFLAGS += -fwritable-strings -save-temps +CC_LINUX = gcc272 -O2 -m386 -malign-functions=0 -malign-jumps=0 -malign-loops=0 $(CC_LINUX_CFLAGS) +CC_LINUX = gcc -Os -march=i386 -mcpu=i386 -malign-functions=0 -malign-jumps=0 -malign-loops=0 $(CC_LINUX_CFLAGS) +# Specifying -mcpu=i586 inhibits use of 'leave', which costs 2 bytes per subr +#CC_LINUX =gcc -Os -march=i386 -mcpu=i586 -malign-functions=0 -malign-jumps=0 -malign-loops=0 $(CC_LINUX_CFLAGS) + + +# /*********************************************************************** +# // main targets +# ************************************************************************/ + +.PHONY: default all stubs mostlyclean clean distclean maintainer-clean ident strings + +default: + @echo "UPX info: type 'make all' if you have all the needed build tools." + +all: stubs upxb upxd + +stubs: $(STUBS) + +mostlyclean: + -rm -f *~ *.bin *.bkp *.i *.lst *.map + +clean: mostlyclean + -rm -f *.o *.asx *.asy upxb upxd + +distclean: clean + +# This command is intended for maintainers to use; it deletes files +# that may require special tools to rebuild. +maintainer-clean: distclean + -rm -f $(STUBS) + +ident: all + ident *.bin + +strings: all + strings *.bin + + +# /*********************************************************************** +# // rules +# ************************************************************************/ + +.asm.asx: + $(APP) $< $@ + +.ash.asy: + $(APP) $< $@ + + +stubify.h: stub.asm + djasm $< $@ + +l_com.h: l_com.asx + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv2b_loader $@ + +l_djgpp2.h: l_djgpp2.asx + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv_loader $@ + +l_exe.h: l_exe.asx + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv_loader $@ + +l_sys.h: l_sys.asx + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv2b_loader $@ + +l_tmt.h: l_tmt.asx + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv_loader $@ + +l_t_n2b.h: l_tos.s + $(CPP_M68K) -D__A68K__ -DNRV2B -o $T.i $< + a68k -q -x $T.i + $(O2BIN) $T.o $T.bin 'UPX1' 'UPX9' + $(BIN2H) $T.bin nrv2b_loader $@ + +l_t_n2bs.h: l_tos.s + $(CPP_M68K) -D__A68K__ -DNRV2B -DSMALL -o $T.i $< + a68k -q -x $T.i + $(O2BIN) $T.o $T.bin 'UPX1' 'UPX9' + $(BIN2H) $T.bin nrv2b_loader_small $@ + +l_t_n2d.h: l_tos.s + $(CPP_M68K) -D__A68K__ -DNRV2D -o $T.i $< + a68k -q -x $T.i + $(O2BIN) $T.o $T.bin 'UPX1' 'UPX9' + $(BIN2H) $T.bin nrv2d_loader $@ + +l_t_n2ds.h: l_tos.s + $(CPP_M68K) -D__A68K__ -DNRV2D -DSMALL -o $T.i $< + a68k -q -x $T.i + $(O2BIN) $T.o $T.bin 'UPX1' 'UPX9' + $(BIN2H) $T.bin nrv2d_loader_small $@ + +l_vxd.h: l_vxd.asm + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv_loader $@ + +l_wcle.h: l_wcle.asx + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv_loader $@ + +l_w32pe.h: l_w32pe.asx + $(NASM) -f bin -o $T.bin $< + $(BIN2H) $T.bin nrv_loader $@ + + +# /*********************************************************************** +# // linux rules (exec, elf, sh, sep) +# ************************************************************************/ + +l_lx_n2b.h: l_lx_exec.c l_xe_n2b.o + $(CC_LINUX) -DNRV2B -s -o $T.o -c $< + ld -s -Map l_lx_n2b.map -o $T.bin \ + l_xe_n2b.o $T.o + objcopy -S -R .comment -R .note $T.bin + $(STRIPELF) $T.bin + $(BRANDELF) $T.bin + $(BIN2H) $T.bin linux_i386exec_nrv2b_loader $@ + +l_le_n2b.h: l_lx_elf.c l_6e_n2b.o l_lx_elf86.lds + $(CC_LINUX) -DNRV2B -s -o $T.o -c $< + ld -T l_lx_elf86.lds -s -Map $T.map -o $T.bin \ + l_6e_n2b.o $T.o + objcopy -S -R .comment -R .note $T.bin + $(SETFOLD) $T.bin 0x`nm l_6e_n2b.o | grep fold_begin` + $(STRIPELF) $T.bin + $(BRANDELF) $T.bin + $(BIN2H) $T.bin linux_i386elf_nrv2b_loader $@ + +l_sh_n2b.h: l_lx_sh.c l_6h_n2b.o l_lx_sh86.lds + $(CC_LINUX) -DNRV2B -s -o $T.o -c $< + ld -T l_lx_sh86.lds -s -Map $T.map -o $T.bin \ + l_6h_n2b.o $T.o + objcopy -S -R .comment -R .note $T.bin + $(SETFOLD) $T.bin 0x`nm l_6h_n2b.o | grep fold_begin` + $(STRIPELF) $T.bin + $(BRANDELF) $T.bin + $(BIN2H) $T.bin linux_i386sh_nrv2b_loader $@ + +l_xe_n2b.o: l_lx_exec86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2B -o $@ $< + +l_6e_n2b.o: l_lx_elf86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2B -o $@ $< + +l_6h_n2b.o: l_lx_sh86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2B -o $@ $< + + +l_lx_n2d.h: l_lx_exec.c l_xe_n2d.o + $(CC_LINUX) -DNRV2D -s -o $T.o -c $< + ld -s -Map $T.map -o $T.bin \ + l_xe_n2d.o $T.o + objcopy -S -R .comment -R .note $T.bin + $(STRIPELF) $T.bin + $(BRANDELF) $T.bin + $(BIN2H) $T.bin linux_i386exec_nrv2d_loader $@ + +l_le_n2d.h: l_lx_elf.c l_6e_n2d.o l_lx_elf86.lds + $(CC_LINUX) -DNRV2D -s -o $T.o -c $< + ld -T l_lx_elf86.lds -s -Map $T.map -o $T.bin \ + l_6e_n2d.o $T.o + objcopy -S -R .comment -R .note $T.bin + $(SETFOLD) $T.bin 0x`nm l_6e_n2d.o | grep fold_begin` + $(STRIPELF) $T.bin + $(BRANDELF) $T.bin + $(BIN2H) $T.bin linux_i386elf_nrv2d_loader $@ + +l_sh_n2d.h: l_lx_sh.c l_6h_n2d.o l_lx_sh86.lds + $(CC_LINUX) -DNRV2D -s -o $T.o -c $< + ld -T l_lx_sh86.lds -s -Map $T.map -o $T.bin \ + l_6h_n2d.o $T.o + objcopy -S -R .comment -R .note $T.bin + $(SETFOLD) $T.bin 0x`nm l_6h_n2d.o | grep fold_begin` + $(STRIPELF) $T.bin + $(BRANDELF) $T.bin + $(BIN2H) $T.bin linux_i386sh_nrv2d_loader $@ + +l_xe_n2d.o: l_lx_exec86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2D -o $@ $< + +l_6e_n2d.o: l_lx_elf86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2D -o $@ $< + +l_6h_n2d.o: l_lx_sh86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2D -o $@ $< + +l_lx_sep.o: l_lx_sep.c + $(CC_LINUX) -c $< + +upxb: l_lx_sep.o l_lx_sep86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2B -o upxb.o l_lx_sep86.asm + ld -T l_lx_sep86.lds -Map upxb.map -o upxb upxb.o l_lx_sep.o + objcopy -S -R .comment -R .note upxb + $(STRIPELF) upxb + $(BRANDELF) upxb + +upxd: l_lx_sep.o l_lx_sep86.asm + $(NASM) -i$(UCL_I386)/ -f elf -dNRV2D -o upxd.o l_lx_sep86.asm + ld -T l_lx_sep86.lds -Map upxd.map -o upxd upxd.o l_lx_sep.o + objcopy -S -R .comment -R .note upxd + $(STRIPELF) upxd + $(BRANDELF) upxd + +# /*********************************************************************** +# // dependencies +# ************************************************************************/ + +DEPS1 = header.ash macros.ash ident.ash ident_n.ash ident_s.ash +DEPS2 = header.asy macros.asy + +l_com.h: n2b_d16.asy $(DEPS2) +l_djgpp2.h: n2b_d32.asy n2d_d32.asy $(DEPS2) +l_exe.h: n2b_d8e.asy n2d_d8e.asy $(DEPS2) +l_sys.h: n2b_d16.asy $(DEPS2) +l_t_n2b.h: n2b_d.ash bits.ash $(DEPS1) +l_t_n2bs.h: n2b_d.ash bits.ash $(DEPS1) +l_t_n2d.h: n2d_d.ash bits.ash $(DEPS1) +l_t_n2ds.h: n2d_d.ash bits.ash $(DEPS1) +l_tmt.h: n2b_d32.asy n2d_d32.asy $(DEPS2) +l_vxd.h: n2b_d32.asy n2d_d32.asy $(DEPS2) +l_wcle.h: n2b_d32.asy n2d_d32.asy $(DEPS2) +l_w32pe.h: n2b_d32.asy n2d_d32.asy $(DEPS2) + +l_xe_n2b.o: n2b_d32.ash $(DEPS1) +l_6e_n2b.o: n2b_d32.ash $(DEPS1) +l_6h_n2b.o: n2b_d32.ash $(DEPS1) + +l_xe_n2d.o: n2d_d32.ash $(DEPS1) +l_6e_n2d.o: n2d_d32.ash $(DEPS1) +l_6h_n2d.o: n2d_d32.ash $(DEPS1) + +l_lx_n2b.h: linux.hh +l_lx_n2d.h: linux.hh +l_le_n2b.h: linux.hh +l_le_n2d.h: linux.hh +l_sh_n2b.h: linux.hh +l_sh_n2d.h: linux.hh +upxb: linux.hh +upxd: linux.hh + +.NOEXPORT: + +# vi:nowrap diff --git a/src/stub/header.ash b/src/stub/header.ash new file mode 100644 index 0000000000..0b7fc18ddb --- /dev/null +++ b/src/stub/header.ash @@ -0,0 +1,60 @@ +; header.ash -- +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +; ------------- HEADER ------------- ; __UPX1HEAD__ + + db 'UPX!' ; 0 magic + db 0 ; 4 version + db 0 ; 5 type (com,sys,...) + db 0 ; 6 compression method + db 0 ; 7 compression level + dd 0 ; 8 uncompressed adler32 + dd 0 ; 12 compressed adler32 + + %ifdef COM + dw 0 ; 16 uncompressed len + dw 0 ; 18 compressed len + db 0 ; 20 filter + db 0 ; 21 header checksum + %elifdef EXE + db 0,0,0 ; 16 uncompressed len + db 0,0,0 ; 19 compressed len + db 0,0,0 ; 22 original file size + db 0 ; 25 filter + db 0 ; 26 header checksum + %else + dd 0 ; 16 uncompressed len + dd 0 ; 20 compressed len + dd 0 ; 24 original file size + db 0 ; 28 filter id + db 0 ; 29 cto (for filters 0x21..0x29) + db 0 ; unsused + db 0 ; 31 header checksum + %endif + + +; vi:ts=8:et:nowrap diff --git a/src/stub/ident.ash b/src/stub/ident.ash new file mode 100644 index 0000000000..9d293cfd2b --- /dev/null +++ b/src/stub/ident.ash @@ -0,0 +1,37 @@ +; ident.ash -- +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +; ------------- COPYRIGHT ------------- + +%ifdef __IDENTSMA__ +%include "ident_s.ash" +%else; __IDENTBIG__ +%include "ident_n.ash" +%endif; __IDENTEND__ + + +; vi:ts=8:et:nowrap diff --git a/src/stub/ident_n.ash b/src/stub/ident_n.ash new file mode 100644 index 0000000000..8ba82b5494 --- /dev/null +++ b/src/stub/ident_n.ash @@ -0,0 +1,39 @@ +; ident_n.ash -- +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +; ------------- COPYRIGHT ------------- + + db 10,0 + db '$Info: This file is packed with the UPX executable packer http://upx.tsx.org $' + db 10,0 + db '$Id: UPX ' + db 'UPXV' + db ' Copyright (C) 1996-2000 the UPX Team. All Rights Reserved. $' + db 10,0 + + +; vi:ts=8:et:nowrap diff --git a/src/stub/ident_s.ash b/src/stub/ident_s.ash new file mode 100644 index 0000000000..c2c9187bf9 --- /dev/null +++ b/src/stub/ident_s.ash @@ -0,0 +1,35 @@ +; ident_s.ash -- +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +; ------------- COPYRIGHT ------------- + + db 10 + db '$Id: ident_s.ash,v 1.1 2000/05/10 04:57:58 jreiser Exp jreiser $' + db 10,0 + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_com.asm b/src/stub/l_com.asm new file mode 100644 index 0000000000..ddb36599ac --- /dev/null +++ b/src/stub/l_com.asm @@ -0,0 +1,95 @@ +; l_com.asm -- loader & decompressor for the dos/com format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +%define COM 1 +%define CJT16 1 +%define jmps jmp short +%include "macros.ash" + + BITS 16 + ORG 0 + SECTION .text + +; ============= +; ============= ENTRY POINT +; ============= + +; __COMMAIN1__ +start: + cmp sp, 'SP' + ja mem_ok + int 0x20 +mem_ok: + mov cx, 'CX' ; size of decomp + sizeof (data) + 1 + mov si, 'SI' ; cx + 0x100 + mov di, 'DI' + mov bx, 0x8000 + + std + rep + movsb + cld + + xchg si, di + sub si, byte start - cutpoint +; __COMSUBSI__ + sbb bp, bp + push di +%ifdef __COMCALLT__ + push di +%endif; __COMMAIN2__ + jmp .1+'JM' +.1: +%include "header.ash" + +cutpoint: +; __COMCUTPO__ + + +; ============= +; ============= DECOMPRESSION +; ============= + +%include "n2b_d16.ash" + +; ============= +; ============= CALLTRICK +; ============= + + +; ============= + +; __CORETURN__ + ret +eof: +; __COMTHEND__ + section .data + dd -1 + dw eof + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_djgpp2.asm b/src/stub/l_djgpp2.asm new file mode 100644 index 0000000000..734e11135b --- /dev/null +++ b/src/stub/l_djgpp2.asm @@ -0,0 +1,93 @@ +; l_djgpp2.asm -- loader & decompressor for the djgpp2/coff format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +%define jmps jmp short +%include "macros.ash" + + BITS 32 + SECTION .text + ORG 0 + +; ============= +; ============= ENTRY POINT +; ============= + +; __DJ2MAIN1__ +start: + push ds + pop es + + mov esi, 'INPP' ; input pointer + mov edi, 'OUTP' ; output pointer +%ifdef __DJCALLT1__ + push edi +%endif; __DJ2MAIN2__ +; cld ; the stub sets this + or ebp, byte -1 + +; ============= +; ============= DECOMPRESSION +; ============= + +%include "n2b_d32.ash" +%include "n2d_d32.ash" + +; ============= + +; __DJ2BSS00__ + mov ecx, 'BSSL' + rep + stosd +%ifdef __DJCALLT2__ + +; ============= +; ============= CALLTRICK +; ============= + + pop edi + cjt32 0 +%endif; __DJRETURN__ + +; ============= + + push dword 'ENTR' ; entry point + ret + +; because of a feature of the djgpp loader, the size of this stub must be +; a multiple of 4 and as the upx decompressor depends on the fact that +; the compressed data stream begins just after the header, i must +; use an alignment here - ML + align 4 +%include "header.ash" +eof: +; __DJTHEEND__ + section .data + dd -1 + dw eof + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_exe.asm b/src/stub/l_exe.asm new file mode 100644 index 0000000000..7bc41a3f14 --- /dev/null +++ b/src/stub/l_exe.asm @@ -0,0 +1,177 @@ +; l_exe.asm -- loader & decompressor for the dos/exe format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +%define EXE +%define jmps jmp short + + BITS 16 + ORG 0 + SECTION .text + +; ============= +; ============= ENTRY POINT +; ============= +; __EXEENTRY__ + mov cx, 'CX' ; first_copy_len/2 + mov si, 'SI' ; cx*2-2 + mov di, si + push ds + db 0xa9 +do_copy: + mov ch, 0x80 ; 64 kbyte + mov ax, cs + add ax, 'DS' + mov ds, ax + add ax, 'ES' + mov es, ax + + std + rep + movsw + cld + + sub [byte cs:si+do_copy+6+2], byte 0x10 + jnc do_copy + xchg ax, dx + scasw + lodsw +%ifdef __EXERELPU__ + push cs +%endif; __EXEMAIN4__ + push cs + push cs + push es + pop ds + pop es + push ss + mov bp, 'BP' ; entry point [0x1,0x10] + mov bx, 'BX' ; 0x800F + 0x10*bp - 0x10 + push bp + retf + +%include "header.ash" + +; __EXECUTPO__ + +; ============= +; ============= DECOMPRESSION +; ============= + +%include "n2b_d8e.ash" +%include "n2d_d8e.ash" + +; ============= +; ============= RELOCATION +; ============= +; __EXEMAIN5__ + pop bp +%ifdef __EXERELOC__ +%ifdef __EXEADJUS__ + mov ax, es + sub ah, 0x6 ; MAXRELOCS >> 12 + mov ds, ax +%else; __EXENOADJ__ + push es + pop ds +%endif; __EXERELO1__ + lea si, [di+'RS'] + lodsw + + pop bx + + xchg ax, cx ; number of 0x01 bytes (not exactly) + lodsw + xchg ax, dx ; seg_hi +reloc_0: + lodsw + xchg ax, di + lodsw + add bx, ax + mov es, bx + xor ax, ax +reloc_1: + add di, ax + add [es:di], bp +reloc_2: + lodsb + dec ax + jz reloc_5 + inc ax + jnz reloc_1 +%ifdef __EXEREL9A__ + inc di +reloc_4: + inc di + cmp byte [es:di], 0x9a + jne reloc_4 + cmp [es:di+3], dx + ja reloc_4 + mov al, 3 + jmps reloc_1 +%endif; __EXERELO2__ +reloc_5: + add di, 0xfe +%ifdef __EXEREBIG__ + jc reloc_0 +%endif; __EXERELO3__ + loop reloc_2 +%endif; __EXEMAIN8__ + +; ============= + + pop es + push es + pop ds +%ifdef __EXESTACK__ + lea ax, ['SS'+bp] + mov ss, ax +%endif; __EXEDUMMS__ +%ifdef __EXESTASP__ + mov sp, 'SP' +%endif; __EXEDUMMP__ + +; ============= + +%ifdef __EXEJUMPF__ + jmp 'CS':'IP' +%else; __EXERETUR__ +%ifdef __EXERCSPO__ + add bp, 'CS' +%endif; __EXERETIP__ + push bp + mov ax, 'IP' + push ax + retf +%endif; __EXEDUMMZ__ +eof: +; __EXETHEND__ + section .data + dd -1 + dw eof + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_lx_elf.c b/src/stub/l_lx_elf.c new file mode 100644 index 0000000000..5fc0a7ff07 --- /dev/null +++ b/src/stub/l_lx_elf.c @@ -0,0 +1,385 @@ +/* l_lx_elf.c -- stub loader for Linux x86 ELF executable + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + Integration of virtual exec() with decompression is + Copyright (C) 2000 John F. Reiser. All rights reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + John F. Reiser + jreiser@BitWagon.com + */ + + +#if !defined(__linux__) || !defined(__i386__) +# error "this stub must be compiled under linux/i386" +#endif + +#include "linux.hh" + + +/************************************************************************* +// configuration section +**************************************************************************/ + +// In order to make it much easier to move this code at runtime and execute +// it at an address different from it load address: there must be no +// static data, and no string constants. + + +#define PAGEMASK (~0u<<12) // discards the offset, keeps the page +#define PAGESIZE ( 1u<<12) +#define MAX_ELF_HDR 512 // Elf32_Ehdr + n*Elf32_Phdr must fit in this + + +/************************************************************************* +// "file" util +**************************************************************************/ + +struct Extent { + size_t size; // must be first to match size[0] uncompressed size + char *buf; +}; + + +static void +xread(struct Extent *x, char *buf, size_t count) +{ + char *p=x->buf, *q=buf; + size_t j; + if (x->size < count) { + exit(127); + } + for (j = count; 0!=j--; ++p, ++q) { + *q = *p; + } + x->buf += count; + x->size -= count; +} + + +/************************************************************************* +// util +**************************************************************************/ + +#if 0 //{ save space +#define ERR_LAB error: exit(127); +#define err_exit(a) goto error +#else //}{ save debugging time +#define ERR_LAB +static void +err_exit(int a) +{ + (void)a; // debugging convenience + exit(127); +} +#endif //} + +static void * +do_brk(void *addr) +{ + return brk(addr); +} + +static char * +do_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + (void)len; (void)prot; (void)flags; (void)fd; (void)offset; + return mmap((int *)&addr); +} + + +/************************************************************************* +// UPX & NRV stuff +**************************************************************************/ + +typedef int f_expand( + const nrv_byte *, nrv_uint, + nrv_byte *, nrv_uint * ); + +static void +unpackExtent( + struct Extent *const xi, // input + struct Extent *const xo, // output + f_expand *const f_decompress +) +{ + while (xo->size) { + struct { + int32_t sz_unc; // uncompressed + int32_t sz_cpr; // compressed + } h; + // Note: if h.sz_unc == h.sz_cpr then the block was not + // compressible and is stored in its uncompressed form. + + // Read and check block sizes. + xread(xi, (char *)&h, sizeof(h)); + if (h.sz_unc == 0) { // uncompressed size 0 -> EOF + if (h.sz_cpr != UPX_MAGIC_LE32) // h.sz_cpr must be h->magic + err_exit(2); + if (xi->size != 0) // all bytes must be written + err_exit(3); + break; + } + if (h.sz_cpr <= 0) { + err_exit(4); +ERR_LAB + } + if (h.sz_cpr > h.sz_unc + || h.sz_unc > (int32_t)xo->size ) { + err_exit(5); + } + // Now we have: + // assert(h.sz_cpr <= h.sz_unc); + // assert(h.sz_unc > 0 && h.sz_unc <= blocksize); + // assert(h.sz_cpr > 0 && h.sz_cpr <= blocksize); + + if (h.sz_cpr < h.sz_unc) { // Decompress block + nrv_uint out_len; + int const j = (*f_decompress)(xi->buf, h.sz_cpr, xo->buf, &out_len); + if (j != 0 || out_len != (nrv_uint)h.sz_unc) + err_exit(7); + xi->buf += h.sz_cpr; + xi->size -= h.sz_cpr; + } + else { // copy literal block + xread(xi, xo->buf, h.sz_cpr); + } + xo->buf += h.sz_unc; + xo->size -= h.sz_unc; + } +} + +// Create (or find) an escape hatch to use when munmapping ourselves the stub. +// Called by do_xmap to create it, and by assembler code to find it. +void * +make_hatch(Elf32_Phdr const *const phdr) +{ + if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) { + unsigned *hatch; + // The format of the 'if' is + // if ( ( (hatch = loc1), test_loc1 ) + // || ( (hatch = loc2), test_loc2 ) ) { + // action + // } + // which uses the comma to save bytes when test_locj involves locj + // and the action is the same when either test succeeds. + + // Try page fragmentation just beyond .text . + if ( ( (hatch = (void *)(phdr->p_memsz + phdr->p_vaddr)), + ( phdr->p_memsz==phdr->p_filesz // don't pollute potential .bss + && 4<=(~PAGEMASK & -(int)hatch) ) ) // space left on page + // Try Elf32_Ehdr.e_ident[12..15] . warning: 'const' cast away + || ( (hatch = (void *)(&((Elf32_Ehdr *)phdr->p_vaddr)->e_ident[12])), + (phdr->p_offset==0) ) ) { + // Omitting 'const' saves repeated literal in gcc. + unsigned /*const*/ escape = 0xc36180cd; // "int $0x80; popa; ret" + // Don't store into read-only page if value is already there. + if (*hatch != escape) { + *hatch = escape; + } + return hatch; + } + } + return 0; +} + +static void +bzero(char *p, size_t len) +{ + if (len) do { + *p++= 0; + } while (--len); +} + + +static Elf32_Addr // entry address +do_xmap(int const fdi, Elf32_Ehdr const *const ehdr, struct Extent *const xi, + Elf32_auxv_t *const a) +{ + Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff + + (char const *)ehdr); + unsigned long base = (ET_DYN==ehdr->e_type) ? 0x40000000 : 0; + int j; + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) + if (PT_PHDR==phdr->p_type) { + a->a_un.a_val = phdr->p_vaddr; + } + else if (PT_LOAD==phdr->p_type) { + struct Extent xo; + size_t mlen = xo.size = phdr->p_filesz; + char *addr = xo.buf = (char *)phdr->p_vaddr; + char *haddr = phdr->p_memsz + (char *)phdr->p_vaddr; + size_t frag = (int)addr &~ PAGEMASK; + mlen += frag; + addr -= frag; + if (ET_DYN==ehdr->e_type) { + addr += base; + haddr += base; + } + else { // There is only one brk, the one for the ET_EXEC + // Not needed if compressed a.elf is invoked directly. + // Needed only if compressed shell script invokes compressed shell. + do_brk(haddr+OVERHEAD); // Also takes care of whole pages of .bss + } + // Decompressor can overrun the destination by 3 bytes. + if (addr != do_mmap(addr, mlen + (xi ? 3 : 0), PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | (xi ? MAP_ANONYMOUS : 0), + fdi, phdr->p_offset - frag) ) { + err_exit(8); + } + if (0==base) { + base = (unsigned long)addr; + } + if (xi) { + unpackExtent(xi, &xo, (f_expand *)fdi); + } + bzero(addr, frag); // fragment at lo end + frag = (-mlen) &~ PAGEMASK; // distance to next page boundary + bzero(mlen+addr, frag); // fragment at hi end + if (xi) { + make_hatch(phdr); + } + if (phdr->p_memsz != phdr->p_filesz) { // .bss + if (ET_DYN==ehdr->e_type) { // PT_INTERP whole pages of .bss? + addr += frag + mlen; + mlen = haddr - addr; + if (0 < (int)mlen) { // need more pages, too + if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ) ) { + err_exit(9); +ERR_LAB + } + } + } + } + else { // no .bss + int prot = 0; + if (phdr->p_flags & PF_X) { prot |= PROT_EXEC; } + if (phdr->p_flags & PF_W) { prot |= PROT_WRITE; } + if (phdr->p_flags & PF_R) { prot |= PROT_READ; } + if (0!=mprotect(addr, mlen, prot)) { + err_exit(10); + } + if (xi) { // cleanup if decompressor overrun crosses page boundary + mlen += 3; + addr += mlen; + mlen &= ~PAGEMASK; + if (mlen<=3) { // page fragment was overrun buffer only + munmap(addr - mlen, mlen); + } + } + } + if (ET_DYN!=ehdr->e_type) { + // Needed only if compressed shell script invokes compressed shell. + do_brk(haddr); + } + } + if (!xi) { + if (0!=close(fdi)) { + err_exit(11); + } + } + if (ET_DYN==ehdr->e_type) { + return ehdr->e_entry + base; + } + else { + return ehdr->e_entry; + } +} + + +/************************************************************************* +// upx_main - called by our entry code +// +// This function is optimized for size. +**************************************************************************/ + +void *upx_main( + char *const uncbuf, + Elf32_Ehdr const *const my_ehdr, + f_expand *const f_decompress, + Elf32_auxv_t *const av, + Elf32_Ehdr *const ehdr +) __asm__("upx_main"); + +void *upx_main( + char *const uncbuf, + Elf32_Ehdr const *const my_ehdr, // to get compressed size and data + f_expand *const f_decompress, + Elf32_auxv_t *const av, + Elf32_Ehdr *const ehdr // temp char[MAX_ELF_HDR+OVERHEAD] +) +{ + size_t const lsize = *(unsigned short const *)(0x7c + (char const *)my_ehdr); + Elf32_Phdr const *phdr = (Elf32_Phdr const *)(1+ehdr); + Elf32_Addr entry; + struct Extent xo; + struct Extent xi = { 0, (sizeof(struct p_info) + lsize + (char *)my_ehdr) }; + // warning: 'const' cast away + + size_t const sz_elfhdrs = ((size_t *)xi.buf)[0]; // sizeof(Ehdr+Phdrs), uncompressed + size_t const sz_pckhdrs = ((size_t *)xi.buf)[1]; // sizeof(Ehdr+Phdrs), compressed + + (void)uncbuf; // used by l_lx_sh.c + // Uncompress Ehdr and Phdrs. + xo.size = sz_elfhdrs; xo.buf = (char *)ehdr; + xi.size = 2*sizeof(size_t) + sz_pckhdrs; + unpackExtent(&xi, &xo, f_decompress); + + // Prepare to decompress the Elf headers again, into the first PT_LOAD. + xi.buf -= 2*sizeof(size_t) + sz_pckhdrs; + xi.size = ((Elf32_Phdr const *)(1 + my_ehdr))->p_filesz - lsize; + + av[0].a_type = AT_PHDR; // av[0].a_un.a_val is set by do_xmap + av[1].a_type = AT_PHENT; av[1].a_un.a_val = ehdr->e_phentsize; + av[2].a_type = AT_PHNUM; av[2].a_un.a_val = ehdr->e_phnum; + av[3].a_type = AT_PAGESZ; av[3].a_un.a_val = PAGESIZE; + av[4].a_type = AT_ENTRY; av[4].a_un.a_val = ehdr->e_entry; + av[5].a_type = AT_NULL; + entry = do_xmap((int)f_decompress, ehdr, &xi, av); + + { // Map PT_INTERP program interpreter + int j; + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) if (PT_INTERP==phdr->p_type) { + char const *const iname = (char const *)phdr->p_vaddr; + int const fdi = open(iname, O_RDONLY, 0); + if (0 > fdi) { + err_exit(18); + } + if (MAX_ELF_HDR!=read(fdi, (void *)ehdr, MAX_ELF_HDR)) { + err_exit(19); + } + entry = do_xmap(fdi, ehdr, 0, 0); + break; + } + } + + return (void *)entry; +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/stub/l_lx_elf86.asm b/src/stub/l_lx_elf86.asm new file mode 100644 index 0000000000..d37d92fe49 --- /dev/null +++ b/src/stub/l_lx_elf86.asm @@ -0,0 +1,281 @@ +; l_lx_elf86.asm -- Linux program entry point & decompressor (Elf binary) +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; Integration of virtual exec() with decompression is +; Copyright (C) 2000 John F. Reiser. All rights reserved. +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; +; John F. Reiser +; jreiser@BitWagon.com + + + BITS 32 + SECTION .text + +%define jmps jmp short + +; defines for ident.ash and n2b_d32.ash +%ifdef SMALL + %define __IDENTSMA__ + %define __N2BSMA10__ + %define __N2BSMA20__ + %define __N2BSMA30__ + %define __N2BSMA40__ + %define __N2BSMA50__ + %define __N2BSMA60__ + %define __N2DSMA10__ + %define __N2DSMA20__ + %define __N2DSMA30__ + %define __N2DSMA40__ + %define __N2DSMA50__ + %define __N2DSMA60__ +%endif + + + +%include "ident.ash" + +; /************************************************************************* +; // program entry point +; // see glibc/sysdeps/i386/elf/start.S +; **************************************************************************/ + +GLOBAL _start + +_start: +;;;; int3 +;; How to debug this code: Uncomment the 'int3' breakpoint instruction above. +;; Build the stubs and upx. Compress a testcase, such as a copy of /bin/date. +;; Invoke gdb, and give a 'run' command. Define a single-step macro such as +;; define g +;; stepi +;; x/i $pc +;; end +;; and a step-over macro such as +;; define h +;; x/2i $pc +;; tbreak *$_ +;; continue +;; x/i $pc +;; end +;; Step through the code; remember that repeats the previous command. +;; + call main ; push address of decompress subroutine + +; /************************************************************************* +; // C callable decompressor +; **************************************************************************/ + +%define INP dword [esp+8*4+4] +%define INS dword [esp+8*4+8] +%define OUTP dword [esp+8*4+12] +%define OUTS dword [esp+8*4+16] + +decompress: + pusha + ; cld + + mov esi, INP + mov edi, OUTP + + or ebp, byte -1 +;;; align 8 +%ifdef NRV2B + %include "n2b_d32.ash" +%elifdef NRV2D + %include "n2d_d32.ash" +%else + %error +%endif + + + ; eax is 0 from decompressor code + ;xor eax, eax ; return code + +; check compressed size + mov edx, INP + add edx, INS + cmp esi, edx + jz .ok + dec eax +.ok: + +; write back the uncompressed size + sub edi, OUTP + mov edx, OUTS + mov [edx], edi + + mov [7*4 + esp], eax + popa + ret + + +%define PAGE_MASK (~0<<12) +%define PAGE_SIZE ( 1<<12) + +%define szElf32_Ehdr 0x34 +%define szElf32_Phdr 8*4 +%define p_filesz 4*4 +%define p_memsz 5*4 +%define a_val 4 + +%define MAP_FIXED 0x10 +%define MAP_PRIVATE 0x02 +%define MAP_ANONYMOUS 0x20 +%define PROT_READ 1 +%define PROT_WRITE 2 +%define PROT_EXEC 4 +%define __NR_mmap 90 +%define __NR_munmap 91 + +; Decompress the rest of this loader, and jump to it +unfold: + pop esi ; &{ sz_uncompressed, sz_compressed, compressed_data...} + cld + lodsd + push eax ; sz_uncompressed (junk, actually) + push esp ; &sz_uncompressed + mov eax, ebp ; &decompress + and eax, dword PAGE_MASK ; &my_elfhdr + mov edx, eax ; need my_elfhdr later + mov ah,0 ; round down to 64KB boundary + push eax ; &destination + + ; mmap a page to hold the decompressed program + xor ecx,ecx + push ecx + push ecx + mov ch, PAGE_SIZE >> 8 + push byte MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS + push byte PROT_READ | PROT_WRITE | PROT_EXEC + push ecx + push eax ; destination + push byte __NR_mmap + pop eax + mov ebx, esp + int 0x80 + add esp, byte 6*4 ; discard args to mmap + + lodsd + push eax ; sz_compressed + push esi ; &compressed_data + call ebp ; decompress(&src, srclen, &dst, &dstlen) + pop eax ; discard &compressed_data + pop eax ; discard sz_compressed + ret ; &destination +main: + pop ebp ; &decompress + call unfold +fold_begin: + ; patchLoader will modify to be + ; dword sz_uncompressed, sz_compressed + ; byte compressed_data... + + pop eax ; discard &sz_uncompressed + pop eax ; discard sz_uncompressed + +; Move argc,argv,envp down so that we can insert more Elf_auxv entries. +; ld-linux.so.2 depends on AT_PHDR and AT_ENTRY, for instance + +%define OVERHEAD 2048 +%define MAX_ELF_HDR 512 + + mov esi, esp + sub esp, byte 6*8 ; AT_PHENT, AT_PHNUM, AT_PAGESZ, AT_ENTRY, AT_PHDR, AT_NULL + mov edi, esp + call do_auxv + + sub esp, dword MAX_ELF_HDR + OVERHEAD + push esp ; argument: temp space + push edi ; argument: AT_next + push ebp ; argument: &decompress + push edx ; argument: my_elfhdr + add edx, [p_memsz + szElf32_Ehdr + edx] + push edx ; argument: uncbuf +EXTERN upx_main + call upx_main ; entry = upx_main(uncbuf, my_elfhdr, &decompress, AT_next, tmp_ehdr) + pop esi ; decompression buffer == (p_vaddr + p_memsz) of stub + pop ebx ; my_elfhdr + add esp, dword 3*4 + MAX_ELF_HDR + OVERHEAD ; remove 3 params, temp space + push eax ; save entry address + + mov edi, [a_val + edi] ; AT_PHDR +find_hatch: + push edi +EXTERN make_hatch + call make_hatch ; find hatch = make_hatch(phdr) + pop ecx ; junk the parameter + add edi, byte szElf32_Phdr ; prepare to try next Elf32_Phdr + test eax,eax + jz find_hatch + xchg eax,edx ; edx= &hatch + +; _dl_start and company (ld-linux.so.2) assumes that it has virgin stack, +; and does not initialize all its stack local variables to zero. +; Ulrich Drepper (drepper@cyngus.com) has refused to fix the bugs. +; See GNU wwwgnats libc/1165 . + +%define N_STKCLR (0x100 + MAX_ELF_HDR + OVERHEAD)/4 + lea edi, [esp - 4*N_STKCLR] + pusha ; values will be zeroed + mov ecx, N_STKCLR + xor eax,eax + rep stosd + + mov ecx,esi ; my p_vaddr + p_memsz + mov bh,0 ; round down to 64KB boundary + sub ecx,ebx ; length to unmap + push byte __NR_munmap + pop eax + jmp edx ; unmap ourselves via escape hatch, then goto entry + +do_auxv: ; entry: %esi=src = &argc; %edi=dst. exit: %edi= &AT_NULL + ; cld + +L10: ; move argc+argv + lodsd + stosd + test eax,eax + jne L10 + +L20: ; move envp + lodsd + stosd + test eax,eax + jne L20 + +L30: ; move existing Elf32_auxv + lodsd + stosd + test eax,eax ; AT_NULL ? + lodsd + stosd + jne L30 + + sub edi, byte 8 ; point to AT_NULL + ret + + +; vi:ts=8:et:nowrap + diff --git a/src/stub/l_lx_elf86.lds b/src/stub/l_lx_elf86.lds new file mode 100644 index 0000000000..e5ae76e352 --- /dev/null +++ b/src/stub/l_lx_elf86.lds @@ -0,0 +1,17 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + /* 0x00401000: l_lx_elf86.asm assumes 1 page up from 64KB boundary */ + . = 0x00401000 + SIZEOF_HEADERS; + . = ALIGN(0x80); + .text : { + *(.text) + *(.data) + } + /* 0x08048000: customary Linux/x86 Elf .text start */ + . = 0x08048000 + (0xfff & .); + .data : { + } +} diff --git a/src/stub/l_lx_exec.c b/src/stub/l_lx_exec.c new file mode 100644 index 0000000000..79cfcf97b5 --- /dev/null +++ b/src/stub/l_lx_exec.c @@ -0,0 +1,495 @@ +/* l_lx_exec.c -- generic stub loader for Linux using execve() + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#if !defined(__linux__) || !defined(__i386__) +# error "this stub must be compiled under linux/i386" +#endif + +#include "linux.hh" + + +/************************************************************************* +// configuration section +**************************************************************************/ + +// use malloc instead of the bss segement +#define USE_MALLOC + + +/************************************************************************* +// file util +**************************************************************************/ + +#undef xread +#undef xwrite + +#if 1 +//static int xread(int fd, void *buf, int count) __attribute__((__stdcall__)); +static int xread(int fd, void *buf, int count) +{ + // note: we can assert(count > 0); + do { + int n = read(fd, buf, count); + if (n == -EINTR) + continue; + if (n <= 0) + break; + buf += n; // gcc extension: add to void * + count -= n; + } while (count > 0); + return count; +} +#else +#define xread(fd,buf,count) ((count) - read(fd,buf,count)) +#endif + + +#if 1 +static __inline__ int xwrite(int fd, const void *buf, int count) +{ + // note: we can assert(count > 0); + do { + int n = write(fd, buf, count); + if (n == -EINTR) + continue; + if (n <= 0) + break; + buf += n; // gcc extension: add to void * + count -= n; + } while (count > 0); + return count; +} +#else +#define xwrite(fd,buf,count) ((count) - write(fd,buf,count)) +#endif + + +/************************************************************************* +// util +**************************************************************************/ + +static char *upx_itoa(char *buf, unsigned long v) +{ + char *p = buf; + { + unsigned long k = v; + do { + p++; + k /= 10; + } while (k > 0); + } + buf = p; + *p = 0; + { + unsigned long k = v; + do { + *--p = '0' + k % 10; + k /= 10; + } while (k > 0); + } + return buf; +} + + +#if defined(__i386__) +# define SET2(p, c0, c1) \ + * (unsigned short *) (p) = ((c1)<<8 | (c0)) +# define SET4(p, c0, c1, c2, c3) \ + * (uint32_t *) (p) = ((c3)<<24 | (c2)<<16 | (c1)<<8 | (c0)) +# define SET3(p, c0, c1, c2) \ + SET4(p, c0, c1, c2, 0) +#else +# define SET2(p, c0, c1) \ + (p)[0] = c0, (p)[1] = c1 +# define SET3(p, c0, c1, c2) \ + (p)[0] = c0, (p)[1] = c1, (p)[2] = c2 +# define SET4(p, c0, c1, c2, c3) \ + (p)[0] = c0, (p)[1] = c1, (p)[2] = c2, (p)[3] = c3 +#endif + + +/************************************************************************* +// UPX & NRV stuff +**************************************************************************/ + +// must be the same as in p_unix.cpp ! +#if !defined(USE_MALLOC) +# define BLOCKSIZE (512*1024) +#endif + + +// patch constants for our loader (le32 format) +#define UPX1 0x31585055 // "UPX1" +#define UPX2 0x32585055 // "UPX2" +#define UPX3 0x33585055 // "UPX4" +#define UPX4 0x34585055 // "UPX4" +#define UPX5 0x35585055 // "UPX5" + + +#if defined(__i386__) +extern int +nrv2b_decompress_asm_fast ( const nrv_byte *src, nrv_uint src_len, + nrv_byte *dst, nrv_uint *dst_len ); +#define nrv2b_decompress nrv2b_decompress_asm_fast +extern int +nrv2d_decompress_asm_fast ( const nrv_byte *src, nrv_uint src_len, + nrv_byte *dst, nrv_uint *dst_len ); +#define nrv2d_decompress nrv2d_decompress_asm_fast +#endif /* __i386__ */ + + +/************************************************************************* +// upx_main - called by our entry code +// +// This function is optimized for size. +**************************************************************************/ + +void upx_main(char *argv[], char *envp[]) __asm__("upx_main"); +void upx_main(char *argv[], char *envp[]) +{ + // file descriptors + int fdi, fdo; + + struct p_info header; + + // for getpid() + pid_t pid; + + // temporary file name (max 14 chars) + static char tmpname_buf[] = "/tmp/upxAAAAAAAAAAA"; + char *tmpname = tmpname_buf; + char procself_buf[64]; + char *procself; + + // decompression buffer +#if defined(USE_MALLOC) + unsigned char *buf; + static int malloc_args[6] = { + 0, UPX5, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 + }; +#else + static unsigned char buf[BLOCKSIZE + OVERHEAD]; +#endif + + // + // ----- Step 0: set /proc/self using /proc/ ----- + // + + //personality(PER_LINUX); + pid = getpid(); + SET4(procself_buf + 0, '/', 'p', 'r', 'o'); + SET2(procself_buf + 4, 'c', '/'); + procself = upx_itoa(procself_buf + 6, pid); + *procself++ = '/'; + + + // + // ----- Step 1: prepare input file ----- + // + + // Open the exe. + SET3(procself, 'e', 'x', 'e'); + fdi = open(procself_buf, O_RDONLY, 0); +#if 1 + // try /proc//file for the sake of FreeBSD + if (fdi < 0) + { + SET4(procself, 'f', 'i', 'l', 'e'); + fdi = open(procself_buf, O_RDONLY, 0); + } +#endif +#if 0 + // Save some bytes of code - the lseek() below will fail anyway. + if (fdi < 0) + goto error1; +#endif + + // Seek to start of compressed data. The offset is patched + // by the compressor. + if (lseek(fdi, UPX1, 0) < 0) + goto error1; + // Read header. + if (xread(fdi, (void *)&header, sizeof(header)) != 0) + goto error1; + // Paranoia. Make sure this is actually our expected executable + // by checking the random program id. (The id is both stored + // in the header and patched into this stub.) + if (header.p_progid != UPX2) + goto error1; + + + // + // ----- Step 2: prepare temporary output file ----- + // + + // Compute name of temporary output file in tmpname[]. + // Protect against Denial-of-Service attacks. + { + char *p = tmpname_buf + sizeof(tmpname_buf) - 1; + uint32_t r; + + // Compute the last 4 characters (20 bits) from getpid(). + { + unsigned k = 4; + r = (uint32_t) pid; + do { + unsigned char d = r % 32; + if (d >= 26) d += '0' - 'Z' - 1; + *--p += d; + r /= 32; + } while (--k > 0); + } + + // Provide 4 random bytes from our program id. + r ^= header.p_progid; + // Mix in 4 runtime random bytes. + // Don't consume precious bytes from /dev/urandom. + { +#if 1 + struct timeval tv; + gettimeofday(&tv, 0); + r ^= (uint32_t) tv.tv_sec; + r ^= ((uint32_t) tv.tv_usec) << 12; // shift into high-bits +#else + // using adjtimex() may cause portability problems + static struct timex tx; + adjtimex(&tx); + r ^= (uint32_t) tx.time.tv_sec; + r ^= ((uint32_t) tx.time.tv_usec) << 12; // shift into high-bits + r ^= (uint32_t) tx.errcnt; +#endif + } + // Compute 7 more characters from the 32 random bits. + { + unsigned k = 7; + do { + unsigned char d = r % 32; + if (d >= 26) d += '0' - 'Z' - 1; + *--p += d; + r /= 32; + } while (--k > 0); + } + } + + // Just in case, remove the file. + { + int err = unlink(tmpname); + if (err != -ENOENT && err != 0) + goto error1; + } + + // Create the temporary output file. + fdo = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, 0700); +#if 0 + // Save some bytes of code - the ftruncate() below will fail anyway. + if (fdo < 0) + goto error; +#endif + + // Set expected file size. + if (ftruncate(fdo, header.p_filesize) != 0) + goto error; + + + // + // ----- Step 3: setup memory ----- + // + +#if defined(USE_MALLOC) + buf = mmap(malloc_args); + if ((unsigned long) buf >= (unsigned long) -4095) + goto error; +#else + if (header.p_blocksize > BLOCKSIZE) + goto error; +#endif + + + // + // ----- Step 4: decompress blocks ----- + // + + for (;;) + { + int32_t size[2]; + // size[0]: uncompressed block size + // size[1]: compressed block size + // Note: if size[0] == size[1] then the block was not + // compressible and is stored in its uncompressed form. + int i; + + // Read and check block sizes. + if (xread(fdi, (void *)size, 8) != 0) + goto error; + if (size[0] == 0) // uncompressed size 0 -> EOF + { + if (size[1] != UPX_MAGIC_LE32) // size[1] must be h->magic + goto error; + if (header.p_filesize != 0) // all bytes must be written + goto error; + break; + } + if (size[1] <= 0) + goto error; + if (size[1] > size[0] || size[0] > (int32_t)header.p_blocksize) + goto error; + // Now we have: + // assert(size[1] <= size[0]); + // assert(size[0] > 0 && size[0] <= blocksize); + // assert(size[1] > 0 && size[1] <= blocksize); + + // Read compressed block. + i = header.p_blocksize + OVERHEAD - size[1]; + if (xread(fdi, buf+i, size[1]) != 0) + goto error; + + // Decompress block. + if (size[1] < size[0]) + { + // in-place decompression + nrv_uint out_len; +#if defined(NRV2B) + i = nrv2b_decompress(buf+i, size[1], buf, &out_len); +#elif defined(NRV2D) + i = nrv2d_decompress(buf+i, size[1], buf, &out_len); +#else +# error +#endif + if (i != 0 || out_len != (nrv_uint)size[0]) + goto error; + // i == 0 now + } + + // Write uncompressed block. + if (xwrite(fdo, buf+i, size[0]) != 0) + { +// error exit is here in the middle to keep the jumps short. + error: + (void) unlink(tmpname); + error1: + // Note: the kernel will close all open files and + // unmap any allocated memory. + for (;;) + (void) exit(127); + } + header.p_filesize -= size[0]; + } + + + // + // ----- Step 5: release resources ----- + // + +#if defined(USE_MALLOC) + munmap(buf, malloc_args[1]); +#endif + + if (close(fdo) != 0) + goto error; + if (close(fdi) != 0) + goto error; + + + // + // ----- Step 6: try to start program via /proc/self/fd/X ----- + // + + // Many thanks to Andi Kleen and + // Jamie Lokier for this nice idea. + + // Open the temp file. + fdi = open(tmpname, O_RDONLY, 0); + if (fdi < 0) + goto error; + + // Compute name of temp fdi. + SET3(procself, 'f', 'd', '/'); + upx_itoa(procself + 3, fdi); + + // Check for working /proc/self/fd/X by accessing the + // temp file again, now via temp fdi. +#define err fdo + err = access(procself_buf, R_OK | X_OK); + if (err == UPX3) + { + // Now it's safe to unlink the temp file (as it is still open). + unlink(tmpname); + // Set the file close-on-exec. + fcntl(fdi, F_SETFD, FD_CLOEXEC); + // Execute the original program via /proc/self/fd/X. + execve(procself_buf, argv, envp); + // If we get here we've lost. + } +#undef err + + // The proc filesystem isn't working. No problem. + close(fdi); + + + // + // ----- Step 7: start program in /tmp ----- + // + + // Fork off a subprocess to clean up. + // We have to do this double-fork trick to keep a zombie from + // hanging around if the spawned original program doesn't check for + // subprocesses (as well as to prevent the real program from getting + // confused about this subprocess it shouldn't have). + // Thanks to Adam Ierymenko for this solution. + + if (fork() == 0) + { + if (fork() == 0) + { + // Sleep 3 seconds, then remove the temp file. + static const struct timespec ts = { UPX4, 0 }; + nanosleep(&ts, 0); + unlink(tmpname); + } + exit(0); + } + + // Wait for the first fork()'d process to die. + waitpid(-1, (int *)0, 0); + + // Execute the original program. + execve(tmpname, argv, envp); + + + // + // ----- Step 8: error exit ----- + // + + // If we return from execve() there was an error. Give up. + goto error; +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/stub/l_lx_exec86.asm b/src/stub/l_lx_exec86.asm new file mode 100644 index 0000000000..f671dd2823 --- /dev/null +++ b/src/stub/l_lx_exec86.asm @@ -0,0 +1,148 @@ +; l_lx_exec86.asm -- Linux program entry point & decompressor (execve) +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + + BITS 32 + SECTION .text + +%define jmps jmp short + +; defines for ident.ash and n2b_d32.ash +%ifdef SMALL + %define __IDENTSMA__ + %define __N2BSMA10__ + %define __N2BSMA20__ + %define __N2BSMA30__ + %define __N2BSMA40__ + %define __N2BSMA50__ + %define __N2BSMA60__ + %define __N2DSMA10__ + %define __N2DSMA20__ + %define __N2DSMA30__ + %define __N2DSMA40__ + %define __N2DSMA50__ + %define __N2DSMA60__ +%endif + + +; /************************************************************************* +; // program entry point +; // see glibc/sysdeps/i386/elf/start.S +; **************************************************************************/ + +GLOBAL _start +EXTERN upx_main + +_start: + xor ebp, ebp ; Clear the frame pointer +%if 0 + ; personality(PER_LINUX) + mov eax, 136 ; syscall_personality + xor ebx, ebx ; PER_LINUX + int 0x80 +%endif + pop eax ; Pop the argument count + mov ecx, esp ; argv starts just at the current stack top + lea edx, [ecx+eax*4+4] ; envp = &argv[argc + 1] + push eax ; Restore the stack + and esp, byte -8 ; Align the stack + push edx ; Push third argument: envp + push ecx ; Push second argument: argv +;;; push eax ; Push first argument: argc + call upx_main ; Call the UPX main function + hlt ; Crash if somehow upx_main does return + +%include "ident.ash" + + +; /************************************************************************* +; // C callable decompressor +; **************************************************************************/ + +%ifdef NRV2B + %define decompress nrv2b_decompress_asm_fast +%elifdef NRV2D + %define decompress nrv2d_decompress_asm_fast +%else + %error +%endif + +GLOBAL decompress + +%define INP dword [esp+24+4] +%define INS dword [esp+24+8] +%define OUTP dword [esp+24+12] +%define OUTS dword [esp+24+16] + +decompress: + push ebp + push edi + push esi + push ebx + push ecx + push edx + cld + + mov esi, INP + mov edi, OUTP + + or ebp, byte -1 +;;; align 8 +%ifdef NRV2B + %include "n2b_d32.ash" +%elifdef NRV2D + %include "n2d_d32.ash" +%else + %error +%endif + + + ; eax is 0 from decompressor code + ;xor eax, eax ; return code + +; check compressed size + mov edx, INP + add edx, INS + cmp esi, edx + jz .ok + dec eax +.ok: + +; write back the uncompressed size + sub edi, OUTP + mov edx, OUTS + mov [edx], edi + + pop edx + pop ecx + pop ebx + pop esi + pop edi + pop ebp + ret + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_lx_sep.c b/src/stub/l_lx_sep.c new file mode 100644 index 0000000000..40049ddd41 --- /dev/null +++ b/src/stub/l_lx_sep.c @@ -0,0 +1,449 @@ +/* l_lxsep.c -- separate loader for Linux Elf executable + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + Integration of virtual exec() with decompression is + Copyright (C) 2000 John F. Reiser. All rights reserved. + + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + */ + + +#if !defined(__linux__) || !defined(__i386__) +# error "this stub must be compiled under linux/i386" +#endif + +#include +#include +#include +#include +#include + +#include "linux.hh" + +/************************************************************************* +// configuration section +**************************************************************************/ + +// must be the same as in p_linux.cpp ! +#define OVERHEAD 2048 + +#define PAGEMASK (~0u<<12) // discards the offset, keeps the page +#define PAGESIZE ( 1u<<12) +#define MAX_ELF 512 // Elf32_Ehdr + n*Elf32_Phdr must fit in this + +#undef int32_t +#undef uint32_t +#define int32_t int +#define uint32_t unsigned int + +#define SEEK_SET 0 +#define SEEK_CUR 1 + +/************************************************************************* +// file util +**************************************************************************/ + +#undef xread +#undef xwrite + +#if 1 +//static int xread(int fd, void *buf, int count) __attribute__((__stdcall__)); +static int xread(int fd, void *buf, int count) +{ + // note: we can assert(count > 0); + do { + int n = read(fd, buf, count); + if (n == -EINTR) + continue; + if (n <= 0) + break; + buf += n; // gcc extension: add to void * + count -= n; + } while (count > 0); + return count; +} +#else +#define xread(fd,buf,count) ((count) - read(fd,buf,count)) +#endif + + +#if 1 +static __inline__ int xwrite(int fd, const void *buf, int count) +{ + // note: we can assert(count > 0); + do { + int n = write(fd, buf, count); + if (n == -EINTR) + continue; + if (n <= 0) + break; + buf += n; // gcc extension: add to void * + count -= n; + } while (count > 0); + return count; +} +#else +#define xwrite(fd,buf,count) ((count) - write(fd,buf,count)) +#endif + + +/************************************************************************* +// util +**************************************************************************/ + +#if 1 //{ save space +#define ERR_LAB error: exit(127); +#define err_exit(a) goto error +#else //}{ save debugging time +#define ERR_LAB +static void +err_exit(int a) +{ + (void)a; // debugging convenience + exit(127); +} +#endif //} + +static void * +do_brk(void *addr) +{ + return brk(addr); +} + +static char * +do_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + (void)len; (void)prot; (void)flags; (void)fd; (void)offset; + return mmap((int *)&addr); +} + +/************************************************************************* +// UPX & NRV stuff +**************************************************************************/ + + +// patch & magic constants for our loader (le32 format) +#define UPX_MAGIC_LE32 0x21585055 // "UPX!" + +typedef int f_expand( + const nrv_byte *, nrv_uint, + nrv_byte *, nrv_uint * ); + +struct Extent { + size_t size; // must be first to match size[0] uncompressed size + char *buf; +}; + +static void +unpackExtent( + struct Extent *const xo, + int fdi, + f_expand *const f_decompress +) +{ + while (xo->size) { + struct { + int32_t sz_unc; // uncompressed + int32_t sz_cpr; // compressed + } h; + // Note: if h.sz_unc == h.sz_cpr then the block was not + // compressible and is stored in its uncompressed form. + int j = 0; + + // Read and check block sizes. + if (xread(fdi, (void *)&h, sizeof(h)) != 0) + err_exit(1); + if (h.sz_unc == 0) // uncompressed size 0 -> EOF + { + if (h.sz_cpr != UPX_MAGIC_LE32) // h.sz_cpr must be h->magic + err_exit(2); + if (xo->size != 0) // all bytes must be written + err_exit(3); + break; + } + if (h.sz_cpr <= 0) { + err_exit(4); +ERR_LAB + } + if (h.sz_cpr > h.sz_unc || h.sz_unc > (int32_t)xo->size) { + err_exit(5); + } + // Now we have: + // assert(h.sz_cpr <= h.sz_unc); + // assert(h.sz_unc > 0 && h.sz_unc <= blocksize); + // assert(h.sz_cpr > 0 && h.sz_cpr <= blocksize); + + j = h.sz_unc - h.sz_cpr; + if (0 < j) { // Compressed block. + j += OVERHEAD; + } + if (0!=xread(fdi, xo->buf+j, h.sz_cpr)) { + err_exit(6); + } + + // Decompress block. + if (h.sz_cpr < h.sz_unc) { + // in-place decompression + nrv_uint out_len; + j = (*f_decompress)(xo->buf+j, h.sz_cpr, xo->buf, &out_len); + if (j != 0 || out_len != (nrv_uint)h.sz_unc) + err_exit(7); + // j == 0 now + } + xo->buf += h.sz_unc; + xo->size -= h.sz_unc; + } +} + +#include + +// Create (or find) an escape hatch to use when munmapping ourselves the stub. +// Called by do_xmap to create it, and by assembler code to find it. +void * +make_hatch(Elf32_Phdr const *const phdr) +{ + if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) { + unsigned *hatch; + // The format of the 'if' is + // if ( ( (hatch = loc1), test_loc1 ) + // || ( (hatch = loc2), test_loc2 ) ) { + // action + // } + // which uses the comma to save bytes when test_locj involves locj + // and the action is the same when either test succeeds. + + // Try page fragmentation just beyond .text . + if ( ( (hatch = (void *)(phdr->p_memsz + phdr->p_vaddr)), + ( phdr->p_memsz==phdr->p_filesz // don't pollute potential .bss + && 4<=(~PAGEMASK & -(int)hatch) ) ) // space left on page + // Try Elf32_Ehdr.e_ident[12..15] . warning: 'const' cast away + || ( (hatch = (void *)(&((Elf32_Ehdr *)phdr->p_vaddr)->e_ident[12])), + (phdr->p_offset==0) ) ) { + // Omitting 'const' saves repeated literal in gcc. + unsigned /*const*/ escape = 0xc36180cd; // "int $0x80; popa; ret" + // Don't store into read-only page if value is already there. + if (*hatch != escape) { + *hatch = escape; + } + return hatch; + } + } + return 0; +} + +static void +bzero(char *p, size_t len) +{ + if (len) do { + *p++= 0; + } while (--len); +} + +static Elf32_Addr // entry address +do_xmap(int fdi, Elf32_Ehdr const *const ehdr, f_expand *const f_decompress, + Elf32_auxv_t *const a) +{ + struct Extent x; + Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff + + (char const *)ehdr); + unsigned long base = (ET_DYN==ehdr->e_type) ? 0x40000000 : 0; + int j; + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) + if (PT_PHDR==phdr->p_type) { + a->a_un.a_val = phdr->p_vaddr; + } + else if (PT_LOAD==phdr->p_type) { + size_t mlen = x.size = phdr->p_filesz; + char *addr = x.buf = (char *)phdr->p_vaddr; + char *haddr = phdr->p_memsz + (char *)phdr->p_vaddr; + size_t frag = (int)addr &~ PAGEMASK; + mlen += frag; + addr -= frag; + if (ET_DYN==ehdr->e_type) { + addr += base; + haddr += base; + } + else { // There is only one brk, the one for the ET_EXEC + do_brk(haddr+OVERHEAD); // Also takes care of whole pages of .bss + } + // Decompressor can overrun the destination by 3 bytes. + if (addr != do_mmap(addr, mlen + (f_decompress ? 3 : 0), PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | (f_decompress ? MAP_ANONYMOUS : 0), + fdi, phdr->p_offset - frag) ) { + err_exit(8); + } + if (0==base) { + base = (unsigned long)addr; + } + if (f_decompress) { + unpackExtent(&x, fdi, f_decompress); + } + bzero(addr, frag); // fragment at lo end + frag = (-mlen) &~ PAGEMASK; // distance to next page boundary + bzero(mlen+addr, frag); // fragment at hi end + if (f_decompress) { + make_hatch(phdr); + } + if (phdr->p_memsz != phdr->p_filesz) { // .bss + if (ET_DYN==ehdr->e_type) { // PT_INTERP whole pages of .bss? + addr += frag + mlen; + mlen = haddr - addr; + if (0 < (int)mlen) { // need more pages, too + if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ) ) { + err_exit(9); +ERR_LAB + } + } + } + } + else { // no .bss + int prot = 0; + if (phdr->p_flags & PF_X) { prot |= PROT_EXEC; } + if (phdr->p_flags & PF_W) { prot |= PROT_WRITE; } + if (phdr->p_flags & PF_R) { prot |= PROT_READ; } + if (0!=mprotect(addr, mlen, prot)) { + err_exit(10); + } + if (f_decompress) { // cleanup if decompressor overrun crosses page boundary + mlen += 3; + addr += mlen; + mlen &= ~PAGEMASK; + if (mlen<=3) { // page fragment was overrun buffer only + munmap(addr - mlen, mlen); + } + } + } + if (ET_DYN!=ehdr->e_type) { + do_brk(haddr); + } + } + if (close(fdi) != 0) + err_exit(11); + if (ET_DYN==ehdr->e_type) { + return ehdr->e_entry + base; + } + else { + return ehdr->e_entry; + } +} + + +/************************************************************************* +// upx_main - called by our entry code +// +// This function is optimized for size. +**************************************************************************/ + +void *upx_main( + char const *argv[], + f_expand *const f_decompress, + Elf32_auxv_t *const av, + Elf32_Ehdr *const ehdr +) __asm__("upx_main"); + +void *upx_main( + char const *argv[], + f_expand *const f_decompress, + Elf32_auxv_t *const av, + Elf32_Ehdr *const ehdr // temp char[MAX_ELF_HDR+OVERHEAD] +) +{ + Elf32_Phdr const *phdr = (Elf32_Phdr const *)(1+ehdr); + int fdi; // file descriptor + size_t sz_elfhdrs; // sizeof(Ehdr and Phdrs), uncompressed + size_t sz_pckhdrs; // sizeof(Ehdr and Phdrs), compressed + Elf32_Addr entry; + struct Extent xo; + int j; + + struct p_info header; + + fdi = open(argv[1], O_RDONLY, 0); +#if 0 + // Save some bytes of code - the lseek() below will fail anyway. + if (fdi < 0) + err_exit(12); +#endif + +#define SCRIPT_MAX 32 + // Seek to start of compressed data. + if (lseek(fdi, SCRIPT_MAX+sizeof(struct l_info), SEEK_SET) < 0) + err_exit(13); + // Read header. + if (xread(fdi, (void *)&header, sizeof(header)) != 0) { + err_exit(14); + } + + + // + // ----- Step 4: decompress blocks ----- + // + + // Get Elf32_Ehdr. First set xo.size = size[0] = uncompressed size + if (0!=xread(fdi, (void *)&xo, sizeof(xo))) { + err_exit(15); + } + if (lseek(fdi, -sizeof(xo), SEEK_CUR) < 0) { + err_exit(16); +ERR_LAB + } + sz_elfhdrs = xo.size; + sz_pckhdrs = (size_t)xo.buf; + xo.buf = (char *)ehdr; + unpackExtent(&xo, fdi, f_decompress); + + // Prepare to decompress the Elf headers again, into the first PT_LOAD. + if (lseek(fdi, -(sizeof(xo) + sz_pckhdrs), SEEK_CUR) < 0) { + err_exit(17); + } + av[0].a_type = AT_PHDR; av[0].a_un.a_val = 0; // updated by do_xmap + av[1].a_type = AT_PHENT; av[1].a_un.a_val = ehdr->e_phentsize; + av[2].a_type = AT_PHNUM; av[2].a_un.a_val = ehdr->e_phnum; + av[3].a_type = AT_PAGESZ; av[3].a_un.a_val = PAGESIZE; + av[4].a_type = AT_ENTRY; av[4].a_un.a_val = ehdr->e_entry; + av[5].a_type = AT_NULL; + entry = do_xmap(fdi, ehdr, f_decompress, av); + + // Map PT_INTERP program interpreter + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) if (PT_INTERP==phdr->p_type) { + char const *const iname = (char const *)phdr->p_vaddr; + if (0 > (fdi = open(iname, O_RDONLY, 0))) { + err_exit(18); + } + if (0!=xread(fdi, (void *)ehdr, MAX_ELF)) { + err_exit(19); + } + entry = do_xmap(fdi, ehdr, 0, 0); + break; + } + + return (void *)entry; +} + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/stub/l_lx_sep86.asm b/src/stub/l_lx_sep86.asm new file mode 100644 index 0000000000..a7d1ff6d5b --- /dev/null +++ b/src/stub/l_lx_sep86.asm @@ -0,0 +1,232 @@ +; l_lxsep86.asm -- Linux program entry point & decompressor (separate script) +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; Integration of virtual exec() with decompression is +; Copyright (C) 2000 John F. Reiser. All rights reserved. +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; +; John F. Reiser +; jreiser@BitWagon.com + + + BITS 32 + SECTION .text + +%define jmps jmp short + +; defines for ident.ash and n2b_d32.ash +%ifdef SMALL + %define __IDENTSMA__ + %define __N2BSMA10__ + %define __N2BSMA20__ + %define __N2BSMA30__ + %define __N2BSMA40__ + %define __N2BSMA50__ + %define __N2BSMA60__ + %define __N2DSMA10__ + %define __N2DSMA20__ + %define __N2DSMA30__ + %define __N2DSMA40__ + %define __N2DSMA50__ + %define __N2DSMA60__ +%endif + + + +%include "ident.ash" + +; /************************************************************************* +; // program entry point +; // see glibc/sysdeps/i386/elf/start.S +; **************************************************************************/ + +GLOBAL _start + +_start: +;;;; int3 +;; How to debug this code: Uncomment the 'int3' breakpoint instruction above. +;; Build the stubs and upx. Compress a testcase, such as a copy of /bin/date. +;; Invoke gdb on the separate stub (such as "gdb upxb"), and give the command +;; "run date". Define a single-step macro such as +;; define g +;; stepi +;; x/i $pc +;; end +;; and a step-over macro such as +;; define h +;; x/2i $pc +;; tbreak *$_ +;; continue +;; x/i $pc +;; end +;; Step through the code; remember that repeats the previous command. +;; + call main ; push address of decompress subroutine + +; /************************************************************************* +; // C callable decompressor +; **************************************************************************/ + +%define INP dword [esp+8*4+4] +%define INS dword [esp+8*4+8] +%define OUTP dword [esp+8*4+12] +%define OUTS dword [esp+8*4+16] + +decompress: + pusha + ; cld + + mov esi, INP + mov edi, OUTP + + or ebp, byte -1 +;;; align 8 +%ifdef NRV2B + %include "n2b_d32.ash" +%elifdef NRV2D + %include "n2d_d32.ash" +%else + %error +%endif + + + ; eax is 0 from decompressor code + ;xor eax, eax ; return code + +; check compressed size + mov edx, INP + add edx, INS + cmp esi, edx + jz .ok + dec eax +.ok: + +; write back the uncompressed size + sub edi, OUTP + mov edx, OUTS + mov [edx], edi + + mov [7*4 + esp], eax + popa + ret + + +%define PAGE_MASK (~0<<12) +%define PAGE_SIZE ( 1<<12) + +%define szElf32_Phdr 8*4 +%define a_val 4 +%define __NR_munmap 91 + +main: + pop ebp ; &decompress + cld + +; Move argc,argv,envp down so that we can insert more Elf_auxv entries. +; ld-linux.so.2 depends on AT_PHDR and AT_ENTRY, for instance + +%define OVERHEAD 2048 +%define MAX_ELF_HDR 512 + + mov esi, esp + sub esp, byte 6*8 ; AT_PHENT, AT_PHNUM, AT_PAGESZ, AT_ENTRY, AT_PHDR, AT_NULL + mov edi, esp + call do_auxv ; edi= &AT_next + + lea ecx, [4+esp] ; argv + sub esp, dword MAX_ELF_HDR + OVERHEAD + + push esp ; argument: temp space + push edi ; argument: AT_next + push ebp ; argument: &decompress + push ecx ; argument: argv +EXTERN upx_main + call upx_main ; entry = upx_main(argv, &decompress, AT_next, tmp_ehdr) + add esp, dword 4*4 + MAX_ELF_HDR + OVERHEAD ; remove temp space, args + + pop ecx ; argc + pop edx ; ++argv discard argv[0] == pathname of stub + dec ecx ; --argc + push ecx + push eax ; save entry address + + mov edi, [a_val + edi] ; AT_PHDR +find_hatch: + push edi +EXTERN make_hatch + call make_hatch ; find hatch = make_hatch(phdr) + pop ecx ; junk the parameter + add edi, byte szElf32_Phdr ; prepare to try next Elf32_Phdr + test eax,eax + jz find_hatch + xchg eax,edx ; edx= &hatch + +; _dl_start and company (ld-linux.so.2) assumes that it has virgin stack, +; and does not initialize all its stack local variables to zero. +; Ulrich Drepper (drepper@cyngus.com) has refused to fix the bugs. +; See GNU wwwgnats libc/1165 . + +%define N_STKCLR (0x100 + MAX_ELF_HDR + OVERHEAD)/4 + lea edi, [esp - 4*N_STKCLR] + pusha ; values will be zeroed + mov ecx, N_STKCLR + xor eax,eax + rep stosd + + mov ecx, dword -PAGE_SIZE + mov ebx, ebp + and ebx, ecx ; round down to page boundary + neg ecx ; PAGE_SIZE (this stub fits in it) + push byte __NR_munmap + pop eax + jmp edx ; unmap ourselves, then goto entry + +do_auxv: ; entry: %esi=src = &argc; %edi=dst. exit: %edi= &AT_NULL + ; cld + +L10: ; move argc+argv + lodsd + stosd + test eax,eax + jne L10 + +L20: ; move envp + lodsd + stosd + test eax,eax + jne L20 + +L30: ; move existing Elf32_auxv + lodsd + stosd + test eax,eax ; AT_NULL ? + lodsd + stosd + jne L30 + + sub edi, byte 8 ; point to AT_NULL + ret + +; vi:ts=8:et:nowrap + diff --git a/src/stub/l_lx_sep86.lds b/src/stub/l_lx_sep86.lds new file mode 100644 index 0000000000..19f5cb2ccf --- /dev/null +++ b/src/stub/l_lx_sep86.lds @@ -0,0 +1,15 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + . = 0x00400000 + SIZEOF_HEADERS; + .text : { + *(.text) + *(.data) + } + /* 0x08048000: customary Linux/x86 Elf .text start */ + . = 0x08048000 + (0xfff & .); + .data : { + } +} diff --git a/src/stub/l_lx_sh.c b/src/stub/l_lx_sh.c new file mode 100644 index 0000000000..ae6aca963d --- /dev/null +++ b/src/stub/l_lx_sh.c @@ -0,0 +1,357 @@ +/* l_lx_sh.c -- stub loader for Linux x86 shell script executable + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + Integration of virtual exec() with decompression is + Copyright (C) 2000 John F. Reiser. All rights reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + + John F. Reiser + jreiser@BitWagon.com + */ + + +#if !defined(__linux__) || !defined(__i386__) +# error "this stub must be compiled under linux/i386" +#endif + +#include "linux.hh" + + +/************************************************************************* +// configuration section +**************************************************************************/ + +// In order to make it much easier to move this code at runtime and execute +// it at an address different from it load address: there must be no +// static data, and no string constants. + + +#define PAGEMASK (~0u<<12) // discards the offset, keeps the page +#define PAGESIZE ( 1u<<12) +#define MAX_ELF_HDR 512 // Elf32_Ehdr + n*Elf32_Phdr must fit in this + + +/************************************************************************* +// "file" util +**************************************************************************/ + +struct Extent { + size_t size; // must be first to match size[0] uncompressed size + char *buf; +}; + + +static void +xread(struct Extent *x, char *buf, size_t count) +{ + char *p=x->buf, *q=buf; + size_t j; + if (x->size < count) { + exit(127); + } + for (j = count; 0!=j--; ++p, ++q) { + *q = *p; + } + x->buf += count; + x->size -= count; +} + + +/************************************************************************* +// util +**************************************************************************/ + +#if 0 //{ save space +#define ERR_LAB error: exit(127); +#define err_exit(a) goto error +#else //}{ save debugging time +#define ERR_LAB +static void +err_exit(int a) +{ + (void)a; // debugging convenience + exit(127); +} +#endif //} + +static void * +do_brk(void *addr) +{ + return brk(addr); +} + +static char * +do_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + (void)len; (void)prot; (void)flags; (void)fd; (void)offset; + return mmap((int *)&addr); +} + + +/************************************************************************* +// UPX & NRV stuff +**************************************************************************/ + + +typedef int f_expand( + const nrv_byte *, nrv_uint, + nrv_byte *, nrv_uint * ); + +static void +unpackExtent( + struct Extent *const xi, // input + struct Extent *const xo, // output + f_expand *const f_decompress +) +{ + while (xo->size) { + struct { + int32_t sz_unc; // uncompressed + int32_t sz_cpr; // compressed + } h; + // Note: if h.sz_unc == h.sz_cpr then the block was not + // compressible and is stored in its uncompressed form. + + // Read and check block sizes. + xread(xi, (char *)&h, sizeof(h)); + if (h.sz_unc == 0) { // uncompressed size 0 -> EOF + if (h.sz_cpr != UPX_MAGIC_LE32) // h.sz_cpr must be h->magic + err_exit(2); + if (xi->size != 0) // all bytes must be written + err_exit(3); + break; + } + if (h.sz_cpr <= 0) { + err_exit(4); +ERR_LAB + } + if (h.sz_cpr > h.sz_unc + || h.sz_unc > (int32_t)xo->size ) { + err_exit(5); + } + // Now we have: + // assert(h.sz_cpr <= h.sz_unc); + // assert(h.sz_unc > 0 && h.sz_unc <= blocksize); + // assert(h.sz_cpr > 0 && h.sz_cpr <= blocksize); + + if (h.sz_cpr < h.sz_unc) { // Decompress block + nrv_uint out_len; + int const j = (*f_decompress)(xi->buf, h.sz_cpr, xo->buf, &out_len); + if (j != 0 || out_len != (nrv_uint)h.sz_unc) + err_exit(7); + xi->buf += h.sz_cpr; + xi->size -= h.sz_cpr; + } + else { // copy literal block + xread(xi, xo->buf, h.sz_cpr); + } + xo->buf += h.sz_unc; + xo->size -= h.sz_unc; + } +} + +static void +bzero(char *p, size_t len) +{ + if (len) do { + *p++= 0; + } while (--len); +} + +// This do_xmap() has no Extent *xi input because it doesn't decompress anything; +// it only maps the shell and its PT_INTERP. So, it was specialized by hand +// to reduce compiled instruction size. gdb 2.91.66 does not notice that +// there is only one call to this static function (from getexec(), which +// would specify 0 for xi), so gdb does not propagate the constant parameter. +// Notice there is no make_hatch(), either. + +static Elf32_Addr // entry address +do_xmap(int const fdi, Elf32_Ehdr const *const ehdr, Elf32_auxv_t *const a) +{ + Elf32_Phdr const *phdr = (Elf32_Phdr const *) (ehdr->e_phoff + + (char const *)ehdr); + unsigned long base = (ET_DYN==ehdr->e_type) ? 0x40000000 : 0; + int j; + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) + if (PT_PHDR==phdr->p_type) { + a->a_un.a_val = phdr->p_vaddr; + } + else if (PT_LOAD==phdr->p_type) { + struct Extent xo; + size_t mlen = xo.size = phdr->p_filesz; + char *addr = xo.buf = (char *)phdr->p_vaddr; + char *haddr = phdr->p_memsz + (char *)phdr->p_vaddr; + size_t frag = (int)addr &~ PAGEMASK; + mlen += frag; + addr -= frag; + if (ET_DYN==ehdr->e_type) { + addr += base; + haddr += base; + } + else { // There is only one brk, the one for the ET_EXEC + do_brk(haddr+OVERHEAD); // Also takes care of whole pages of .bss + } + // Decompressor can overrun the destination by 3 bytes. + if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE, + fdi, phdr->p_offset - frag) ) { + err_exit(8); + } + if (0==base) { + base = (unsigned long)addr; + } + bzero(addr, frag); // fragment at lo end + frag = (-mlen) &~ PAGEMASK; // distance to next page boundary + bzero(mlen+addr, frag); // fragment at hi end + if (phdr->p_memsz != phdr->p_filesz) { // .bss + if (ET_DYN==ehdr->e_type) { // PT_INTERP whole pages of .bss? + addr += frag + mlen; + mlen = haddr - addr; + if (0 < (int)mlen) { // need more pages, too + if (addr != do_mmap(addr, mlen, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 ) ) { + err_exit(9); +ERR_LAB + } + } + } + } + else { // no .bss + int prot = 0; + if (phdr->p_flags & PF_X) { prot |= PROT_EXEC; } + if (phdr->p_flags & PF_W) { prot |= PROT_WRITE; } + if (phdr->p_flags & PF_R) { prot |= PROT_READ; } + if (0!=mprotect(addr, mlen, prot)) { + err_exit(10); + } + } + if (ET_DYN!=ehdr->e_type) { + do_brk(haddr); + } + } + if (0!=close(fdi)) { + err_exit(11); + } + if (ET_DYN==ehdr->e_type) { + return ehdr->e_entry + base; + } + else { + return ehdr->e_entry; + } +} + + +Elf32_Addr // entry address +getexec(char const *const fname, Elf32_Ehdr *const ehdr, Elf32_auxv_t *const av) +{ + int const fdi = open(fname, O_RDONLY, 0); + if (0 > fdi) { + err_exit(18); + } + if (MAX_ELF_HDR!=read(fdi, (void *)ehdr, MAX_ELF_HDR)) { + err_exit(19); + } + return do_xmap(fdi, ehdr, av); +} + + +/************************************************************************* +// upx_main - called by our entry code +// +// This function is optimized for size. +**************************************************************************/ + +void *upx_main( + char *const uncbuf, + Elf32_Ehdr const *const my_ehdr, + f_expand *const f_decompress, + Elf32_auxv_t *const av, + Elf32_Ehdr *const ehdr +) __asm__("upx_main"); + +void *upx_main( + char *const uncbuf, // place to put decompressed shell script + Elf32_Ehdr const *const my_ehdr, // to get compressed size and data + f_expand *const f_decompress, + Elf32_auxv_t *const av, + Elf32_Ehdr *const ehdr // temp char[MAX_ELF_HDR] +) +{ + Elf32_Addr entry; + size_t const lsize = sizeof(struct p_info) + + *(unsigned short const *)(0x7c + (char const *)my_ehdr); + struct Extent xi = { // describe compressed shell script + ((Elf32_Phdr const *)(1 + my_ehdr))->p_filesz - lsize, + (lsize + (char *)my_ehdr) // warning: 'const' cast away + }; + struct Extent xo = { ((struct p_info *)xi.buf)[-1].p_filesize, uncbuf }; + + // Allocate space for decompressed shell script. + // "1+": guarantee '\0' terminator at end of decompressed script + if (xo.buf != do_mmap(xo.buf, 1+3+xo.size, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) { + err_exit(20); + } + + // Uncompress shell script + xo.buf += 3; // leave room for "-c" argument + unpackExtent(&xi, &xo, f_decompress); + + { // Map shell program + // 'fn' and 'efn' must not suffer constant-propagation by gcc + // UPX2 = 3 + offset to name_of_shell + // UPX3 = strlen(name_of_shell) +// patch & magic constants for our loader (le32 format) +#define UPX2 0x32585055 // "UPX2" +#define UPX3 0x33585055 // "UPX3" + char * /*const*/ volatile fn = UPX2 + uncbuf; // past "-c" and "#!" + char * /*const*/ volatile efn = UPX3 + fn; // &terminator + char const c = *efn; *efn = 0; // terminator + entry = getexec(fn, ehdr, av); + *efn = c; // replace terminator character + + av[0].a_type = AT_PHDR; // av[0].a_un.a_val is set by do_xmap + av[1].a_type = AT_PHENT; av[1].a_un.a_val = ehdr->e_phentsize; + av[2].a_type = AT_PHNUM; av[2].a_un.a_val = ehdr->e_phnum; + av[3].a_type = AT_PAGESZ; av[3].a_un.a_val = PAGESIZE; + av[4].a_type = AT_ENTRY; av[4].a_un.a_val = entry; + av[5].a_type = AT_NULL; + } + + { // Map PT_INTERP program interpreter + Elf32_Phdr const *phdr = (Elf32_Phdr *)(1+ehdr); + int j; + for (j=0; j < ehdr->e_phnum; ++phdr, ++j) if (PT_INTERP==phdr->p_type) { + entry = getexec((char const *)phdr->p_vaddr, ehdr, 0); + break; + } + } + return (void *)entry; +} + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/stub/l_lx_sh86.asm b/src/stub/l_lx_sh86.asm new file mode 100644 index 0000000000..9a78510a33 --- /dev/null +++ b/src/stub/l_lx_sh86.asm @@ -0,0 +1,291 @@ +; l_lx_sh86.asm -- Linux program entry point & decompressor (shell script) +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; Integration of virtual exec() with decompression is +; Copyright (C) 2000 John F. Reiser. All rights reserved. +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; +; John F. Reiser +; jreiser@BitWagon.com + + + BITS 32 + SECTION .text + +%define jmps jmp short + +; defines for ident.ash and n2b_d32.ash +%ifdef SMALL + %define __IDENTSMA__ + %define __N2BSMA10__ + %define __N2BSMA20__ + %define __N2BSMA30__ + %define __N2BSMA40__ + %define __N2BSMA50__ + %define __N2BSMA60__ + %define __N2DSMA10__ + %define __N2DSMA20__ + %define __N2DSMA30__ + %define __N2DSMA40__ + %define __N2DSMA50__ + %define __N2DSMA60__ +%endif + + + +%include "ident.ash" + +; /************************************************************************* +; // program entry point +; // see glibc/sysdeps/i386/elf/start.S +; **************************************************************************/ + +GLOBAL _start + +_start: +;;;; int3 +;; How to debug this code: Uncomment the 'int3' breakpoint instruction above. +;; Build the stubs and upx. Compress a testcase, such as a copy of /bin/date. +;; Invoke gdb, and give a 'run' command. Define a single-step macro such as +;; define g +;; stepi +;; x/i $pc +;; end +;; and a step-over macro such as +;; define h +;; x/2i $pc +;; tbreak *$_ +;; continue +;; x/i $pc +;; end +;; Step through the code; remember that repeats the previous command. +;; + call main ; push address of decompress subroutine + +; /************************************************************************* +; // C callable decompressor +; **************************************************************************/ + +%define INP dword [esp+8*4+4] +%define INS dword [esp+8*4+8] +%define OUTP dword [esp+8*4+12] +%define OUTS dword [esp+8*4+16] + +decompress: + pusha + ; cld + + mov esi, INP + mov edi, OUTP + + or ebp, byte -1 +;;; align 8 +%ifdef NRV2B + %include "n2b_d32.ash" +%elifdef NRV2D + %include "n2d_d32.ash" +%else + %error +%endif + + + ; eax is 0 from decompressor code + ;xor eax, eax ; return code + +; check compressed size + mov edx, INP + add edx, INS + cmp esi, edx + jz .ok + dec eax +.ok: + +; write back the uncompressed size + sub edi, OUTP + mov edx, OUTS + mov [edx], edi + + mov [7*4 + esp], eax + popa + ret + + +%define PAGE_MASK (~0<<12) +%define PAGE_SIZE ( 1<<12) + +%define szElf32_Ehdr 0x34 +%define szElf32_Phdr 8*4 +%define p_filesz 4*4 +%define p_memsz 5*4 +%define a_val 4 + +%define MAP_FIXED 0x10 +%define MAP_PRIVATE 0x02 +%define MAP_ANONYMOUS 0x20 +%define PROT_READ 1 +%define PROT_WRITE 2 +%define PROT_EXEC 4 +%define __NR_mmap 90 +%define __NR_munmap 91 + +; Decompress the rest of this loader, and jump to it +unfold: + pop esi ; &{ sz_uncompressed, sz_compressed, compressed_data...} + cld + lodsd + push eax ; sz_uncompressed (junk, actually) + push esp ; &sz_uncompressed + mov eax, ebp ; &decompress + and eax, dword PAGE_MASK ; &my_elfhdr + mov edx, eax ; need my_elfhdr later + add eax, [p_memsz + szElf32_Ehdr + eax] + push eax ; &destination + + ; mmap a page to hold the decompressed program + xor ecx,ecx + push ecx + push ecx + mov ch, PAGE_SIZE >> 8 + push byte MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS + push byte PROT_READ | PROT_WRITE | PROT_EXEC + push ecx + push eax ; destination + push byte __NR_mmap + pop eax + mov ebx, esp + int 0x80 + add esp, byte 6*4 ; discard args to mmap + + lodsd + push eax ; sz_compressed + push esi ; &compressed_data + call ebp ; decompress(&src, srclen, &dst, &dstlen) + pop ecx ; discard &compressed_data + pop ecx ; discard sz_compressed + pop ecx ; &destination + jmp ecx ; goto fold_begin at p_vaddr + p_memsz +main: + pop ebp ; &decompress + call unfold +fold_begin: + ; patchLoader will modify to be + ; dword sz_uncompressed, sz_compressed + ; byte compressed_data... + + pop eax ; discard &sz_uncompressed + pop eax ; discard sz_uncompressed + +; Move argc,argv,envp down so that we can insert more Elf_auxv entries. +; ld-linux.so.2 depends on AT_PHDR and AT_ENTRY, for instance + +%define OVERHEAD 2048 +%define MAX_ELF_HDR 512 + + mov esi, esp + sub esp, byte 6*8 ; AT_PHENT, AT_PHNUM, AT_PAGESZ, AT_ENTRY, AT_PHDR, AT_NULL + mov edi, esp + call do_auxv + + sub esp, dword MAX_ELF_HDR + OVERHEAD + push esp ; argument: temp space + push edi ; argument: AT_next + push ebp ; argument: &decompress + push edx ; argument: my_elfhdr + add ecx, PAGE_SIZE ; uncompressed stub fits in this + push ecx ; argument: uncbuf +EXTERN upx_main + call upx_main ; entry = upx_main(uncbuf, my_elfhdr, &decompress, AT_next, tmp_ehdr) + pop esi ; decompression buffer + pop ebx ; my_elfhdr + add esp, dword 3*4 + MAX_ELF_HDR + OVERHEAD ; remove 3 params, temp space + + pop ecx ; argc + pop edx ; $0 filename, to become argv[0] + push edx ; restore $0 filename + + add esi, byte 3 + inc ecx + push esi ; &uncompressed shell script + sub esi, byte 3 + + mov [esi], word 0x632d ; "-c" + inc ecx + push esi ; "-c" + + inc ecx + push edx ; argv[0] is duplicate of $0 + + push ecx ; new argc + push eax ; save entry address + +; _dl_start and company (ld-linux.so.2) assumes that it has virgin stack, +; and does not initialize all its stack local variables to zero. +; Ulrich Drepper (drepper@cyngus.com) has refused to fix the bugs. +; See GNU wwwgnats libc/1165 . + +%define N_STKCLR (0x100 + MAX_ELF_HDR + OVERHEAD)/4 + lea edi, [esp - 4*N_STKCLR] + pusha ; values will be zeroed + mov ecx, N_STKCLR + xor eax,eax + rep stosd + +; Because the decompressed shell script occupies low memory anyway, +; there isn't much payback to unmapping the compressed script and +; ourselves the stub. We would need a place to put the escape hatch +; "int $0x80; popa; ret", and some kernels do not allow execution +; on the stack. So, we would have to dirty a page of the shell +; or of /lib/ld-linux.so. It's simpler just to omit the unapping. + popa + ret + +do_auxv: ; entry: %esi=src = &argc; %edi=dst. exit: %edi= &AT_NULL + ; cld + +L10: ; move argc+argv + lodsd + stosd + test eax,eax + jne L10 + +L20: ; move envp + lodsd + stosd + test eax,eax + jne L20 + +L30: ; move existing Elf32_auxv + lodsd + stosd + test eax,eax ; AT_NULL ? + lodsd + stosd + jne L30 + + sub edi, byte 8 ; point to AT_NULL + ret + + +; vi:ts=8:et:nowrap + diff --git a/src/stub/l_lx_sh86.lds b/src/stub/l_lx_sh86.lds new file mode 100644 index 0000000000..b3b1e1a815 --- /dev/null +++ b/src/stub/l_lx_sh86.lds @@ -0,0 +1,17 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + /* 0x00800000: avoid 0x00400000 for shell itself being compressed */ + . = 0x00800000 + SIZEOF_HEADERS; + . = ALIGN(0x80); + .text : { + *(.text) + *(.data) + } + /* 0x08048000: customary Linux/x86 Elf .text start */ + . = 0x08048000 + (0xfff & .); + .data : { + } +} diff --git a/src/stub/l_sys.asm b/src/stub/l_sys.asm new file mode 100644 index 0000000000..7f2bfefc3a --- /dev/null +++ b/src/stub/l_sys.asm @@ -0,0 +1,124 @@ +; l_sys.asm -- loader & decompressor for the dos/sys format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +%define SYS 1 +%define COM 0 +%define CJT16 1 +%define jmps jmp short +%include "macros.ash" + + BITS 16 + ORG 0 + SECTION .text + +; ============= +; ============= ENTRY POINT +; ============= + +; __SYSMAIN1__ +start: + dd -1 + dw 0 + dw strategy ; .sys header + dw 0 ; opendos wants this field untouched +strategy: +%ifdef __SYSI2861__ + pusha +%else; __SYSI0861__ + push ax + push bx + push cx + push dx + push si + push di + push bp +%endif; __SYSMAIN2__ + mov si, 'SI' + mov di, 'DI' + + mov cx, si ; at the end of the copy si will be 0 + + push es + push ds + pop es + + std + rep + movsb + cld + + mov bx, 0x8000 + + xchg si, di + sub si, byte start - cutpoint +; __SYSSUBSI__ + sbb bp, bp +%ifdef __SYSCALLT__ + push di +%endif; __SYSMAIN3__ + jmp .1+'JM' ; jump to the decompressor +.1: +%include "header.ash" + +cutpoint: +; __SYSCUTPO__ + +; ============= +; ============= DECOMPRESSION +; ============= + +%include "n2b_d16.ash" + +; ============= +; ============= CALLTRICK +; ============= + + +; ============= + +; __SYSMAIN5__ + pop es +%ifdef __SYSI2862__ + popa +%else; __SYSI0862__ + pop bp + pop di + pop si + pop dx + pop cx + pop bx + pop ax +%endif; __SYSJUMP1__ + jmp eof+'JO' +eof: +; __SYSTHEND__ + section .data + dd -1 + dw eof + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_tmt.asm b/src/stub/l_tmt.asm new file mode 100644 index 0000000000..317a8558ce --- /dev/null +++ b/src/stub/l_tmt.asm @@ -0,0 +1,105 @@ +; l_tmt.asm -- loader & decompressor for the tmt/adam format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +%define jmps jmp short +%include "macros.ash" + + BITS 32 + SECTION .text + ORG 0 + +; ============= +; ============= ENTRY POINT +; ============= + +start: +; __TMTMAIN1__ + mov edi, 0 ; relocation offset + push edi + lea esi, [edi + 'ESI0'] + lea edi, [edi + 'EDI0'] + mov ecx, 'ECX0' + + std + rep + movsb + cld + + lea esi, [edi + 1] + pop edi + or ebp, byte -1 + push edi +%ifdef __TMTCALT1__ + push edi +%endif; __TMTMAIN2__ + jmp .1 + 'JMPD' +.1: +%include "header.ash" + +cutpoint: +; __TMTCUTPO__ + +; ============= +; ============= DECOMPRESSION +; ============= + +%include "n2b_d32.ash" +%include "n2d_d32.ash" + +; __TMTMAIN5__ + pop ebp + mov esi, edi + sub esi, [edi - 4] + +; ============= +; ============= CALLTRICK +; ============= + +%ifdef __TMTCALT2__ + pop edi + cjt32 ebp +%endif; __TMTRELOC__ + +; ============= +; ============= RELOCATION +; ============= + + lea edi, [ebp - 4] + reloc32 esi, edi, ebp + +; ============= +; __TMTJUMP1__ + jmp .1+'JMPO' +.1: +eof: +; __TMTHEEND__ + section .data + dd -1 + dw eof + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_tos.s b/src/stub/l_tos.s new file mode 100644 index 0000000000..db93137981 --- /dev/null +++ b/src/stub/l_tos.s @@ -0,0 +1,349 @@ +; l_tos.s -- loader & decompressor for the atari/tos format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +#define NRV_BB 8 + + +; +; see also: +; mint/src/basepage.h +; mint/src/mem.h (FILEHEAD) +; mint/src/mem.c (load_region, load_and_reloc) +; + +; +; This file is first preprocessed by cpp, then the a68k assembler +; is run and finally the generated object file is translated to a .h file +; by a simple perl script. We also maintain compatiblity with the pasm +; assembler (which must be started in the emulator window). +; + + +#ifdef __A68K__ +# define align4 align 0,4 +# define L(label) \/**/label +# define macro(name) name macro +# define text section code +#else +# define align4 align 4 +# define L(label) ./**/label +# define macro(name) macro name +#endif + +; defines needed for including ident_[ns].ash +#define db dc.b +#define dw dc.w +#define dd dc.l + + +; basepage offsets +p_lowtpa equ $0 ; .l +p_hitpa equ $4 ; .l +p_tbase equ $8 ; .l +p_tlen equ $c ; .l +p_dbase equ $10 ; .l +p_dlen equ $14 ; .l +p_bbase equ $18 ; .l +p_blen equ $1c ; .l + +#if 0 +; file header offsets (NOT USED) +fh_branch equ $0 ; .w $601a +fh_tlen equ $2 ; .l +fh_dlen equ $6 ; .l +fh_blen equ $a ; .l +fh_slen equ $e ; .l +fh_res1 equ $12 ; .l +fh_res2 equ $16 ; .l +fh_flag equ $1a ; .w + +fh_size equ $1c ; 28 bytes +#endif + +; +; long living registers: +; d4 p_tbase - start of text segment +; a6 p_bbase - start of uncompressed bss segment, this also is the +; - end of decompressed text+data +; - beginning of decompressed relocations +; - beginning of dirty bss +; a5 final startup code copied below stack +; + + +; /************************************************************************* +; // entry - the text segment of a compressed executable +; // +; // note: compressed programs never have the F_SHTEXT flag set, +; // so we can assume that the text, data & bss segments +; // are contiguous in memory +; **************************************************************************/ + + text + dc.b 'UPX1' ; marker for o2bin.pl +start: + move.l a0,d0 ; a0 is basepage if accessory + beq L(l_app) + move.l 4(a0),sp ; accessory - get stack + bra L(start) +L(l_app): move.l 4(sp),d0 ; application - get basepage +L(start): movem.l d1-d7/a0-a6,-(sp) + + +; ------------- restore original basepage + + ; we also setup d4, a6 and a1 here + + move.l d0,a2 ; a2 = basepage + addq.l #p_tbase,a2 + move.l (a2)+,a6 + move.l a6,d4 ; d4 = p_tbase + move.l #'up11',(a2) ; p_tlen + add.l (a2)+,a6 + move.l a6,(a2)+ ; p_dbase + move.l #'up12',(a2) ; p_dlen + add.l (a2)+,a6 ; a6 = uncompressed p_bbase + move.l (a2),a1 ; a1 = compressed p_bbase + move.l a6,(a2)+ ; p_bbase + move.l #'up13',(a2) ; p_blen + + +; ------------- copy data segment (from a1 to a0, downwards) + + ; a1 (top of compressed data) already initialized above + + move.l d4,a0 + add.l #'up21',a0 ; top of data segment + offset + +#if defined(SMALL) + + move.l #'up22',d0 ; (len / 4) + + ; copy 4 bytes per loop +L(loop): move.l -(a1),-(a0) + ;;subq.l #1,d0 + dc.b 'u1' ; subq.l #1,d0 / subq.w #1,d0 + bne L(loop) + +#else + + move.l #'up22',d0 ; (len / 160) + + ; loop1 - use 10 registers to copy 4*10*4 = 160 bytes per loop +L(loop1): + lea.l -160(a1),a1 + movem.l 120(a1),d1-d3/d5-d7/a2-a5 + movem.l d1-d3/d5-d7/a2-a5,-(a0) + movem.l 80(a1),d1-d3/d5-d7/a2-a5 + movem.l d1-d3/d5-d7/a2-a5,-(a0) + movem.l 40(a1),d1-d3/d5-d7/a2-a5 + movem.l d1-d3/d5-d7/a2-a5,-(a0) + movem.l (a1),d1-d3/d5-d7/a2-a5 + movem.l d1-d3/d5-d7/a2-a5,-(a0) + ;;subq.l #1,d0 + dc.b 'u1' ; subq.l #1,d0 / subq.w #1,d0 + bne L(loop1) + + ; loop2 - copy the remaining 4..160 bytes + ;;moveq.l #xx,d0 ; ((len % 160) / 4) - 1 + dc.b 'u2' ; moveq.l #xx,d0 + +L(loop2): move.l -(a1),-(a0) + dbra d0,L(loop2) + +#endif + + +; ------------- copy code to stack + +; Copy the final startup code below the stack. This will get +; called via "jmp (a5)" after decompression and relocation. + +copy_to_stack: + lea.l clear_bss_end(pc),a2 + move.l sp,a5 + moveq.l #((clear_bss_end-clear_bss)/2),d0 + + move.l d4,-(a5) ; entry point for final jmp +L(loop): move.w -(a2),-(a5) + subq.w #1,d0 + bne L(loop) + + ; note: now d0 is 0 + + +; ------------- prepare decompressor + + ; a0 now points to the start of the compressed block + ; note: the next statement can be moved below cutpoint + ; if it helps for the align4 + ;;move.l d4,a1 ; dest. for uncompressing + move.l d4,a1 ; dest. for uncompressing + + +; ------------- jump to copied decompressor + + move.l d4,a2 + add.l #'up31',a2 + jmp (a2) ; jmp cutpoint + + +; /************************************************************************* +; // this is the final part of the startup code which runs in the stack +; **************************************************************************/ + + ; on entry d1 and d2 are 0 + +; ------------- clear dirty bss + +clear_bss: + +#if defined(SMALL) +L(loop): move.l d1,(a6)+ + ;;subq.l #1,d0 + dc.b 'u4' ; subq.l #1,d0 / subq.w #1,d0 + bne L(loop) +#else + ; the dirty bss is usually not too large, so we don't + ; bother making movem optimizations here +L(loop): move.l d1,(a6)+ + move.l d1,(a6)+ + move.l d1,(a6)+ + move.l d1,(a6)+ + ;;subq.l #1,d0 + dc.b 'u4' ; subq.l #1,d0 / subq.w #1,d0 + bne L(loop) +#endif + + +; ------------- start program + + ; note: d0.l is now 0 + + movem.l (sp)+,d1-d7/a0-a6 + cmp.l d0,a0 + beq L(l_app) + ;;suba.l sp,sp ; accessory: no stack + move.l d0,sp ; accessory: no stack +L(l_app): dc.w $4ef9 ; jmp $xxxxxxxx - jmp to text segment + +clear_bss_end: + + +; /************************************************************************* +; // UPX ident & packheader +; **************************************************************************/ + +#if defined(SMALL) +# include "ident_s.ash" +#else +# include "ident_n.ash" +#endif + even + + align4 + + dc.b 'UPX!' ; magic + ds.b 28 ; #include "header.ash" + + + ; end of text segment - size is a multiple of 4 + + +; /************************************************************************* +; // This part is appended after the compressed data. +; // It runs in the last part of the dirty bss (after the relocations). +; **************************************************************************/ + +cutpoint: + +; ------------- decompress (from a0 to a1) + +#if defined(NRV2B) +# include "m68k/n2b_d.ash" +#elif defined(NRV2D) +# include "m68k/n2d_d.ash" +#else +# error +#endif + + +; ------------- reloc + +; The decompressed relocations now are just after the decompressed +; data segment, i.e. at the beginning of the (dirty) bss. + + ; note: d1 and d2 are 0 from decompressor above + +reloc: + ;;move.w #'u3',d3 ; #0 or #1 + dc.b 'u3' ; moveq.l #0,d3 / moveq.l #1,d3 + beq reloc_end ; don't reloc + + move.l a6,a0 ; a0 = start of relocations + + move.l d4,a1 + add.l (a0)+,a1 ; get initial fixup + +L(loop1): add.l d1,a1 ; increase fixup + add.l d4,(a1) ; reloc one address +L(loop2): move.b (a0)+,d1 + beq reloc_end + cmp.b d3,d1 ; note: d3.b is #1 + bne L(loop1) + lea 254(a1),a1 ; d1 == 1 -> add 254, don't reloc + bra L(loop2) + +reloc_end: + + ; note: d1 and d2 are still 0 + + +; ------------- clear dirty bss & start program + +; We are currently running in the dirty bss. +; Jump to the code we copied below the stack. + +#if defined(SMALL) + move.l #'up41',d0 ; dirty_bss / 4 +#else + move.l #'up41',d0 ; dirty_bss / 16 +#endif + + jmp (a5) ; jmp clear_bss (on stack) + + +eof: + dc.w cutpoint-start ; size of entry + dc.w eof-cutpoint ; size of decompressor + dc.b 'UPX9' ; marker for o2bin.pl + + end + + +; vi:ts=8:et:nowrap + diff --git a/src/stub/l_w32pe.asm b/src/stub/l_w32pe.asm new file mode 100644 index 0000000000..53750ff676 --- /dev/null +++ b/src/stub/l_w32pe.asm @@ -0,0 +1,224 @@ +; l_w32pe.asm -- loader & decompressor for the w32/pe format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +%define jmps jmp short +%define jnzn jnz near +%define jbn jb near +%include "macros.ash" + + BITS 32 + SECTION .text + ORG 0 + +; ============= +; ============= ENTRY POINT +; ============= + +%ifdef __PEISDLL1__ + cmp byte [esp + 8], 1 + jnzn reloc_end_jmp +%endif; __PEMAIN01__ + pushad + mov esi, 'ESI0' ; relocated + lea edi, [esi + 'EDI0'] +%ifdef __PEICONS1__ + inc word [edi + 'ICON'] +%else; __PEICONS2__ + add word [edi + 'ICON'],'DR' +%endif; __PEICONSZ__ +%ifdef __PETLSHAK__ + mov dword [edi + 'TLSA'],'TLSV' +%endif; __PEMAIN02__ + push edi +mpass: + or ebp, byte -1 + +; ============= +; ============= DECOMPRESSION +; ============= + +%include "n2b_d32.ash" +%include "n2d_d32.ash" + +; ============= + +%ifdef __PEMULTIP__ + lodsd + add edi, eax + jbn mpass +%endif; __PEMAIN10__ + +; ============= + pop esi ; load vaddr + +; ============= +; ============= CALLTRICK +; ============= + +%ifdef __PECALLTR__ +%ifdef __PECTTPOS__ + lea edi, [esi + 'TEXV'] +%else; __PECTTNUL__ + mov edi, esi +%endif; __PEDUMMY0__ + cjt32 esi +%endif; __PEDUMMY1__ + +; ============= +; ============= IMPORTS +; ============= + +%ifdef __PEIMPORT__ + lea edi, [esi + 'BIMP'] +next_dll: + mov eax, [edi] + or eax, eax + jz imports_done + mov ebx, [edi+4] ; iat + lea eax, [eax + esi + 'IMPS'] + add ebx, esi + push eax + add edi, byte 8 + call [esi + 'LOAD'] ; LoadLibraryA + xchg eax, ebp +next_func: + mov al, [edi] + inc edi + or al, al + jz next_dll + mov ecx, edi ; something > 0 +%ifdef __PEIBYORD__ + jns byname +%ifdef __PEK32ORD__ + jpe not_kernel32 + mov eax, [edi] + add edi, byte 4 + mov eax, [eax + esi + 'K32O'] + jmps next_imp +not_kernel32: +%endif; __PEIMORD1__ + movzx eax, word [edi] + inc edi + push eax + inc edi + db 0xb9 ; mov ecx,xxxx +byname: +%endif; __PEIMPOR2__ + push edi + dec eax + repne + scasb + + push ebp + call [esi + 'GETP'] ; GetProcAddr + or eax, eax + jz imp_failed +next_imp: + mov [ebx], eax + add ebx, byte 4 + jmps next_func +imp_failed: +%ifdef __PEIERDLL__ + popad + xor eax, eax + retn 0x0c +%else; __PEIEREXE__ + call [esi + 'EXIT'] ; ExitProcess +%endif; __PEIMDONE__ +imports_done: +%endif; __PEIMPOR9__ + +; ============= +; ============= RELOCATION +; ============= + +%ifdef __PERELOC1__ + lea edi, [esi + 'BREL'] +; __PERELOC2__ + add edi, byte 4 +; __PERELOC3__ + lea ebx, [esi - 4] + reloc32 edi, ebx, esi +%endif; __PERELOC9__ + +; ============= + +; FIXME: depends on that in PERELOC1 edi is set!! +%ifdef __PERLOHI0__ + xchg edi, esi + lea ecx, [edi + 'DELT'] +%endif; __PERLOHIZ__ + +%ifdef __PERELLO0__ + db 0xA9 +rello0: + add [edi + eax], cx + lodsd + or eax, eax + jnz rello0 +%endif; __PERELLOZ__ + +; ============= + +%ifdef __PERELHI0__ + shr ecx, 16 + db 0xA9 +relhi0: + add [edi + eax], cx + lodsd + or eax, eax + jnz relhi0 +%endif; __PERELHIZ__ + +; ============= + +; __PEMAIN20__ + popad +reloc_end_jmp: +%ifdef __PERETURN__ + xor eax, eax + inc eax + retn 0x0C +%else; __PEDOJUMP__ + jmp .1+'JMPO' +.1: +%endif; __PEDUMMY3__ + +; ============= +; ============= CUT HERE +; ============= + +%include "header.ash" + +eof: +; __PETHEEND__ + section .data + dd -1 + dw eof + + +; vi:ts=8:et:nowrap diff --git a/src/stub/l_wcle.asm b/src/stub/l_wcle.asm new file mode 100644 index 0000000000..68cdd24643 --- /dev/null +++ b/src/stub/l_wcle.asm @@ -0,0 +1,139 @@ +; l_wcle.asm -- loader & decompressor for the watcom/le format +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +%define jmps jmp short +%include "macros.ash" + + BITS 32 + SECTION .text + ORG 0 + +; ============= +; ============= ENTRY POINT +; ============= + +start: +; __WCLEMAIN__ + mov edi, 'alib' ; address of obj#1:0 (filled by a fixup record) + +; The following hack fools the lame protection of dos4g/w, which expects the +; 'WATCOM' string somewhere in the first 18 bytes after the entry point +; I use this imul thingy, because it's 1 byte shorter than a jump ;-) +; ... and "alibiWATCOM" looks cool + db 'iWATCOM' ; imul edx,[edi+0x41],'TCOM' + + push es + push ds + pop es + push edi + + lea esi, [edi + 'ESI0'] + lea edi, [edi + 'EDI0'] + mov ecx, 'ECX0' + + std + rep + movsd + cld + + lea esi, [edi + 4] + pop edi + or ebp, byte -1 + push edi + jmp .1 + 'JMPD' +.1: +%include "header.ash" + +cutpoint: +; __WCLECUTP__ + +; ============= +; ============= DECOMPRESSION +; ============= + +%include "n2b_d32.ash" +%include "n2d_d32.ash" + +; ============= + +; __WCLEMAI2__ + pop ebp + push esi + lea esi, [ebp + 'RELO'] + push esi + +; ============= +; ============= CALLTRICK +; ============= + +%ifdef __WCALLTRI__ +%ifdef __WCCTTPOS__ + lea edi, [ebp + 'TEXV'] +%else; __WCCTTNUL__ + mov edi, ebp +%endif; __WCALLTR1__ + cjt32 ebp +%endif; __WCDUMMY1__ + +; ============= +; ============= RELOCATION +; ============= + +%ifdef __WCRELOC1__ + lea edi, [ebp - 4] + reloc32 esi, edi, ebp +; eax = 0 +%endif; __WCDUMMY2__ + +%ifdef __WCRELSEL__ + call esi ; selector fixup code (modifies bx) +%endif; __WCLEMAI4__ + +; ============= + + pop edi + pop ecx + sub ecx, edi + shr ecx, 2 + rep + stosd ; clear dirty memory + pop es + lea esp, [ebp + 'ESP0'] + + jmp .1+'JMPO' +.1: + +; ============= + +eof: +; __WCTHEEND__ + section .data + dd -1 + dw eof + + +; vi:ts=8:et:nowrap diff --git a/src/stub/linux.hh b/src/stub/linux.hh new file mode 100644 index 0000000000..83049cd303 --- /dev/null +++ b/src/stub/linux.hh @@ -0,0 +1,276 @@ +/* linux.hh -- common stuff the the Linux stub loaders + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#if !defined(__linux__) || !defined(__i386__) +# error "this stub must be compiled under linux/i386" +#endif + + +/************************************************************************* +// includes +**************************************************************************/ + +#define __need_timeval +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/************************************************************************* +// constants and types +**************************************************************************/ + +// !!! must be the same as in p_unix.h !!! +#define OVERHEAD 2048 + + +#define UPX_MAGIC_LE32 0x21585055 // "UPX!" + + +#undef int32_t +#undef uint32_t +#define int32_t int +#define uint32_t unsigned int + + +typedef int nrv_int; +typedef int nrv_int32; +typedef unsigned int nrv_uint; +typedef unsigned int nrv_uint32; +#define nrv_byte unsigned char +#define nrv_voidp void * + + +// From ../p_unix.h +struct l_info { // 12-byte trailer in header for loader + uint32_t l_checksum; + uint32_t l_magic; + uint16_t l_lsize; + uint8_t l_version; + uint8_t l_format; +}; +struct p_info // 12-byte packed program header follows stub loader +{ + uint32_t p_progid; + uint32_t p_filesize; + uint32_t p_blocksize; +}; + + +/************************************************************************* +// syscalls +// +// Because of different versions and subtle bugs +// in both gcc and egcs we define all syscalls manually. +// +// Also, errno conversion is not necessary in our case, and we +// use optimized assembly statements to further decrease the size. +**************************************************************************/ + +#undef _syscall0 +#undef _syscall1 +#undef _syscall2 +#undef _syscall3 +#ifndef __NR__exit +# define __NR__exit __NR_exit +#endif + +#define Z0(x) (__builtin_constant_p(x) && (long)(x) == 0) +#define Z1(x) (__builtin_constant_p(x) && (long)(x) >= -128 && (long)(x) <= 127) + +#define _syscall0(type,name) \ +type name(void) \ +{ \ + long __res; \ + if (Z1(__NR_##name)) { \ + __asm__ __volatile__ ("push %1; popl %0; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name)); \ + } else { \ + __asm__ __volatile__ ("int $0x80" \ + : "=a" (__res) \ + : "a" (__NR_##name)); \ + } \ + return (type) __res; \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) \ +{ \ + long __res; \ + if (Z1(__NR_##name)) { \ + if (Z0(arg1)) { \ + __asm__ __volatile__ ("push %1; popl %0; xorl %%ebx,%%ebx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name) \ + : "ebx"); \ + } else if (Z1(arg1)) { \ + __asm__ __volatile__ ("push %1; popl %0; push %2; popl %%ebx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"g" ((long)(arg1)) \ + : "ebx"); \ + } else { \ + __asm__ __volatile__ ("push %1; popl %0; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"b" ((long)(arg1))); \ + } \ + } else { \ + __asm__ __volatile__ ("int $0x80" \ + : "=a" (__res) \ + : "a" (__NR_##name),"b" ((long)(arg1))); \ + } \ + return (type) __res; \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1,type2 arg2) \ +{ \ + long __res; \ + if (Z1(__NR_##name)) { \ + if (Z0(arg1) && Z0(arg2)) { \ + __asm__ __volatile__ ("push %1; popl %0; xorl %%ebx,%%ebx; xorl %%ecx,%%ecx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name) \ + : "ebx", "ecx"); \ + } else if (Z0(arg1) && Z1(arg2)) { \ + __asm__ __volatile__ ("push %1; popl %0; xorl %%ebx,%%ebx; push %2; popl %%ecx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"g" ((long)(arg2)) \ + : "ebx", "ecx"); \ + } else if (Z1(arg1) && Z0(arg2)) { \ + __asm__ __volatile__ ("push %1; popl %0; push %2; popl %%ebx; xorl %%ecx,%%ecx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"g" ((long)(arg1)) \ + : "ebx", "ecx"); \ + } else if (Z1(arg1) && Z1(arg2)) { \ + __asm__ __volatile__ ("push %1; popl %0; push %2; popl %%ebx; push %3; popl %%ecx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"g" ((long)(arg1)),"g" ((long)(arg2)) \ + : "ebx", "ecx"); \ + } else if (Z0(arg1)) { \ + __asm__ __volatile__ ("push %1; popl %0; xorl %%ebx,%%ebx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"c" ((long)(arg2)) \ + : "ebx"); \ + } else if (Z0(arg2)) { \ + __asm__ __volatile__ ("push %1; popl %0; xorl %%ecx,%%ecx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"b" ((long)(arg1)) \ + : "ecx"); \ + } else if (Z1(arg1)) { \ + __asm__ __volatile__ ("push %1; popl %0; push %2; popl %%ebx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"g" ((long)(arg1)),"c" ((long)(arg2)) \ + : "ebx"); \ + } else if (Z1(arg2)) { \ + __asm__ __volatile__ ("push %1; popl %0; push %3; popl %%ecx; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"b" ((long)(arg1)),"g" ((long)(arg2)) \ + : "ecx"); \ + } else { \ + __asm__ __volatile__ ("push %1; popl %0; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \ + } \ + } else { \ + __asm__ __volatile__ ("int $0x80" \ + : "=a" (__res) \ + : "a" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \ + } \ + return (type) __res; \ +} + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1,type2 arg2,type3 arg3) \ +{ \ + long __res; \ + if (Z1(__NR_##name)) { \ + __asm__ __volatile__ ("push %1; popl %0; int $0x80" \ + : "=a" (__res) \ + : "g" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } else { \ + __asm__ __volatile__ ("int $0x80" \ + : "=a" (__res) \ + : "a" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } \ + return (type) __res; \ +} + +#define access syscall_access +#define fcntl syscall_fcntl +#define getcwd syscall_getcwd +#define getrusage syscall_getrusage +#define gettimeofday syscall_gettimeofday +#define nanosleep syscall_nanosleep +#define open syscall_open +#define personality syscall_personality + +static inline _syscall2(int,access,const char *,file,int,mode) +static inline _syscall1(int,adjtimex,struct timex *,ntx) +static inline _syscall1(void *,brk,void *,high) +static inline _syscall1(int,close,int,fd) +static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) +static inline _syscall1(int,_exit,int,exitcode) +static inline _syscall3(int,fcntl,int,fd,int,cmd,long,arg) +static inline _syscall2(int,ftruncate,int,fd,size_t,len) +static inline _syscall0(pid_t,fork) +static inline _syscall2(int,getcwd,char *,buf,unsigned long,size); +static inline _syscall0(pid_t,getpid) +static inline _syscall2(int,getrusage,int,who,struct rusage *,usage); +static inline _syscall2(int,gettimeofday,struct timeval *,tv,void *,tz) +static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,whence) +static inline _syscall1(caddr_t,mmap,const int *,args) +static inline _syscall3(int,mprotect,void *,addr,size_t,len,int,prot) +static inline _syscall3(int,msync,const void *,start,size_t,length,int,flags) +static inline _syscall2(int,munmap,void *,start,size_t,length) +static inline _syscall2(int,nanosleep,const struct timespec *,rqtp,struct timespec *,rmtp) +static inline _syscall3(int,open,const char *,file,int,flag,int,mode) +static inline _syscall1(int,personality,unsigned long,persona) +static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) +static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) +static inline _syscall1(int,unlink,const char *,file) +#define exit _exit + +#undef Z0 +#undef Z1 + + +/* +vi:ts=4:et:nowrap +*/ + diff --git a/src/stub/macros.ash b/src/stub/macros.ash new file mode 100644 index 0000000000..446020b212 --- /dev/null +++ b/src/stub/macros.ash @@ -0,0 +1,215 @@ +; macros.ash -- +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2000 Laszlo Molnar +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them 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; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +; + + +; ============= +; ============= 16-BIT CALLTRICK & JUMPTRICK +; ============= + + +%macro cjt16 1 +%ifdef __CALLTR16__ + pop si + mov cx, 'CT' +cjt16_L1: + lodsb + sub al, 0xe8 + cmp al, 1 + ja cjt16_L1 + +%ifdef __CT16I286__ + rol word [si], 8 +; __CT16SUB0__ + sub [si], si +%else; __CT16I086__ + mov bx, [si] + xchg bl, bh + sub bx, si + mov [si], bx +%endif; __CALLTRI2__ + lodsw + loop cjt16_L1 +%endif; __CT16DUM1__ + +; ============= + +%ifdef __CT16E800__ + mov al, 0xe8 +%else; __CT16E900__ + mov al, 0xe9 +%endif; __CALLTRI5__ + pop di + mov cx, 'CT' +cjt16_L11: + repne + scasb +%ifdef __CT16JEND__ + jnz %1 ; FIXME: this doesn't get relocated +%else; __CT16JUL2__ + jnz cjt16_L2 +%endif; __CT16DUM2__ + +%ifdef __CT16I287__ + rol word [di], 8 +; __CT16SUB1__ + sub [di], di +%else; __CT16I087__ + mov bx, [di] + xchg bl, bh + sub bx, di + mov [di], bx +%endif; __CALLTRI6__ + scasw + jmps cjt16_L11 +cjt16_L2: +; __CT16DUMM3__ +%endmacro + + + +;; ============= +;; ============= 32-BIT CALLTRICK & JUMPTRICK +;; ============= + +;; call & jump trick : 2 in 1 +%macro cjt32 1 +%ifdef __CALLTR00__ + mov ecx, 'TEXL' +calltrickloop: + mov al, [edi] + inc edi + sub al, 0xE8 +ct1: + cmp al, 1 + ja calltrickloop +%ifdef __CTCLEVE1__ + cmp byte [edi], '?' + jnz calltrickloop +%endif; __CALLTR01__ + mov eax, [edi] + mov bl, [edi + 4] +%ifdef __CTDUMMY1__ +%ifdef __CTBSHR01__ + shr ax, 8 +%else; __CTBROR01__ + xchg al, ah +%endif; __CTBSWA01__ + rol eax, 16 + xchg al, ah +%endif; __CALLTR02__ + sub eax, edi + sub bl, 0xE8 + %ifnidn %1,0 + add eax, %1 + %endif + mov [edi], eax + add edi, byte 5 + mov eax, ebx + loop ct1 +%else; __CALLTR10__ +;; 32-bit call XOR jump trick + mov ecx, 'TEXL' +ctloop1: +%ifdef __CALLTRE8__ + mov al,0xE8 +%else; __CALLTRE9__ + mov al,0xE9 +%endif; __CALLTR11__ +ctloop2: + repnz + scasb + jnz ctend +%ifdef __CTCLEVE2__ + cmp byte [edi], '?' + jnz ctloop2 +%endif; __CALLTR12__ + mov eax, [edi] +%ifdef __CTDUMMY2__ +%ifdef __CTBSHR11__ + shr ax, 8 +%else; __CTBROR11__ + xchg al, ah +%endif; __CTBSWA11__ + rol eax, 16 + xchg al, ah +%endif; __CALLTR13__ + sub eax, edi + %ifnidn %1,0 + add eax, %1 + %endif + stosd + jmps ctloop1 +ctend: +%endif; __CTTHEEND__ +%endmacro + + + +;; ============= +;; ============= 32-BIT RELOCATIONS +;; ============= + +%macro reloc32 3 +; __RELOC320__ +reloc_main: + xor eax, eax + mov al, [%1] + inc %1 + or eax, eax + jz reloc_endx + cmp al, 0xEF + ja reloc_fx +reloc_add: + add %2, eax + %if 1 + mov eax, [%2] + xchg al, ah + rol eax, 16 + xchg al, ah + add eax, %3 + mov [%2], eax + %else + add [%2], %3 + %endif + jmps reloc_main +reloc_fx: + and al, 0x0F + shl eax, 16 + mov ax, [%1] + add %1, byte 2 +%ifdef __REL32BIG__ + or eax, eax + jnz reloc_add + mov eax, [%1] + add %1, byte 4 +%endif; __RELOC32J__ + jmps reloc_add +reloc_endx: +; __REL32END__ +%endmacro + + +; vi:ts=8:et:nowrap diff --git a/src/stub/merge.sh b/src/stub/merge.sh new file mode 100644 index 0000000000..d878d07fd5 --- /dev/null +++ b/src/stub/merge.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -x + +for i in Makefile *.h *.c *.ash *.asm *.lds +do + diff3 -m ./$i ../../../upx-ancestor/src/stub/$i ../../../upx-1.10/src/stub/$i > tmp.$$ + mv tmp.$$ ./$i + read junk +done diff --git a/src/stub/scripts/app.pl b/src/stub/scripts/app.pl new file mode 100644 index 0000000000..548271de5c --- /dev/null +++ b/src/stub/scripts/app.pl @@ -0,0 +1,120 @@ +#! /usr/bin/perl -w +# +# app.pl -- assembly preprocessor for upx +# +# This file is part of the UPX executable compressor. +# +# Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +# Copyright (C) 1996-2000 Laszlo Molnar +# +# UPX and the UCL library are free software; you can redistribute them +# and/or modify them 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; see the file COPYING. +# If not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Markus F.X.J. Oberhumer Laszlo Molnar +# markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +# + +# +# usage: app.pl infile outfile +# + +$in = shift || die; +$ou = shift || die; + +open (IN,"<$in") or die; +open (OU,">$ou") or die; +binmode IN; +binmode OU; + +@lines = ; + +%labels = (); +$i = 0; +$cs = ""; + +($ilabel = $in) =~ s,^.*[\/\\],,; # get basename +$ilabel =~ s/\W//g; + +# 1st pass +for $line (@lines) +{ + $labels{$1} = "$cs" if ($line =~ /^(\w+):/ && $cs); + if ($line =~ /__([A-Z0-9]{8})__/) { + $cs = $1; + # verify the line + if ($line =~ /^\%ifdef/) { + # ok + } elsif ($line =~ /^(\%\w+)?\s*;/) { + # ok + } else { + print STDERR "$in:$i:warning:$line" + } + } + + if ($line =~ /^\%(if|el|endi)/) + { + if ($line =~ /__([A-Z0-9]{8})__/) + { + $line=";$line"; + } + else + { + print STDERR "$in:$i:warning:$line"; + } + } + $line =~ s/\.ash/\.asy/ if ($line =~ /^\s*\%include/); + $i++; +} + +$cs = ""; +$i = 0; + +# 2nd pass +for $line (@lines) +{ + if ($line =~ /^\s+(j\w+|loop|call)\s+(\w*)/) + { + $label = $2; + die "$line" if ($label =~ /(\bnear\b|\bshort\b)/); + if (defined $labels{$label}) + { + $ts = $labels{$label}; + if ($ts ne $cs) + { + $line =~ s/$label/J$i$ilabel/; + print OU $line; + print OU "J$i$ilabel:\n"; + print OU "\t\tsection\t.data\n\t\tdd\t"; + print OU "0,J$i$ilabel,\'$ts\',$label - S$ts$ilabel\n"; + print OU "\t\tsection\t.text\n\n"; + $line = ""; + } + } + } + + $line = ";$line" if ($line =~ /^\s+align\s/); + + print OU $line; + if ($line =~ /__([A-Z0-9]{8})__/) + { + print OU "S$1$ilabel:\n"; + print OU "\t\tsection\t.data\n\t\tdd\t\'$1\',S$1$ilabel\n"; + print OU "\t\tsection\t.text\n\n"; + $cs = $1; + } + $i++; +} + +# vi:ts=4:et diff --git a/src/stub/scripts/bin2h.pl b/src/stub/scripts/bin2h.pl new file mode 100644 index 0000000000..fd4356b279 --- /dev/null +++ b/src/stub/scripts/bin2h.pl @@ -0,0 +1,100 @@ +#! /usr/bin/perl -w +# +# bin2h.pl -- +# +# This file is part of the UPX executable compressor. +# +# Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +# Copyright (C) 1996-2000 Laszlo Molnar +# +# UPX and the UCL library are free software; you can redistribute them +# and/or modify them 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; see the file COPYING. +# If not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Markus F.X.J. Oberhumer Laszlo Molnar +# markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +# + + +$delim = $/; +undef $/; # undef input record separator - read file as a whole + +$ifile = shift || die; +$ident = shift || die; +$ofile = shift || die; + +open(INFILE,$ifile) || die "$ifile\n"; +binmode(INFILE); +open(OUTFILE,">$ofile") || die "$ofile\n"; +binmode(OUTFILE); + +# read whole file +$data = ; +close(INFILE); +$n = length($data); + +# print +select(OUTFILE); + +$o = $ofile; +$o =~ s/.*[\/\\]//; + +print <<"EOF"; +/* $o -- created from $ifile, $n bytes + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer\@jk.uni-linz.ac.at ml1050\@cdata.tvnet.hu + */ + + +EOF + +printf("unsigned char %s[%d] = {", $ident, $n); +for ($i = 0; $i < $n; $i++) { + if ($i % 16 == 0) { + printf(" /* 0x%4x */", $i - 16) if $i > 0; + print "\n"; + } + printf("%3d", ord(substr($data, $i, 1))); + print "," if ($i != $n - 1); +} +print "\n};\n"; + +close(OUTFILE) || die; +select(STDOUT); + +undef $delim; +exit(0); + +# vi:ts=4:et diff --git a/src/stub/scripts/brandelf.pl b/src/stub/scripts/brandelf.pl new file mode 100644 index 0000000000..922e23467f --- /dev/null +++ b/src/stub/scripts/brandelf.pl @@ -0,0 +1,47 @@ +#! /usr/bin/perl -w +# +# brandelf.pl -- brand an ELF binary as Linux or FreeBSD +# +# This file is part of the UPX executable compressor. +# +# Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +# Copyright (C) 1996-2000 Laszlo Molnar +# +# UPX and the UCL library are free software; you can redistribute them +# and/or modify them 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; see the file COPYING. +# If not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Markus F.X.J. Oberhumer Laszlo Molnar +# markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +# + + +$fname = shift || die; + +$sig = shift || "Linux"; +die if length($sig) > 7; + +sysopen (FH,$fname,2) || die; +binmode FH; + +sysread (FH,$header,8) || die; +die if (substr($header, 0, 7) ne "\x7f\x45\x4c\x46\x01\x01\x01"); + +syswrite (FH,$sig,length($sig)) || die; +syswrite (FH,"\0\0\0\0\0\0\0\0",8-length($sig)) || die; +close (FH) || die; + +exit (0); + +# vi:ts=4:et diff --git a/src/stub/scripts/o2bin.pl b/src/stub/scripts/o2bin.pl new file mode 100644 index 0000000000..6ec7b10fb3 --- /dev/null +++ b/src/stub/scripts/o2bin.pl @@ -0,0 +1,75 @@ +#! /usr/bin/perl -w +# +# o2bin.pl -- +# +# This file is part of the UPX executable compressor. +# +# Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +# Copyright (C) 1996-2000 Laszlo Molnar +# +# UPX and the UCL library are free software; you can redistribute them +# and/or modify them 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; see the file COPYING. +# If not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Markus F.X.J. Oberhumer Laszlo Molnar +# markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +# + + +$delim = $/; +undef $/; # undef input record separator - read file as a whole + +$ifile = shift || die; +$ofile = shift || die; +$x_start = shift || die; +$x_end = shift || die; + +# read whole file +open(INFILE,$ifile) || die "$ifile\n"; +binmode(INFILE); +$data = ; +close(INFILE) || die; + +# delete everything up to 'UPX1' +die if ($data =~ s/^.*${x_start}//s) != 1; + +# delete everything from 'UPX9' +die if ($data =~ s/${x_end}.*$//s) != 1; + +# write file +open(OUTFILE,">$ofile") || die "$ofile\n"; +binmode(OUTFILE); +if ($ofile =~ /\.(db)$/i) { + # asm "db xx" output + $n = length($data); + $l = 16; + for ($i = 0; $i < $n; ) { + print OUTFILE "db " if ($i % $l == 0); + printf OUTFILE ("%d", ord(substr($data, $i, 1))); + ++$i; + if ($i == $n || $i % $l == 0) { + print OUTFILE "\n"; + } else { + print OUTFILE ","; + } + } +} else { + print OUTFILE $data; +} +close(OUTFILE) || die; + +undef $delim; +exit(0); + +# vi:ts=4:et diff --git a/src/stub/scripts/setfold.pl b/src/stub/scripts/setfold.pl new file mode 100644 index 0000000000..5424d67b18 --- /dev/null +++ b/src/stub/scripts/setfold.pl @@ -0,0 +1,45 @@ +#! /usr/bin/perl -w +# +# setfold.pl -- set Elf32_Phdr[1].p_offset +# +# This file is part of the UPX executable compressor. +# +# Copyright (C) 2000 John F. Reiser. All rights reserved. +# +# UPX and the UCL library are free software; you can redistribute them +# and/or modify them 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; see the file COPYING. +# If not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# John Reiser +# jreiser@BitWagon.com +# + +$fname = shift || die; +sysopen (FH,$fname,2) || die; +binmode FH; + +$val = oct shift || die; +$num = pack("V", $val); + +# 0x34 = sizeof(Elf32_Ehdr) +# 0x20 = sizeof(Elf32_Phdr) +# 4 = offset(p_offset) +sysseek (FH,0x34+0x20+4,0) || die; + +syswrite (FH,$num,4) || die; + +close(FH) || die; +exit 0; + +# vi:ts=4:et diff --git a/src/stub/scripts/stripelf.pl b/src/stub/scripts/stripelf.pl new file mode 100644 index 0000000000..5fd596071c --- /dev/null +++ b/src/stub/scripts/stripelf.pl @@ -0,0 +1,77 @@ +#! /usr/bin/perl -w +# +# stripelf.pl -- strip section headers from an ELF executable +# +# This file is part of the UPX executable compressor. +# +# Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +# Copyright (C) 1996-2000 Laszlo Molnar +# +# UPX and the UCL library are free software; you can redistribute them +# and/or modify them 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; see the file COPYING. +# If not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Markus F.X.J. Oberhumer Laszlo Molnar +# markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +# + +# +# Strip section headers from the Linux stub. Section headers are +# optional for executables, but nevertheless binutils (2.9.1.0.25) +# complain with a "File format not recognized" error. +# Looks like a bug in binutils to me. +# +# A positive side effect of this is that `strip' cannot ruin an UPX +# compressed file any longer. +# + +$fname = shift || die; +sysopen (FH,$fname,2) || die; +binmode FH; + +sysseek (FH,0x20,0) || die; +sysread (FH,$num,4) || die; +$shpos = unpack ("V",$num); # e_shoff + +sysseek (FH,0x2e,0) || die; +sysread (FH,$num,2) || die; +$ssize = unpack ("v",$num); # e_shentsize + +sysseek (FH,0x32,0) || die; +sysread (FH,$num,2) || die; +$idx = unpack ("v",$num); # e_shstrndx + +sysseek (FH,$shpos + $idx * $ssize + 16,0) || die; +sysread (FH,$num,4) || die; +$neweof = unpack ("V",$num); # sh_offset of the e_shstrndx section + +$num = pack ("x6"); +sysseek (FH,0x20,0) || die; +syswrite (FH,$num,4) || die; # clear e_shoff + +if (1) { + sysseek (FH,0x2e,0) || die; + syswrite (FH,$num,6) || die; # clear e_shentsize, e_shnum & e_shstrndx +} else { + sysseek (FH,0x30,0) || die; + syswrite (FH,$num,4) || die; # clear e_shnum & e_shstrndx +} + +truncate (FH,$neweof) || die; +close(FH) || die; + +print STDOUT "$0: truncated $fname to $neweof bytes.\n"; +exit 0; + +# vi:ts=4:et diff --git a/src/stub/scripts/version.pl b/src/stub/scripts/version.pl new file mode 100644 index 0000000000..3ecd66bd5e --- /dev/null +++ b/src/stub/scripts/version.pl @@ -0,0 +1,40 @@ +#! /usr/bin/perl -w +# +# version.pl -- convert version.h into version.asy +# +# This file is part of the UPX executable compressor. +# +# Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer +# Copyright (C) 1996-2000 Laszlo Molnar +# +# UPX and the UCL library are free software; you can redistribute them +# and/or modify them 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; see the file COPYING. +# If not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Markus F.X.J. Oberhumer Laszlo Molnar +# markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu +# + +$mode = shift || "--nasm"; +while (<>) { + chop; + s/\s+$//; + if (/^\s*\#\s*define\s+(.*)/) { + print "%define $1\n" if ($mode eq "--nasm"); + } +} + +exit (0); + +# vi:ts=4:et diff --git a/src/stub/stub.asm b/src/stub/stub.asm new file mode 100644 index 0000000000..cc641f5421 --- /dev/null +++ b/src/stub/stub.asm @@ -0,0 +1,984 @@ +; Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details +; Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details +; Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details +; Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details +; -*- asm -*- +; +; KLUDGE-WARNING! +; +; So you say you want to change this file, right? Are you really sure +; that's a good idea? Let me tell you a bit about the pitfalls here: +; +; * Some code runs in protected mode, some in real-mode, some in both. +; * Some code must run on a 8088 without crashing it. +; * Registers and flags may be expected to survive for a long time. +; * The code is optimized for size, not for speed or readability. +; * Some comments are parsed by other programs. +; +; You still want to change it? Oh well, go ahead, but don't come +; crying back saying you weren't warned. +; +;----------------------------------------------------------------------------- +; djgpp extender-less stub loader +; +; (C) Copyright 1993-1995 DJ Delorie +; +; Redistribution and use in source and binary forms are permitted +; provided that: (1) source distributions retain this entire copyright +; notice and comment, (2) distributions including binaries display +; the following acknowledgement: ``This product includes software +; developed by DJ Delorie and contributors to the djgpp project'' +; in the documentation or other materials provided with the distribution +; and in all advertising materials mentioning features or use of this +; software, and (3) binary distributions include information sufficient +; for the binary user to obtain the sources for the binary and utilities +; required to built and use it. Neither the name of DJ Delorie nor the +; names of djgpp's contributors may be used to endorse or promote +; products derived from this software without specific prior written +; permission. +; +; THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +; IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +; +; Revision history: +; +; 93/12/05 DJ Delorie Initial version v2.00, requires DPMI 0.9 +; 94/10/13 CW Sandmann v2.01, accumlated changes: 60K load bug, limits, cwsdpmi, optimization +; 94/10/29 CW Sandmann v2.03, M Welinder changes; cwsdpmi load anywhere, size decrease +; + .copyright "The STUB.EXE stub loader is Copyright (C) 1993-1995 DJ Delorie. " + .copyright "Permission granted to use for any purpose provided this copyright " + .copyright "remains present and unmodified. " + .copyright "This only applies to the stub, and not necessarily the whole program.\n" + .id +; +;----------------------------------------------------------------------------- +; Interface to 32-bit executable: +; +; cs:eip according to COFF header +; ds 32-bit data segment for COFF program +; fs selector for our data segment (fs:0 is stubinfo) +; ss:sp our stack (ss to be freed) +; All unspecified registers have unspecified values in them. +;----------------------------------------------------------------------------- +; This is the stubinfo structure. The presence of this structure +; indicates that the executable is a djgpp v2.00 executable. +; Fields will never be deleted from this structure, only obsoleted. +; + .org 0 ; just in case +stubinfo: +stubinfo_magic: ; char [16] + .db "go32stub, v 2.02" ; version may change, [0..7] won't +stubinfo_size: ; unsigned long + .dd stubinfo_end ; bytes in structure +stubinfo_minstack: ; unsigned long + .dd 0x80000 ; minimum amount of DPMI stack space (512K) +stubinfo_memory_handle: ; unsigned long + .dd 0 ; DPMI memory handle +stubinfo_initial_size: ; unsigned long + .dd 0 ; size of initial segment +stubinfo_minkeep: ; unsigned short + .dw 16384 ; amount of automatic real-mode buffer +stubinfo_ds_selector: ; unsigned short + .dw 0 ; our DS selector (used for transfer buffer) +stubinfo_ds_segment: ; unsigned short + .dw 0 ; our DS segment (used for simulated calls) +stubinfo_psp_selector: ; unsigned short + .dw 0 ; PSP selector +stubinfo_cs_selector: ; unsigned short + .dw 0 ; to be freed +stubinfo_env_size: ; unsigned short + .dw 0 ; number of bytes of environment +stubinfo_basename: ; char [8] + .db 8 .dup 0 ; base name of executable to load (asciiz if < 8) +stubinfo_argv0: ; char [16] + .db 16 .dup 0 ; used ONLY by the application (asciiz if < 16) +stubinfo_dpmi_server: ; char [16] + .db "CWSDPMI.EXE\0\0\0\0\0" ; used by stub to load DPMI server if no DPMI already present + + .align 4 +stubinfo_end: + +;----------------------------------------------------------------------------- +; First, set up our memory and stack environment + + .start ; execution begins here + push cs + pop ds + mov [stubinfo_ds_segment], ds + + mov [psp_segment], es ; save the PSP segment + cld + +;----------------------------------------------------------------------------- +; Check that we have DOS 3.00 or later. (We need this because earlier +; versions don't supply argv[0] to us and will scrog registers on dpmi exec). + mov ah, 0x30 + int 0x21 + cmp al, 3 + jae dos3ok + mov al, 109 + mov dx, msg_bad_dos + jmpl error +dos3ok: + mov [dos_major], al + mov si, stubinfo_minkeep + +;----------------------------------------------------------------------------- +; Resize memory in case we need to exec a DPMI server + +resize_again: + mov ax, [si] ; si=&stubinfo_minkeep + or ax, ax + jnz @f1 +; mov ax,0xfe00 ; 0 was probably 64k, so max it! (mod 512) + mov ah,0xfe ; al already 0 +@f1: + mov bx, end_of_memory ; does not include PSP + cmp bx, ax ; is our program big enough to hold it? + jae @f1 + mov bx, ax +@f1: + mov [si], bx ; si=&stubinfo_minkeep store for reference + inc bh ; add 256 bytes for PSP + mov cx, 0xff04 ; 0xff is for below + shr bx, cl ; bytes to paragraphs + + mov ah, 0x4a ; ES = PSP segment from above + int 0x21 ; resize our memory block + jnc @f1 ; did it work? + shl bx,cl ; calculate smaller [keep] value + dec bh + mov [si], bx ; si=&stubinfo_minkeep + jmp resize_again ; and try again +@f1: + +;----------------------------------------------------------------------------- +; Scan environment for "PATH=" and the stub's full name after environment + + mov es, es:[0x2c] ; get environment segment + xor di, di ; begin search for NUL/NUL (di = 0) +; mov cx, 0xff04 ; effectively `infinite' loop + xor al, al + .db 0xa9 ; "test ax,...." -- skip 2 bytes +scan_environment: + repne + scasb ; search for NUL + cmpw es:[di], 0x4150 ; "PA" + jne not_path + scasw + cmpw es:[di], 0x4854 ; "TH" + jne not_path + scasw + cmpb es:[di], '=' + jne not_path + inc di ; Point to PATH contents + mov [path_off], di ; save for later + dec di ; in case the PATH is empty +not_path: + scasb + jne scan_environment ; no, still environment + scasw ; adjust pointer to point to prog name + +;; When we are spawned from a program which has more than 20 handles in use, +;; all the handles passed to us by DOS are taken (since only the first 20 +;; handles are inherited), and opening the .exe file will fail. +;; Therefore, we forcefully close handles 18 and 19, to make sure at least two +;; handles are available. + + mov ah, 0x3e + mov bx, 19 + int 0x21 ; don't care about errors + mov ah, 0x3e + mov bx, 18 + int 0x21 ; don't care about errors + +;----------------------------------------------------------------------------- +; Get DPMI information before doing anything 386-specific + + push es + push di + xor cx, cx ; flag for load attempt set cx = 0 + jz @f2 ; We always jump, shorter than jmp +@b1: + mov al, 110 + mov dx, msg_no_dpmi + jmpl error +@b2: + or cx, cx + jnz @b1 ; we already tried load once before + inc cx + call load_dpmi + jc @b1 +@f2: + mov ax, 0x1687 ; get DPMI entry point + int 0x2f + or ax, ax + jnz @b2 ; if 0 then it's there + and bl, 1 ; 32 bit capable? + jz @b2 +@f3: + mov [modesw], di ; store the DPMI entry point + mov [modesw+2], es + mov [modesw_mem], si + pop di + pop es + +;----------------------------------------------------------------------------- +; Now, find the name of the program file we are supposed to load. + +; xor ah, ah ; termination character (set above!) + call store_env_string ; copy it to loadname, set bx + + mov [stubinfo_env_size], di + mov [loadname_nul], si ; remember nul so we can change it to $ + cmpb stubinfo_basename[0], 0 + je no_symlink + +;----------------------------------------------------------------------------- +; Replace the stub's file name with the link's name after the directory + + mov cx, 8 ; max length of basename + mov di, stubinfo_basename ; pointer to new basename +@b1: + mov al, [di] ; get next character + inc di + or al, al ; end of basename? + je @f1 + mov [bx], al ; store character + inc bx + loop @b1 ; eight characters? +@f1: + movd [bx], 0x4558452e ; append ".EXE" + add bx, 4 + movb [bx], 0 ; null terminate + mov [loadname_nul], bx ; remember nul so we can change it to $ + +no_symlink: + +;----------------------------------------------------------------------------- +; Load the COFF information from the file + + mov ax, 0x3d00 ; open file for reading + mov dx, loadname + int 0x21 + jcl error_no_progfile ; do rest of error message + +@f1: + mov [program_file], ax ; store for future reference + + mov bx, ax + mov cx, exe_header_length + mov dx, exe_header + mov ah, 0x3f ; read EXE header + int 0x21 + + xor dx, dx ; dx = 0 + xor cx, cx ; offset of COFF header + + mov ax, [exe_magic] + cmp ax, 0x014c ; COFF? + je file_is_just_coff + cmp ax, 0x5a4d ; EXE magic value + jnel error_not_exe + + mov dx, [exe_sectors] + shl dx, 9 ; 512 bytes per sector + mov bx, [exe_bytes_last_page] + or bx, bx ; is bx = 0 ? + je @f1 + sub dh, 2 ; dx -= 512 + add dx, bx +@f1: + +file_is_just_coff: ; cx:dx is offset + mov coff_offset[0], dx + mov coff_offset[2], cx + mov ax, 0x4200 ; seek from beginning + mov bx, [program_file] + int 0x21 + + mov cx, coff_header_length + mov dx, coff_header + mov ah, 0x3f ; read file (bx = handle) + int 0x21 + + cmp ax, coff_header_length + jne @f2 + cmpw coff_header[coff_magic], 0x014c +@f2: + jnel error_not_coff + + mov eax, aout_header[aout_entry] + mov [start_eip], eax + + mov ecx, [coff_offset] + + mov eax, text_section[s_scnptr] + add eax, ecx + mov [text_foffset], eax + + mov eax, data_section[s_scnptr] + add eax, ecx + mov [data_foffset], eax + + mov ebx, bss_section[s_vaddr] + mov eax, bss_section[s_size] + add ebx, eax + mov eax, 0x00010001 + cmp ebx, eax + jae @f1 + mov ebx, eax ; ensure 32-bit segment +@f1: + add ebx, 0x0000ffff ; ensure 64K rounded + xor bx, bx ; clear rounded bits + mov [stubinfo_initial_size], ebx + +;----------------------------------------------------------------------------- +; Set up for the DPMI environment + + call include_umb + mov bx, [modesw_mem] + or bx, bx + jz no_dos_alloc + + mov ah, 0x48 ; allocate memory for the DPMI host + int 0x21 + jcl error_no_dos_memory_umb + mov es, ax + +no_dos_alloc: + call restore_umb + mov ax, 1 ; indicates a 32-bit client + callf [modesw] ; enter protected mode + + jcl error_in_modesw + +;----------------------------------------------------------------------------- +; We're in protected mode at this point. + + mov [stubinfo_psp_selector], es + mov [stubinfo_cs_selector], cs + mov ax, ds + mov [stubinfo_ds_selector], ax + mov es, ax + + xor ax, ax ; AX = 0x0000 + mov cx, 1 + int 0x31 ; allocate LDT descriptor + jc @f2 + mov [client_cs], ax + + xor ax, ax ; AX = 0x0000 +; mov cx, 1 ; already set above + int 0x31 ; allocate LDT descriptor +@f2: + jcl perror_no_selectors + mov [client_ds], ax + +; Try getting a DPMI 1.0 memory block first, then try DPMI 0.9 +; Note: This causes the Borland Windows VxD to puke, commented for now with ;* +;* mov ax, 0x0504 +;* xor ebx, ebx ; don't specify linear address + mov ecx, stubinfo_initial_size[0] +;* mov edx, 1 ; allocate committed pages +;* int 0x31 ; allocate memory block +;* jc try_old_dpmi_alloc +;* mov client_memory[0], ebx +;* mov stubinfo_memory_handle[0], esi +;* jmp got_dpmi_memory +try_old_dpmi_alloc: + mov ax, 0x0501 + mov bx, stubinfo_initial_size[2] +; mov cx, stubinfo_initial_size[0] ;Set above + int 0x31 ; allocate memory block + jcl perror_no_dpmi_memory + mov client_memory[2], bx + mov client_memory[0], cx + mov stubinfo_memory_handle[2], si + mov stubinfo_memory_handle[0], di +got_dpmi_memory: + + mov ax, 0x0007 + mov bx, [client_cs] ; initialize client CS + mov cx, client_memory[2] + mov dx, client_memory[0] + int 0x31 ; set segment base address + + mov ax, 0x0009 +; mov bx, [client_cs] ; already set above + mov cx, cs ; get CPL + and cx, 0x0003 + shl cx, 5 + push cx ; save shifted CPL for below + or cx, 0xc09b ; 32-bit, big, code, non-conforming, readable + int 0x31 ; set descriptor access rights + + mov ax, 0x0008 +; mov bx, [client_cs] ; already set above + mov cx, stubinfo_initial_size[2] + dec cx + mov dx, 0xffff + int 0x31 ; set segment limit + + mov ax, 0x0007 + mov bx, [client_ds] ; initialize client DS + mov cx, client_memory[2] + mov dx, client_memory[0] + int 0x31 ; set segment base address + + mov ax, 0x0009 +; mov bx, [client_ds] ; already set above + pop cx ; shifted CPL from above + or cx, 0xc093 ; 32-bit, big, data, r/w, expand-up + int 0x31 ; set descriptor access rights + + mov ax, 0x0008 +; mov bx, [client_ds] ; already set above + mov cx, stubinfo_initial_size[2] + dec cx + mov dx, 0xffff + int 0x31 ; set segment limit + +;----------------------------------------------------------------------------- +; Load the program data + + mov ax, 0x0100 + mov bx, 0x0f00 ; 60K DOS block size + int 0x31 ; allocate DOS memory + jnc @f1 + cmp ax, 0x0008 + jnel perror_no_dos_memory + mov ax, 0x0100 ; try again with new value in bx + int 0x31 ; allocate DOS memory + jcl perror_no_dos_memory +@f1: + mov [dos_block_seg], ax + mov [dos_block_sel], dx + shl bx, 4 ; paragraphs to bytes + mov [dos_block_size], bx + + mov esi, [text_foffset] ; load text section + mov edi, text_section[s_vaddr] + mov ecx, text_section[s_size] + call read_section + + mov esi, [data_foffset] ; load data section + mov edi, data_section[s_vaddr] + mov ecx, data_section[s_size] + call read_section + + mov es, [client_ds] ; clear the BSS section + mov edi, bss_section[s_vaddr] + mov ecx, bss_section[s_size] + xor eax,eax + shr ecx,2 + .addrsize + rep + stosd + + mov ah,0x3e + mov bx, [program_file] + int 0x21 ; close the file + + mov ax, 0x0101 + mov dx, [dos_block_sel] + int 0x31 ; free up the DOS memory + + push ds + pop fs + mov ds, [client_ds] + .opsize + jmpf fs:[start_eip] ; start program + +;----------------------------------------------------------------------------- +; Read a section from the program file + +read_section: + mov eax, esi ; sector alignment by default + and eax, 0x1ff + add ecx, eax + sub si, ax ; sector align disk offset (can't carry) + sub edi, eax ; memory maybe not aligned! + + mov [read_size], ecx ; store for later reference + mov [read_soffset], edi + + call zero_regs + mov dpmi_regs[dr_dx], si ; store file offset + shr esi, 16 + mov dpmi_regs[dr_cx], si + mov bx, [program_file] + mov dpmi_regs[dr_bx], bx + movw dpmi_regs[dr_ax], 0x4200 + call pm_dos ; seek to start of data + +; Note, handle set above + mov ax, [dos_block_seg] + mov dpmi_regs[dr_ds], ax + movw dpmi_regs[dr_dx], 0 ; store file offset +read_loop: + movb dpmi_regs[dr_ah], 0x3f + mov ax, read_size[2] ; see how many bytes to read + or ax, ax + jnz read_too_big + mov ax, read_size[0] + cmp ax, [dos_block_size] + jna read_size_in_ax ; jna shorter than jmp +read_too_big: + mov ax, [dos_block_size] +read_size_in_ax: + mov dpmi_regs[dr_cx], ax + call pm_dos ; read the next chunk of file data + + xor ecx, ecx + mov cx, dpmi_regs[dr_ax] ; get byte count + mov edi, [read_soffset] ; adjust pointers + add [read_soffset], ecx + sub [read_size], ecx + + xor esi, esi ; esi=0 offset for copy data + shr cx, 2 ; ecx < 64K + push ds + push es + mov es, [client_ds] + mov ds, [dos_block_sel] + .addrsize + rep + movsd + pop es + pop ds + + add ecx, [read_size] ; ecx zero from the rep movsd + jnz read_loop + + ret + +;----------------------------------------------------------------------------- +; Routine to check al for delimiter + +test_delim: + cmp al, ':' ; watch for file name part + je @f3 + cmp al, '/' + je @f3 + cmp al, '\\' +@f3: + ret + +;----------------------------------------------------------------------------- +; Copy string from environment to loadname. +; On entry: di = environment offset +; ah = termination character (null also does) +; On exit: bx = pointer to one character after last observed file delimiter +; di = pointer to one character after last copied +; si = pointer to the copied termination character +; al = terminating character + +store_env_string: + mov si, loadname ; pointer to buffer + mov bx, si ; in case no delimiters +@b1: + mov al, es:[di] ; copy a character to buffer + inc di + mov [si], al + cmp al, ah ; end of file name? + je @f1 + or al, al ; end of file name? + je @f1 + inc si + call test_delim + jne @b1 + mov bx, si ; remember pointer to first char of + je @b1 ; next name component (shorter than jmp) +@f1: + ret + +;----------------------------------------------------------------------------- +; Most errors come here, early ones jump direct (8088 instructions) + +error_no_progfile: + mov al, 102 + mov dx, msg_no_progfile + jmp error_fn + +error_not_exe: + mov al, 103 + mov dx, msg_not_exe + jmp error_fn + +error_not_coff: + mov al, 104 + mov dx, msg_not_coff +; jmp error_fn + +error_fn: + push dx + mov bx, [loadname_nul] ; error, print file name + movb [bx], '$' + mov bx, loadname + jmp @f1 + +error_no_dos_memory_umb: + call restore_umb +error_no_dos_memory: + mov al, 105 + mov dx, msg_no_dos_memory + jmp error + +error_in_modesw: + mov al, 106 + mov dx, msg_error_in_modesw + jmp error + +perror_no_selectors: + mov al, 107 + mov dx, msg_no_selectors + jmp error + +perror_no_dpmi_memory: + mov al, 108 + mov dx, msg_no_dpmi_memory + jmp error + +perror_no_dos_memory: + mov al, 105 + mov dx, msg_no_dos_memory +; jmp error + +error: + push dx + mov bx, err_string +@f1: + call printstr + pop bx + call printstr +exit: + mov bx, crlfdollar + call printstr + mov ah, 0x4c ; error exit - exit code is in al + int 0x21 + +printstr1: + inc bx + push ax ; have to preserve al set by error call + mov ah, 2 + int 0x21 + pop ax ; restore ax (John A.) +printstr: + mov dl, [bx] + cmp dl, '$' + jne printstr1 + ret + +crlfdollar: + .db 13,10,'$' +;----------------------------------------------------------------------------- +; DPMI utility functions + +zero_regs: + push ax + push cx + push di + xor ax, ax + mov di, dpmi_regs + mov cx, 0x19 + rep + stosw + pop di + pop cx + pop ax + ret + +pm_dos: + mov ax, 0x0300 ; simulate interrupt + mov bx, 0x0021 ; int 21, no flags + xor cx, cx ; cx = 0x0000 (copy no args) + mov edi, dpmi_regs + int 0x31 + ret + +;----------------------------------------------------------------------------- +; load DPMI server if not present +; First check directory from which stub is loaded, then path, then default +; On entry di points to image name + +path_off: + .dw 0 ; If stays zero, no path + +load_dpmi: + xor ah, ah ; Copy until this character (=0) + call store_env_string ; copy stub image to "loadname" + mov si, bx ; remove name so we can add DPMI name + mov di, [path_off] ; Pointer to path contents (next try) + jmp @f2 +loadloop: + mov ah, ';' ; Copy until this character + call store_env_string ; to "loadname" + cmp si, loadname ; anything there? + je do_exec ; final try (no path) let it return + mov al, [si-1] + call test_delim ; is final character a path delimiter + je @f2 + movb [si], '\\' ; no, add separator between path & name + inc si +@f2: + call do_exec ; copy our name to string and try load + jc loadloop + ret + +;----------------------------------------------------------------------------- +; add the string CWSDPMI to path ending + +do_exec: + call include_umb + mov bx, stubinfo_dpmi_server +@b1: + mov al, [bx] + mov [si], al + inc bx + inc si + or al, al + jne @b1 +; movw [si], 0x0a0d ;debug +; movb [si+2], '$' ;debug + + push es ; Save in case of failure + push di + +;memory saving - use dpmi_regs as a temporary parameter block + push ds + pop es ;zero_regs needs es set + call zero_regs + mov bx, dpmi_regs + mov [bx+4], ds ;segment of command tail + mov [bx+2], bx ;offset (point to zero) + + mov dx, loadname +; mov ah, 9 ;debug +; int 0x21 ;debug + mov ax, 0x4b00 ;Do program exec + int 0x21 + pop di + pop es + jc @f1 ;carry set if exec failed + + mov ah, 0x4d ;get return code + int 0x21 + sub ax, 0x300 ;ah=3 TSR, al=code (success) + neg ax ;CY, if not originally 0x300 +@f1: + jmp restore_umb ;called func. return for us. + +;----------------------------------------------------------------------------- +; Make upper memory allocatable. Clobbers Ax and Bx. + +include_umb: + cmpb [dos_major], 5 ; Won't work before dos 5 + jb @f1 + mov ax, 0x5800 ; get allocation strategy + int 0x21 + mov [old_strategy],al + mov ax, 0x5802 ; Get UMB status. + int 0x21 + mov [old_umb],al + mov ax, 0x5801 + mov bx, 0x0080 ; first fit, first high then low + int 0x21 + mov ax, 0x5803 + mov bx, 0x0001 ; include UMB in memory chain + int 0x21 +@f1: + ret + +; Restore upper memory status. All registers and flags preserved. + +restore_umb: + pushf + cmpb [dos_major], 5 ; Won't work before dos 5 + jb @f1 + push ax + push bx + mov ax, 0x5803 ; restore UMB status. + mov bl,[old_umb] + xor bh, bh + int 0x21 + mov ax, 0x5801 ; restore allocation strategy + mov bl,[old_strategy] + xor bh, bh + int 0x21 + pop bx + pop ax +@f1: + popf + ret + +;----------------------------------------------------------------------------- +; Stored Data +err_string: + .db "Load error: $" +msg_no_progfile: + .db ": can't open$" +msg_not_exe: + .db ": not EXE$" +msg_not_coff: + .db ": not COFF (Check for viruses)$" +msg_no_dpmi: + .db "no DPMI - Get csdpmi*b.zip$" +msg_no_dos_memory: + .db "no DOS memory$" +msg_bad_dos: + .db "need DOS 3$" +msg_error_in_modesw: + .db "can't switch mode$" +msg_no_selectors: + .db "no DPMI selectors$" +msg_no_dpmi_memory: + .db "no DPMI memory$" + +;----------------------------------------------------------------------------- +; Unstored Data, available during and after mode switch + +last_generated_byte: + + .align 512 ; Align ourselves to a sector + ; boundary for startup speed. + .bss ; data after this isn't in file. + +modesw: ; address of DPMI mode switch + .dd 0 +modesw_mem: ; amount of memory DPMI needs + .dw 0 + +program_file: ; file ID of program data + .dw 0 + +text_foffset: ; offset in file + .dd 0 + +data_foffset: ; offset in file + .dd 0 + +start_eip: ; EIP value to start at + .dd 0 +client_cs: ; must follow start_eip + .dw 0 +client_ds: + .dw 0 + +client_memory: + .dd 0 + +dos_block_seg: + .dw 0 +dos_block_sel: + .dw 0 +dos_block_size: + .dw 0 + +read_soffset: + .dd 0 +read_size: + .dd 0 + +dpmi_regs: + .db 0x32 .dup 0 +dr_edi = 0x00 +dr_di = 0x00 +dr_esi = 0x04 +dr_si = 0x04 +dr_ebp = 0x08 +dr_bp = 0x08 +dr_ebx = 0x10 +dr_bx = 0x10 +dr_bl = 0x10 +dr_bh = 0x11 +dr_edx = 0x14 +dr_dx = 0x14 +dr_dl = 0x14 +dr_dh = 0x15 +dr_ecx = 0x18 +dr_cx = 0x18 +dr_cl = 0x18 +dr_ch = 0x19 +dr_eax = 0x1c +dr_ax = 0x1c +dr_al = 0x1c +dr_ah = 0x1d +dr_efl = 0x20 +dr_es = 0x22 +dr_ds = 0x24 +dr_fs = 0x26 +dr_gs = 0x28 +dr_ip = 0x2a +dr_cs = 0x2c +dr_sp = 0x2e +dr_ss = 0x30 + +;----------------------------------------------------------------------------- + + .align 16 ; so that stack ends on para boundary + .dw 128 .dup 0 + .stack + +;----------------------------------------------------------------------------- +; At one time real mode only data. Header stuff now used during image load. + +psp_segment: + .dw 0 + +loadname_nul: ; offset of NUL so it can become '$' + .dw 0 +loadname: ; name of program file to load, if it + .db 81 .dup 0 ; gets really long ok to overwrite next + +exe_header: ; loaded from front of loadfile +exe_magic: + .dw 0 +exe_bytes_last_page: + .dw 0 +exe_sectors: + .dw 0 +exe_header_length = . - exe_header + +coff_offset: + .dd 0 ; from start of file + +coff_header: ; loaded from after stub + .db 20 .dup 0 +aout_header: + .db 28 .dup 0 +text_section: + .db 40 .dup 0 +data_section: + .db 40 .dup 0 +bss_section: + .db 40 .dup 0 +coff_header_length = . - coff_header + +old_strategy: + .db 0 +old_umb: + .db 0 + +dos_major: + .db 0 + + .align 16 ; Align ourselves to a paragraph +end_of_memory: ; resize is done early so must keep all + +;----------------------------------------------------------------------------- +; structure definitions +; + +coff_magic = 0 ; from coff header + +aout_entry = 16 ; from aout header + +s_paddr = 8 ; from section headers +s_vaddr = 12 +s_size = 16 +s_scnptr = 20 + + diff --git a/src/stub/util/sstrip/Makefile b/src/stub/util/sstrip/Makefile new file mode 100644 index 0000000000..dad4c92a6a --- /dev/null +++ b/src/stub/util/sstrip/Makefile @@ -0,0 +1,7 @@ +# Makefile for sstrip + +sstrip: sstrip.c + gcc -ggdb -Wall -W -o sstrip sstrip.c + +clean: + rm -f sstrip diff --git a/src/stub/util/sstrip/README b/src/stub/util/sstrip/README new file mode 100644 index 0000000000..b96c17163e --- /dev/null +++ b/src/stub/util/sstrip/README @@ -0,0 +1,40 @@ +sstrip is a small utility that removes the contents at the end of an +ELF file that are not part of the program's memory image. + +Most ELF executables are built with both a program header table and a +section header table. However, only the former is required in order +for the OS to load, link and execute a program. sstrip attempts to +extract the ELF header, the program header table, and its contents, +leaving everything else in the bit bucket. It can only remove parts of +the file that occur at the end, after the parts to be saved. However, +this almost always includes the section header table, and occasionally +a few random sections that are not used when running a program. + +It should be noted that the GNU bfd library is (understandably) +dependent on the section header table as an index to the file's +contents. Thus, an executable file that has no section header table +cannot be used with gdb, objdump, or any other program based upon the +bfd library, at all. In fact, the program will not even recognize the +file as a valid executable. (This limitation is noted in the source +code comments for bfd, and is marked "FIXME", so this may change at +some future date. However, I would imagine that it is a pretty +low-priority item, as executables without a section header table are +rare in the extreme.) This probably also explains why strip doesn't +offer the option to do this. + +Shared library files may also have their section header table removed. +Such a library will still function; however, it will no longer be +possible for a compiler to link a new program against it. + +As an added bonus, sstrip also tries to removes trailing zero bytes +from the end of the file. (This normally cannot be done with an +executable that has a section header table.) + +sstrip is a very simplistic program. It depends upon the common +practice of putting the parts of the file that contribute to the +memory image at the front, and the remaining material at the end. This +permits it to discard the latter material without affecting file +offsets and memory addresses in what remains. However, the ELF +standard permits files to be organized in almost any order. So +although this procedure usually works in practice, it is not meant to +be taken too seriously. diff --git a/src/stub/util/sstrip/README.1ST b/src/stub/util/sstrip/README.1ST new file mode 100644 index 0000000000..2cbb363d48 --- /dev/null +++ b/src/stub/util/sstrip/README.1ST @@ -0,0 +1,70 @@ +This distribution is a collection of programs that are generally +unrelated, except in that they all deal with the ELF file format. + +The main purpose of these programs is to be illustrative and +educational -- to help fellow programmers understand the ELF file +format and something of how it works under the Linux platform. For the +most part, these programs have limited real-world utility. (Although I +myself have found these programs quite useful while writing the +others.) + +Each program is independent. There is no shared code between them, and +in fact they all take slightly different approaches to handling ELF +files. + +The table of contents: + +sstrip/ + sstrip is a small utility that removes everything from an ELF file + that is not part of the file's memory image. + +elfls/ + elfls is a utility that displays an ELF file's program and/or + section header tables, which serve as a kind of global roadmap to + the file's contents. + +elftoc/ + elftoc takes an ELF file and generates C code that defines a + structure with the same memory image, using the structures and + preprocessor symbols defined in . + +ebfc/ + ebfc is a compiler for a tiny programming language. The compiler can + generate ELF executables, object files, and shared libraries. + +tiny/ + This directory contains a collection of very small ELF executables. + +See the README in each directory for more details. + +The ELF standard is necessary reading if you wish to fully understand +how these programs work. You can download a copy as a Postscript +document from ftp://tsx.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz. +Alternately, you can obtain a flat-text transcription of this document +from http://www.muppetlabs.com/~breadbox/software/ELF.txt. + + All these programs are Copyright (C) 1999 by Brian Raiter. + + These programs are all free software; you can redistribute and/or + modify them 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. + + These programs are distributed in the hope that they will be + interesting, 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, in the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA. + +Bug reports and general feedback should be directed to the author at +breadbox@muppetlabs.com. + +Share and enjoy. + +Brian Raiter +breadbox@muppetlabs.com +July, 1999 diff --git a/src/stub/util/sstrip/sstrip.c b/src/stub/util/sstrip/sstrip.c new file mode 100644 index 0000000000..e109e8eb9d --- /dev/null +++ b/src/stub/util/sstrip/sstrip.c @@ -0,0 +1,218 @@ +/* sstrip, version 1.0: Copyright (C) 1999 by Brian Raiter, under the + * GNU General Public License. No warranty. See COPYING for details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* The memory-allocation macro. + */ +#define alloc(p, n) (((p) = realloc(p, n)) \ + || (fputs("Out of memory.\n", stderr), \ + exit(EXIT_FAILURE), 0)) + +static char const *thefilename; /* the current file name */ +static FILE *thefile; /* the current file handle */ + +static Elf32_Ehdr elfhdr; /* original ELF header */ +static Elf32_Phdr *phdrs = NULL; /* original program header tbl */ +static unsigned long phdrsize; /* size of program header tbl */ +static unsigned long newsize; /* size of the new file */ + +/* An error-handling function. The given error message is used only + * when errno is not set. + */ +static int err(char const *errmsg) +{ + if (errno) + perror(thefilename); + else + fprintf(stderr, "%s: %s\n", thefilename, errmsg); + return FALSE; +} + +/* readheaders() reads the ELF header and the program header table, + * and checks to make sure that this is in fact a file that we should + * be munging. + */ +static int readheaders(void) +{ + int bigend; + + errno = 0; + if (fread(&elfhdr, sizeof elfhdr, 1, thefile) != 1) + return err("not an ELF file."); + if (elfhdr.e_ident[EI_MAG0] != ELFMAG0 + || elfhdr.e_ident[EI_MAG1] != ELFMAG1 + || elfhdr.e_ident[EI_MAG2] != ELFMAG2 + || elfhdr.e_ident[EI_MAG3] != ELFMAG3) + return err("not an ELF file."); + + bigend = TRUE; + *(char*)&bigend = 0; + if (elfhdr.e_ident[EI_DATA] != (bigend ? ELFDATA2MSB : ELFDATA2LSB)) { + fprintf(stderr, "%s: not %s-endian.\n", + thefilename, bigend ? "big" : "little"); + return FALSE; + } + if (elfhdr.e_ehsize != sizeof(Elf32_Ehdr)) { + fprintf(stderr, "%s: unrecognized ELF header size " + "(size = %u instead of %u).\n", + thefilename, elfhdr.e_ehsize, sizeof(Elf32_Ehdr)); + return FALSE; + } + if (!elfhdr.e_phoff) + return err("no program header table."); + if (elfhdr.e_phentsize != sizeof(Elf32_Phdr)) { + fprintf(stderr, "%s: unrecognized program header size " + "(size = %u instead of %u).\n", + thefilename, elfhdr.e_phentsize, sizeof(Elf32_Ehdr)); + return FALSE; + } + + phdrsize = elfhdr.e_phnum * elfhdr.e_phentsize; + alloc(phdrs, phdrsize); + errno = 0; + if (fread(phdrs, phdrsize, 1, thefile) != 1) + return err("invalid program header table."); + + return TRUE; +} + +/* getloadsize() determines the offset of the last byte of the file + * that is actually loaded into memory. Anything after this point can + * be safely discarded. + */ +static int getloadsize(void) +{ + Elf32_Phdr *phdr; + unsigned long n; + int i; + + newsize = elfhdr.e_phoff + phdrsize; + phdr = phdrs; + for (i = 0 ; i < elfhdr.e_phnum ; ++i) { + if (phdr->p_type == PT_NULL || phdr->p_type == PT_NOTE) + continue; + n = phdr->p_offset + phdr->p_filesz; + if (n > newsize) + newsize = n; + phdr = (Elf32_Phdr*)((char*)phdr + elfhdr.e_phentsize); + } + + for (i = 0 ; i < elfhdr.e_phnum ; ++i) + if (phdr->p_filesz > 0 && phdr->p_offset >= newsize) + memset(phdr, 0, elfhdr.e_phentsize); + + return TRUE; +} + +/* truncatezeros() examines the bytes at the end of the file's + * size-to-be, and reduces the size to exclude trailing zero bytes. + */ +static int truncatezeros(void) +{ + char contents[1024]; + unsigned long n; + + do { + n = sizeof contents; + if (n > newsize) + n = newsize; + if (fseek(thefile, newsize - n, SEEK_SET) + || fread(contents, n, 1, thefile) != 1) + return err("cannot read file contents"); + while (n && !contents[--n]) + --newsize; + } while (newsize && !n); + + return TRUE; +} + +/* modifyheaders() removes references to the section header table if + * it was removed, and reduces program header table entries that + * included truncated bytes at the end of the file. + */ +static int modifyheaders(void) +{ + Elf32_Phdr *phdr; + int i; + + if (elfhdr.e_shoff >= newsize) { + elfhdr.e_shoff = 0; + elfhdr.e_shnum = 0; + elfhdr.e_shentsize = 0; + elfhdr.e_shstrndx = 0; + } + + phdr = phdrs; + for (i = 0 ; i < elfhdr.e_phnum ; ++i) { + if (phdr->p_offset + phdr->p_filesz > newsize) { + if (phdr->p_offset >= newsize) + phdr->p_filesz = 0; + else + phdr->p_filesz = newsize - phdr->p_offset; + } + phdr = (Elf32_Phdr*)((char*)phdr + elfhdr.e_phentsize); + } + + return TRUE; +} + +/* savestripped() writes the new headers back to the original file + * and sets the new file size. + */ +static int savestripped(void) +{ + rewind(thefile); + + errno = 0; + if (fwrite(&elfhdr, sizeof elfhdr, 1, thefile) != 1 + || fwrite(phdrs, phdrsize, 1, thefile) != 1 + || ftruncate(fileno(thefile), newsize)) { + err("could not write contents"); + fprintf(stderr, "WARNING: %s may be corrupted!\n", thefilename); + return FALSE; + } + + return TRUE; +} + +/* main() loops over the cmdline arguments, leaving all the real work + * to the other functions. + */ +int main(int argc, char *argv[]) +{ + char **arg; + int ret = 0; + + if (argc < 2 || !strcmp(argv[1], "-h")) { + printf("sstrip, version 2.0: Copyright (C) 1999 Brian Raiter\n" + "Usage: sstrip FILE...\n"); + return 0; + } + + for (arg = argv + 1 ; (thefilename = *arg) != NULL ; ++arg) { + if (!(thefile = fopen(thefilename, "rb+"))) { + err("unable to open."); + ++ret; + continue; + } + if (!readheaders() || !getloadsize() || !truncatezeros() + || !modifyheaders() || !savestripped()) + ++ret; + fclose(thefile); + } + + return ret; +} diff --git a/src/tailor.h b/src/tailor.h new file mode 100644 index 0000000000..9fb2500c70 --- /dev/null +++ b/src/tailor.h @@ -0,0 +1,192 @@ +/* tailor.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#if defined(__CYGWIN32__) && !defined(__CYGWIN__) +# define __CYGWIN__ __CYGWIN32__ +#endif + + +/************************************************************************* +// +**************************************************************************/ + +#if !defined(__MFX_DOS) && !defined(__MFX_WIN) && !defined(__MFX_OS2) +#if !defined(__MFX_TOS) && !defined(__MFX_PALMOS) +# if defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) +# define __MFX_WIN +# elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) +# define __MFX_WIN +# elif defined(__NT__) || defined(__NT_DLL__) || defined(__WINDOWS_386__) +# define __MFX_WIN +# elif defined(__DOS__) || defined(__MSDOS__) || defined(MSDOS) +# define __MFX_DOS +# elif defined(__OS2__) || defined(__OS2V2__) || defined(OS2) +# define __MFX_OS2 +# elif defined(__TOS__) || defined(__atarist__) +# define __MFX_TOS +# endif +#endif +#endif + +#if defined(__MFX_DOS) && !defined(__MFX_DOS16) && !defined(__MFX_DOS32) +# define __MFX_DOS32 +#endif +#if defined(__MFX_WIN) && !defined(__MFX_WIN16) && !defined(__MFX_WIN32) +# define __MFX_WIN32 +#endif + +#if !defined(DOSISH) +# if defined(__MFX_DOS) || defined(__MFX_WIN) +# define DOSISH +# elif defined(__MFX_OS2) || defined(__EMX__) +# define DOSISH +# elif defined(__MFX_TOS) +# define DOSISH +# endif +#endif + +#if defined(DOSISH) +# define HAVE_SIGNAL_H 1 +# define HAVE_CTIME 1 +# define HAVE_FILENO 1 +# define HAVE_GMTIME 1 +# define HAVE_LOCALTIME 1 +# define HAVE_MEMCMP 1 +# define HAVE_MEMCPY 1 +# define HAVE_MEMMOVE 1 +# define HAVE_MEMSET 1 +# define HAVE_SETMODE 1 +# define HAVE_STRCHR 1 +# define HAVE_STRDUP 1 +# define HAVE_STRFTIME 1 +# if defined(__CYGWIN__) +# define HAVE_STRCASECMP 1 +# define HAVE_STRNCASECMP 1 +# else +# define HAVE_STRICMP 1 +# define HAVE_STRNICMP 1 +# endif +# if !defined(DIR_SEP) +# define DIR_SEP "/\\" +# endif +# if !defined(fn_tolower) +# define fn_tolower(x) tolower(((unsigned char)(x))) +# endif +# undef __UNIX__ +# undef UNIX +# undef __unix__ +# undef unix +#endif + +#if defined(__DJGPP__) || defined(__EMX__) || defined(__CYGWIN__) +# define TIME_WITH_SYS_TIME 1 +# define HAVE_IO_H 1 +# define HAVE_UNISTD_H 1 +# define HAVE_UTIME_H 1 +# define HAVE_MODE_T 1 +# define HAVE_CHMOD 1 +# define HAVE_GETTIMEOFDAY 1 +# define HAVE_UTIME 1 +#elif defined(__MINGW32__) +# define TIME_WITH_SYS_TIME 1 +# define HAVE_CONIO_H 1 +# define HAVE_IO_H 1 +# define HAVE_SHARE_H 1 +# define HAVE_UNISTD_H 1 +# define HAVE_SYS_UTIME_H 1 +# define HAVE_MODE_T 1 +# define HAVE_CHMOD 1 +# define HAVE_UTIME 1 +#elif defined(__GNUC__) && defined(__MFX_TOS) +# define TIME_WITH_SYS_TIME 1 +# define HAVE_UNISTD_H 1 +# define HAVE_UTIME_H 1 +# define HAVE_CHMOD 1 +# define HAVE_UTIME 1 +#elif defined(__BORLANDC__) +# define __UPX_CDECL __cdecl +# define SIGTYPEENTRY __cdecl +# define HAVE_CONIO_H 1 +# define HAVE_IO_H 1 +# define HAVE_MALLOC_H 1 +# define HAVE_CHMOD 1 +# define HAVE_SHARE_H 1 +# define HAVE_UTIME_H 1 +# define HAVE_UTIME 1 +# define HAVE_VSNPRINTF 1 +# define vsnprintf _vsnprintf +#elif defined(_MSC_VER) +# define __UPX_CDECL __cdecl +# define SIGTYPEENTRY __cdecl +# define HAVE_CONIO_H 1 +# define HAVE_IO_H 1 +# define HAVE_MALLOC_H 1 +# define HAVE_CHMOD 1 +# if (_MSC_VER >= 1000) +# define HAVE_SHARE_H 1 +# define HAVE_SYS_UTIME_H 1 +# define HAVE_UTIME 1 +# define HAVE_VSNPRINTF 1 +# define vsnprintf _vsnprintf +//# pragma warning(once: 4097 4710) +# pragma warning(disable: 4097 4710) +# endif +#elif defined(__WATCOMC__) +# define __UPX_CDECL __cdecl +# define HAVE_IO_H 1 +# define HAVE_SYS_UTIME_H 1 +# define HAVE_CHMOD 1 +# define HAVE_UTIME 1 +# define NO_BOOL 1 +#endif + +#if defined(__MFX_DOS) +# define HAVE_DOS_H 1 +#endif + + +/************************************************************************* +// +**************************************************************************/ + +#ifndef DIR_SEP +# define DIR_SEP "/" +#endif + +#ifndef OPTIONS_VAR +# define OPTIONS_VAR "UPX" +#endif + +#ifndef fn_tolower +# define fn_tolower(x) (x) +#endif + + +/* +vi:ts=4:et +*/ + diff --git a/src/ui.cpp b/src/ui.cpp new file mode 100644 index 0000000000..94e036a2fd --- /dev/null +++ b/src/ui.cpp @@ -0,0 +1,659 @@ +/* ui.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "screen.h" +#include "ui.h" +#include "packer.h" + + +#if 1 && defined(USE_SCREEN) +#define UI_USE_SCREEN +#endif + + +enum { + M_QUIET, // nothing at all '-qqq' + M_INFO, // print a one line info after compression '-qq' + M_MSG, // print "compressing", then "\r" and M_INFO + M_CB_TERM, // 1 line callback using stdout + M_CB_SCREEN // 2 line callback using screen +}; + + +struct UiPacker::State +{ + int mode; + + unsigned is; + unsigned step; + unsigned next_update; + + int pass; + int total_passes; + + // message stuff + char msg_buf[1+79+1]; + int pos; // last progress bar position + int counter; // for spinner + + int bar_pos; + int bar_len; + int pass_digits; // number of digits needed to print total_passes + +#if defined(UI_USE_SCREEN) + screen_t *screen; + int b_cx, b_cy; + int s_cx, s_cy; + int s_fg, s_bg; + int c_fg; + int scroll_up; + int cursor_shape; +#else + void *screen; +#endif + + // debug + unsigned progress_updates; +}; + + +long UiPacker::total_files = 0; +long UiPacker::total_files_done = 0; +long UiPacker::total_c_len = 0; +long UiPacker::total_u_len = 0; +long UiPacker::total_fc_len = 0; +long UiPacker::total_fu_len = 0; +long UiPacker::update_c_len = 0; +long UiPacker::update_u_len = 0; +long UiPacker::update_fc_len = 0; +long UiPacker::update_fu_len = 0; + +#define clear_cb() memset(&cb, 0, sizeof(cb)) + + +static const char header_line1[] = + " File size Ratio Format Name\n"; +static const char header_line2[] = +#ifdef __MSDOS__ + " ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄ\n"; +#else + " ------------------- ------ ----------- -----------\n"; +#endif + + +static const char *mkline(unsigned long fu_len, unsigned long fc_len, + unsigned long u_len, unsigned long c_len, + const char *format, const char *filename) +{ + static char buf[2000]; + + char r[7+1]; +#if 0 + unsigned ratio = get_ratio(fc_len,fu_len); + upx_snprintf(r,sizeof(r)," %1d.%03d", ratio / 10000, (ratio % 10000) / 10); +#else + unsigned ratio = get_ratio(fc_len,fu_len, 1000); + upx_snprintf(r,sizeof(r),"%3d.%02d%%", ratio / 1000, (ratio % 1000) / 10); +#endif + upx_snprintf(buf,sizeof(buf),"%10ld ->%9ld %7s %13s %s\n", + fu_len, fc_len, r, center_string(format,13), filename); + UNUSED(u_len); + UNUSED(c_len); + return buf; +} + + +/************************************************************************* +// +**************************************************************************/ + +UiPacker::UiPacker(const Packer *p_) : + p(p_), s(NULL) +{ + clear_cb(); + + s = new State; + memset(s,0,sizeof(*s)); + s->msg_buf[0] = '\r'; + +#if defined(UI_USE_SCREEN) + // ugly hack + s->screen = sobject_get_screen(); +#endif + + if (opt->verbose < 0) + s->mode = M_QUIET; + else if (opt->verbose == 0 || !isatty(STDOUT_FILENO)) + s->mode = M_INFO; + else if (opt->verbose == 1 || opt->no_progress) + s->mode = M_MSG; + else if (!s->screen) + s->mode = M_CB_TERM; + else + s->mode = M_CB_SCREEN; +} + + +UiPacker::~UiPacker() +{ + clear_cb(); + delete s; s = NULL; +} + + +/************************************************************************* +// start callback +**************************************************************************/ + +void UiPacker::startCallback(unsigned is, unsigned step, + int pass, int total_passes) +{ + s->is = is; + s->step = step; + s->next_update = step; + + s->pass = pass; + s->total_passes = total_passes; + + s->bar_len = 64; + s->pos = -2; + s->counter = 0; + s->bar_pos = 1; // because of the leading `\r' + s->progress_updates = 0; + s->pass_digits = 0; + + clear_cb(); + + if (s->pass < 0) // no callback wanted + return; + if (s->mode <= M_INFO) + return; + if (s->mode == M_MSG) + { + if (pass <= 1) + { + con_fprintf(stdout,"Compressing %s [%s]",p->fi->getName(),p->getName()); + fflush(stdout); + printSetNl(2); + } + return; + } + + cb.callback = callback; + cb.user = this; + + if (s->mode == M_CB_TERM) + { + const char *fname = fn_basename(p->fi->getName()); + int l = (int) strlen(fname); + if (l > 0 && l <= 30) + { + strcpy(&s->msg_buf[s->bar_pos], fname); + s->bar_pos += l; + s->msg_buf[s->bar_pos++] = ' '; + s->msg_buf[s->bar_pos++] = ' '; + s->bar_len -= l + 2; + } + } + + // set pass + if (total_passes > 1) + { + do { + s->pass_digits++; + total_passes /= 10; + } while (total_passes > 0); + int l = sprintf(&s->msg_buf[s->bar_pos], "%*d/%*d ", + s->pass_digits, s->pass, + s->pass_digits, s->total_passes); + if (l > 0 && s->bar_len - l > 10) + { + s->bar_len -= l; + s->bar_pos += l; + } + } + +#if defined(UI_USE_SCREEN) + if (s->mode == M_CB_SCREEN) + { + s->screen->getCursor(s->screen,&s->s_cx,&s->s_cy); + s->s_fg = s->screen->getFg(s->screen); + s->s_bg = s->screen->getBg(s->screen); + + // FIXME: this message can be longer than one line. + // must adapt endCallback() for this case. + con_fprintf(stdout,"Compressing %s [%s]\n",p->fi->getName(),p->getName()); + s->screen->getCursor(s->screen,&s->b_cx,&s->b_cy); + if (s->b_cy == s->s_cy) + s->scroll_up++; + if (s->screen->hideCursor) + s->cursor_shape = s->screen->hideCursor(s->screen); + } +#endif /* UI_USE_SCREEN */ +} + + +// may only get called directly after startCallback() +void UiPacker::firstCallback() +{ + if (s->pos == -2) + doCallback(0, 0); +} + + +/************************************************************************* +// end callback +**************************************************************************/ + +void UiPacker::endCallback() +{ + const bool done = (s->total_passes <= 0 || s->pass >= s->total_passes); + + if (s->mode == M_CB_TERM) + { + if (done) + printClearLine(stdout); + else + printSetNl(2); + } + + // restore screen +#if defined(UI_USE_SCREEN) + if (s->mode == M_CB_SCREEN) + { +#if 0 + if (s->scroll_up) + s->screen->scrollDown(screen,s->scroll_up); + else + s->screen->clearLine(s->screen,s->s_cy+1); + s->screen->setCursor(s->screen,s->s_cx,s->s_cy); +#else + assert(s->s_cx == 0 && s->b_cx == 0); + s->screen->clearLine(s->screen,s->b_cy); + s->screen->setCursor(s->screen,s->b_cx,s->b_cy-1); +#endif + s->screen->setFg(s->screen,s->s_fg); + s->screen->setBg(s->screen,s->s_bg); + if (s->cursor_shape > 0) + s->screen->setCursorShape(s->screen,s->cursor_shape); + } +#endif /* UI_USE_SCREEN */ + + clear_cb(); +#if 0 + printf("callback: pass %d, step %6d, updates %6d\n", + s->pass, s->step, s->progress_updates); +#endif +} + + +/************************************************************************* +// the callback +**************************************************************************/ + +void __UPX_ENTRY UiPacker::callback(upx_uint is, upx_uint os, int state, void * user) +{ + //printf("%6d %6d %d\n", is, os, state); + if (state != -1 && state != 3) return; + if (user) + { + UiPacker *uip = reinterpret_cast(user); + uip->doCallback(is,os); + } +} + + +void UiPacker::doCallback(unsigned isize, unsigned osize) +{ + static const char spinner[] = "|\\-/"; + + if (s->is == 0 || isize > s->is) + return; + // check if we should update the display + if (s->step > 0 && isize > 0 && isize < s->is) + { + if (isize < s->next_update) + return; + s->next_update += s->step; + } + +#if 0 + printf("%6d %6d %6d %6d\n", isize, osize, s->step, s->next_update); + return; +#endif + + // compute progress position + int pos = -1; + if (isize > 0) + pos = get_ratio(isize,s->is) * s->bar_len / 10000; + if (pos < s->pos) + return; + if (pos < 0 && pos == s->pos) + return; + + // fill the progress bar + char *m = &s->msg_buf[s->bar_pos]; + *m++ = '['; + for (int i = 0; i < s->bar_len; i++) + { +#ifdef __MSDOS__ + //*m++ = i <= pos ? '\xdb' : '.'; + //*m++ = i <= pos ? '\xb0' : '\x07'; + //*m++ = i <= pos ? '\xb1' : '\x07'; + *m++ = i <= pos ? '\xfe' : '\xf9'; +#else + *m++ = i <= pos ? '*' : '.'; +#endif + } + *m++ = ']'; + + // compute current compression ratio + unsigned ratio = 100*100; + if (osize > 0) + ratio = get_ratio(osize,isize); + + sprintf(m," %3d.%1d%% %c ", + ratio / 100, (ratio % 100) / 10, + spinner[s->counter]); + assert((int)strlen(s->msg_buf) < 1 + 80); + + s->pos = pos; + s->counter = (s->counter + 1) & 3; + s->progress_updates++; + + if (s->mode == M_CB_TERM) + { + const char *msg = &s->msg_buf[0]; + int fg = con_fg(stdout,FG_CYAN); + con_fprintf(stdout,"%s",msg); // avoid backslash interpretation + (void) con_fg(stdout,fg); + fflush(stdout); + printSetNl(1); + return; + } + +#if defined(UI_USE_SCREEN) + if (s->mode == M_CB_SCREEN) + { + const char *msg = &s->msg_buf[1]; +#if 0 + s->screen->putString(s->screen,msg,s->b_cx,s->b_cy); +#else + // FIXME: this doesn't honor `--mono' etc. + int attr = FG_CYAN | s->s_bg; + s->screen->putStringAttr(s->screen,msg,attr,s->b_cx,s->b_cy); +#endif + s->screen->refresh(s->screen); + } +#endif /* UI_USE_SCREEN */ +} + + +/************************************************************************* +// pack +**************************************************************************/ + +void UiPacker::uiPackStart(const OutputFile *fo) +{ + total_files++; + UNUSED(fo); +} + + +void UiPacker::uiPackEnd(const OutputFile *fo) +{ + uiUpdate(fo->getBytesWritten()); + + if (s->mode == M_QUIET) + return; + if (s->mode == M_MSG) + { + // We must put this here and not in endCallback() as we may + // have multiple passes. + printClearLine(stdout); + } + + con_fprintf(stdout,"%s", + mkline(p->ph.u_file_size, fo->getBytesWritten(), + p->ph.u_len, p->ph.c_len, + p->getName(), fn_basename(p->fi->getName()))); + printSetNl(0); +} + + +void UiPacker::uiPackTotal() +{ + uiListTotal(); + uiFooter("Packed"); +} + + +/************************************************************************* +// unpack +**************************************************************************/ + +void UiPacker::uiUnpackStart(const OutputFile *fo) +{ + total_files++; + UNUSED(fo); +} + + +void UiPacker::uiUnpackEnd(const OutputFile *fo) +{ + uiList(fo->getBytesWritten()); + uiUpdate(-1, fo->getBytesWritten()); +} + + +void UiPacker::uiUnpackTotal() +{ + uiListTotal(); + uiFooter("Unpacked"); +} + + +/************************************************************************* +// list +**************************************************************************/ + +void UiPacker::uiListStart() +{ + total_files++; +} + + +void UiPacker::uiList(long fu_len) +{ + if (fu_len < 0) + fu_len = p->ph.u_file_size; + con_fprintf(stdout,"%s", + mkline(fu_len, p->file_size, + p->ph.u_len, p->ph.c_len, + p->getName(), p->fi->getName())); +} + + +void UiPacker::uiListEnd() +{ + uiUpdate(); +} + + +void UiPacker::uiListTotal() +{ + if (opt->verbose >= 1 && total_files >= 2) + { + char name[32]; + upx_snprintf(name,sizeof(name),"[ %ld file%s ]", total_files_done, total_files_done == 1 ? "" : "s"); + con_fprintf(stdout,"%s%s", + header_line2, + mkline(total_fu_len, total_fc_len, + total_u_len, total_c_len, + "", name)); + } +} + + +/************************************************************************* +// test +**************************************************************************/ + +void UiPacker::uiTestStart() +{ + total_files++; + + if (opt->verbose >= 1) + { + con_fprintf(stdout,"testing %s ", p->fi->getName()); + fflush(stdout); + printSetNl(1); + } +} + + +void UiPacker::uiTestEnd() +{ + if (opt->verbose >= 1) + { + con_fprintf(stdout,"[OK]\n"); + fflush(stdout); + printSetNl(0); + } + uiUpdate(); +} + + +void UiPacker::uiTestTotal() +{ + uiFooter("Tested"); +} + + +/************************************************************************* +// info +**************************************************************************/ + +bool UiPacker::uiFileInfoStart() +{ + total_files++; + + int fg = con_fg(stdout,FG_CYAN); + con_fprintf(stdout,"%s [%s]\n", p->fi->getName(), p->getName()); + fg = con_fg(stdout,fg); + UNUSED(fg); + if (p->ph.c_len > 0) + { + con_fprintf(stdout," %8ld bytes", (long)p->file_size); + con_fprintf(stdout,", compressed by UPX %d, method %d, level %d, filter 0x%02x/0x%02x\n", + p->ph.version, p->ph.method, p->ph.level, p->ph.filter, p->ph.filter_cto); + return false; + } + else + { + con_fprintf(stdout," %8ld bytes", (long)p->file_size); + con_fprintf(stdout,", not compressed by UPX\n"); + return true; + } +} + +void UiPacker::uiFileInfoEnd() +{ + uiUpdate(); +} + + +void UiPacker::uiFileInfoTotal() +{ +} + + +/************************************************************************* +// util +**************************************************************************/ + +void UiPacker::uiHeader() +{ + static bool done = false; + if (done) + return; + done = true; + if (opt->cmd == CMD_TEST || opt->cmd == CMD_FILEINFO) + return; + if (opt->verbose >= 1) + { + con_fprintf(stdout,"%s%s", header_line1, header_line2); + } +} + + +void UiPacker::uiFooter(const char *t) +{ + static bool done = false; + if (done) + return; + done = true; + if (opt->verbose >= 1) + { + long n1 = total_files; + long n2 = total_files_done; + long n3 = total_files - total_files_done; + if (n3 == 0) + con_fprintf(stdout,"\n%s %ld file%s.\n", + t, n1, n1 == 1 ? "" : "s"); + else + con_fprintf(stdout,"\n%s %ld file%s: %ld ok, %ld error%s.\n", + t, n1, n1 == 1 ? "" : "s", n2, n3, n3 == 1 ? "" : "s"); + } +} + + +void UiPacker::uiUpdate(long fc_len, long fu_len) +{ + update_fc_len = (fc_len >= 0) ? fc_len : p->file_size; + update_fu_len = (fu_len >= 0) ? fu_len : p->ph.u_file_size; + update_c_len = p->ph.c_len; + update_u_len = p->ph.u_len; +} + + +void UiPacker::uiConfirmUpdate() +{ + total_files_done++; + total_fc_len += update_fc_len; + total_fu_len += update_fu_len; + total_c_len += update_c_len; + total_u_len += update_u_len; +} + + +/* +vi:ts=4:et +*/ + diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000000..ab2303c6ff --- /dev/null +++ b/src/ui.h @@ -0,0 +1,116 @@ +/* ui.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_UI_H +#define __UPX_UI_H + +class InputFile; +class OutputFile; +class Packer; +class UiPacker; + + +/************************************************************************* +// +**************************************************************************/ + +class UiPacker +{ +public: + UiPacker(const Packer *p); +public: + virtual ~UiPacker(); + + static void uiConfirmUpdate(); + static void uiPackTotal(); + static void uiUnpackTotal(); + static void uiListTotal(); + static void uiTestTotal(); + static void uiFileInfoTotal(); + +public: + virtual void uiPackStart(const OutputFile *fo); + virtual void uiPackEnd(const OutputFile *fo); + virtual void uiUnpackStart(const OutputFile *fo); + virtual void uiUnpackEnd(const OutputFile *fo); + virtual void uiListStart(); + virtual void uiList(long fu=-1); + virtual void uiListEnd(); + virtual void uiTestStart(); + virtual void uiTestEnd(); + virtual bool uiFileInfoStart(); + virtual void uiFileInfoEnd(); + + // callback + typedef upx_progress_callback_t cb_t; + virtual void startCallback(unsigned is, unsigned step, + int pass, int total_passes); + virtual void firstCallback(); + virtual void endCallback(); + virtual cb_t *getCallback() { return &cb; } +protected: + static void __UPX_ENTRY callback(upx_uint is, upx_uint os, int, void *); + virtual void doCallback(unsigned is, unsigned os); + +protected: + virtual void uiUpdate(long fc=-1, long fu=-1); + +public: + static void uiHeader(); + static void uiFooter(const char *n); + +protected: + const Packer *p; + + // callback + cb_t cb; + + // internal state + struct State; + struct State *s; + + // totals + static long total_files; + static long total_files_done; + static long total_c_len; + static long total_u_len; + static long total_fc_len; + static long total_fu_len; + static long update_c_len; + static long update_u_len; + static long update_fc_len; + static long update_fu_len; +}; + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/unupx.h b/src/unupx.h new file mode 100644 index 0000000000..a69271d914 --- /dev/null +++ b/src/unupx.h @@ -0,0 +1,100 @@ +/* unupx.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_UNUPX_H +#define __UPX_UNUPX_H + + +/************************************************************************* +// integral and pointer types +**************************************************************************/ + +#ifndef upx_byte +typedef int upx_int; +typedef unsigned upx_uint; +typedef int upx_int32; +typedef unsigned upx_uint32; + +#define upx_byte unsigned char +#define upx_bytep upx_byte * +#define upx_voidp void * +#define upx_uintp upx_uint * +#endif + + +/************************************************************************* +// calling conventions +**************************************************************************/ + +#ifndef __UPX_CDECL +#define __UPX_CDECL +#endif +#ifndef __UPX_ENTRY +#define __UPX_ENTRY __UPX_CDECL +#endif +#define UPX_EXTERN(x) extern "C" x __UPX_ENTRY +#define UPX_EXTERN_CDECL(x) extern "C" x __UPX_CDECL + + +/************************************************************************* +// constants +**************************************************************************/ + +/* Executable formats. Note: big endian types are >= 128 */ +#define UPX_F_DOS_COM 1 +#define UPX_F_DOS_SYS 2 +#define UPX_F_DOS_EXE 3 +#define UPX_F_DJGPP2_COFF 4 +#define UPX_F_WC_LE 5 +#define UPX_F_VXD_LE 6 +#define UPX_F_DOS_EXEH 7 /* OBSOLETE */ +#define UPX_F_TMT_ADAM 8 +#define UPX_F_W32_PE 9 +#define UPX_F_LINUX_i386 10 +#define UPX_F_BVMLINUX_i386 11 +#define UPX_F_LINUX_ELF_i386 12 +#define UPX_F_LINUX_SEP_i386 13 +#define UPX_F_LINUX_SH_i386 14 +#define UPX_F_ATARI_TOS 129 +#define UPX_F_SOLARIS_SPARC 130 + + +#define UPX_MAGIC_LE32 0x21585055 /* "UPX!" */ + + +/************************************************************************* +// prototypes +**************************************************************************/ + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000000..bf630ddc7f --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,593 @@ +/* util.cpp -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "util.h" + + +/************************************************************************* +// +**************************************************************************/ + +int be16_compare(const void *e1, const void *e2) +{ + const unsigned d1 = get_be16(e1); + const unsigned d2 = get_be16(e2); + return (d1 < d2) ? -1 : ((d1 > d2) ? 1 : 0); +} + +int be32_compare(const void *e1, const void *e2) +{ + const unsigned d1 = get_be32(e1); + const unsigned d2 = get_be32(e2); + return (d1 < d2) ? -1 : ((d1 > d2) ? 1 : 0); +} + +int le16_compare(const void *e1, const void *e2) +{ + const unsigned d1 = get_le16(e1); + const unsigned d2 = get_le16(e2); + return (d1 < d2) ? -1 : ((d1 > d2) ? 1 : 0); +} + +int le32_compare(const void *e1, const void *e2) +{ + const unsigned d1 = get_le32(e1); + const unsigned d2 = get_le32(e2); + return (d1 < d2) ? -1 : ((d1 > d2) ? 1 : 0); +} + + +/************************************************************************* +// +**************************************************************************/ + +upx_bytep find(const void *b, int blen, const void *what, int wlen) +{ + int i; + const upx_bytep base = (const upx_bytep) b; + unsigned char firstc = * (const upx_bytep) what; + + for (i = 0; i <= blen - wlen; i++, base++) + if (*base == firstc && memcmp(base,what,wlen) == 0) + return const_cast(base); + + return NULL; +} + + +upx_bytep find_be16(const void *b, int blen, unsigned what) +{ + unsigned char w[2]; + set_be16(w,what); + return find(b,blen,w,2); +} + + +upx_bytep find_be32(const void *b, int blen, unsigned what) +{ + unsigned char w[4]; + set_be32(w,what); + return find(b,blen,w,4); +} + + +upx_bytep find_le16(const void *b, int blen, unsigned what) +{ + unsigned char w[2]; + set_le16(w,what); + return find(b,blen,w,2); +} + + +upx_bytep find_le32(const void *b, int blen, unsigned what) +{ + unsigned char w[4]; + set_le32(w,what); + return find(b,blen,w,4); +} + + +/************************************************************************* +// string util +**************************************************************************/ + +int upx_snprintf(char *str, long n, const char *format, ...) +{ + int r = -1; + if (n > 0) + { + va_list args; + va_start(args,format); + r = upx_vsnprintf(str,n,format,args); + va_end(args); + } + assert(r >= 0 && r < n); // UPX assertion + return r; +} + + +int upx_vsnprintf(char *str, long n, const char *format, va_list ap) +{ + int r = -1; + if (n > 0) + { +#if defined(HAVE_VSNPRINTF) + r = vsnprintf(str,(size_t)n,format,ap); +#else + r = vsprintf(str,format,ap); +#endif + // UPX extension: make sure the string is '\0' terminated in any case + str[n-1] = 0; + } + assert(r >= 0 && r < n); // UPX assertion + return r; +} + + +/************************************************************************* +// ctype util +**************************************************************************/ + +#if 0 +bool upx_isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +bool upx_islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +bool upx_isspace(int c) +{ + // according to "C" and "POSIX" locales + return strchr(" \f\n\r\t\v", c) != NULL; +} + +int upx_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + return c; +} +#endif + + +/************************************************************************* +// filename util +**************************************************************************/ + +static const char dir_sep[] = DIR_SEP; + +#define fn_is_sep(c) (strchr(dir_sep,c) != NULL) + +#if defined(DOSISH) +#define fn_is_drive(n) (n[0] && n[1] == ':') +#define fn_skip_drive(n) (fn_is_drive(n) ? (n) + 2 : (n)) +#else +#define fn_is_drive(n) (0) +#define fn_skip_drive(n) (n) +#endif + + +char *fn_basename(const char *name) +{ + const char *n, *nn; + + name = fn_skip_drive(name); + for (nn = n = name; *nn; nn++) + if (fn_is_sep(*nn)) + n = nn + 1; + return const_cast(n); +} + + +bool fn_has_ext(const char *name, const char *ext) +{ + const char *n, *e; + + name = fn_basename(name); + for (n = e = name; *n; n++) + if (*n == '.') + e = n; + return (fn_strcmp(ext,e+1) == 0); +} + + +void fn_addslash(char *name, bool slash) +{ + char *p; + + name = fn_skip_drive(name); + p = name + strlen(name); + while (p > name && fn_is_sep(p[-1])) + *p-- = 0; + if (p > name) + { + if (slash) + *p++ = dir_sep[0]; + *p = 0; + } +} + + +char *fn_strlwr(char *n) +{ + char *p; + for (p = n; *p; p++) + *p = (char) fn_tolower(*p); + return n; +} + + +int fn_strcmp(const char *n1, const char *n2) +{ + for (;;) + { + if (*n1 != *n2) + { + int c = fn_tolower(*n1) - fn_tolower(*n2); + if (c) + return c; + } + if (*n1 == 0) + return 0; + n1++; n2++; + } +} + + +bool fn_is_same_file(const char *n1, const char *n2) +{ + /* very simple... */ + if (fn_strcmp(n1,n2) == 0) + return 1; + return 0; +} + + +/************************************************************************* +// time util +**************************************************************************/ + +#if 0 // not used + +#if defined(HAVE_LOCALTIME) +void tm2str(char *s, const struct tm *tmp) +{ + sprintf(s,"%04d-%02d-%02d %02d:%02d:%02d", + (int) tmp->tm_year + 1900, (int) tmp->tm_mon + 1, + (int) tmp->tm_mday, + (int) tmp->tm_hour, (int) tmp->tm_min, (int) tmp->tm_sec); +} +#endif + + +void time2str(char *s, const time_t *t) +{ +#if defined(HAVE_LOCALTIME) + tm2str(s,localtime(t)); +#elif defined(HAVE_CTIME) + const char *p = ctime(t); + memset(s, ' ', 16); + memcpy(s + 2, p + 4, 6); + memcpy(s + 11, p + 11, 5); + s[16] = 0; +#else + s[0] = 0; +#endif +} + +#endif + + +/************************************************************************* +// misc. +**************************************************************************/ + +char *center_string(const char *name, size_t s) +{ + static char buf[256+1]; + size_t l = strlen(name); + assert(l <= s && l < sizeof(buf)); + memset(buf,' ',s); + memcpy(buf+(s-l)/2,name,l); + buf[s] = 0; + return buf; +} + + +bool file_exists(const char *name) +{ + int fd; + + fd = open(name,O_RDONLY); + if (fd < 0) + return 0; + (void) close(fd); + return 1; +} + + +bool maketempname(char *ofilename, const char *ifilename, + const char *ext, bool force) +{ + char *ofext = NULL, *ofname; + int ofile; + + strcpy(ofilename, ifilename); + for (ofname = fn_basename(ofilename); *ofname; ofname++) + { + if (*ofname == '.') + ofext = ofname; + } + if (ofext == NULL) + ofext = ofilename + strlen(ofilename); + strcpy(ofext, ext); + if (!force) + return true; + if (file_exists(ofilename)) + for (ofile = 0; ofile < 999; ofile++) + { + sprintf(ofext, ".%03d", ofile); + if (!file_exists(ofilename)) + return true; + } + return false; +} + + +void makebakname(char *ofilename, const char *ifilename) +{ + char *ofext = NULL, *ofname; + + strcpy(ofilename, ifilename); + for (ofname = fn_basename(ofilename); *ofname; ofname++) + { + if (*ofname == '.') + ofext = ofname; + } + if (ofext == NULL) + strcat(ofilename, ".~"); + else if (strlen(ofext) < 1 + 3) + strcat(ofilename, "~"); + else + ofext[strlen(ofext)-1] = '~'; +} + + +/* test if fd is connected to a file or a pipe */ +bool isafile(int fd) +{ + if (isatty(fd)) + return 0; +#if defined(HAVE_FSTAT) + { + struct stat st; + if (fstat(fd, &st) != 0) + return 0; + /* fprintf(stderr,"fstat(%d): %o\n", fd, st.st_mode); */ + if (S_ISDIR(st.st_mode)) + return 0; + } +#endif + return 1; +} + + +/************************************************************************* +// +**************************************************************************/ + +unsigned get_ratio (unsigned long packedsize, unsigned long size, + unsigned long scale) +{ + unsigned long n1, n2; + + n1 = 100 * scale; n2 = 1; + if (size <= 0) + return (unsigned) n1; + while (n1 > 1 && packedsize > (ULONG_MAX / n1)) + { + n1 /= 10; + n2 *= 10; + } + return (unsigned) ((packedsize * n1) / (size / n2)) + 5; +} + + +/************************************************************************* +// memory debugging +**************************************************************************/ + +#if defined(WITH_GC) +extern "C" { + +#undef malloc +#undef realloc +#undef free +#ifndef __malloc_ptr_t +# define __malloc_ptr_t __ptr_t +#endif + +__malloc_ptr_t malloc(size_t size) +{ + return GC_MALLOC(size); +} + +__malloc_ptr_t realloc(__malloc_ptr_t ptr, size_t size) +{ + return GC_REALLOC(ptr, size); +} + +void free(__malloc_ptr_t ptr) +{ + GC_FREE(ptr); +} + +}; // extern "C" +#endif + + +/************************************************************************* +// Don't link these functions from libc ==> save xxx bytes +**************************************************************************/ + +extern "C" { + +#if defined(__DJGPP__) +int _is_executable(const char *, int , const char *) +{ + return 0; +} + +//FIXME: something wants to link in ctime.o +time_t mktime(struct tm *) +{ + return 0; +} + +time_t time(time_t *) +{ + return 0; +} +#endif /* __DJGPP__ */ + + +// These space savings are only useful when building a statically +// linked version, so this is disabled for now. +#if 0 && defined(__linux__) && defined(__GLIBC__) + +#if 1 +// We don't need floating point support in printf(). +// See glibc/stdio-common/* +int __printf_fp(void) +{ + assert(0); + return -1; +} +int __printf_fphex(void) +{ + assert(0); + return -1; +} +#endif + + +#if 1 +// We don't need multibyte character support. +// See and glibc/xxx/vfprintf.c +int mblen(const char *, size_t) +{ + assert(0); + return -1; +} +int mbtowc(...) +{ + assert(0); + return -1; +} +int mbrtowc(...) +{ + assert(0); + return -1; +} +// glibc internals +size_t __ctype_get_mb_cur_max(void) +{ + assert(0); + return 1; +} +int __mbrlen(...) +{ + assert(0); + return -1; +} +int __mbrtowc(...) +{ + assert(0); + return -1; +} +int __wcrtomb(...) +{ + assert(0); + return -1; +} +int __wcsrtombs(...) +{ + assert(0); + return -1; +} +// see strtol.c +int __btowc(...) +{ + assert(0); + return -1; +} +#endif + + +#if 1 +// We don't need *scanf. +// See iovsscanf.c +// (libc6 2.1.1-10: this function pulls in ~80kB code !) +int _IO_vsscanf(void) +{ + assert(0); + return -1; +} +#endif + + +#if 0 +// FIXME - also should get rid of this intl stuff +// see glibc/intl/* +static const char _nl_default_default_domain[] = "messages"; +static const char *_nl_current_default_domain = _nl_default_default_domain; +static const char _nl_default_dirname[] = ""; +char *bindtextdomain(const char *, const char *) +{ + return NULL; +} +char *textdomain(const char *) +{ + return (char *) _nl_current_default_domain; +} +#endif + +#endif /* __linux__ && __GLIBC__ */ + + +}; // extern "C" + + +/* +vi:ts=4:et +*/ + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000000..194e29435d --- /dev/null +++ b/src/util.h @@ -0,0 +1,88 @@ +/* util.h -- + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#ifndef __UPX_UTIL_H +#define __UPX_UTIL_H + + +/************************************************************************* +// misc. support functions +**************************************************************************/ + +int upx_snprintf(char *str, long n, const char *format, ...); +int upx_vsnprintf(char *str, long n, const char *format, va_list ap); + +char *fn_basename(const char *name); +int fn_strcmp(const char *n1, const char *n2); +char *fn_strlwr(char *n); +bool fn_has_ext(const char *name, const char *ext); + +bool file_exists(const char *name); +bool maketempname(char *ofilename, const char *ifilename, + const char *ext, bool force); +void makebakname(char *ofilename, const char *ifilename); +bool isafile(int fd); + +unsigned get_ratio(unsigned long packedsize, unsigned long size, + unsigned long scale=100); +char *center_string(const char *name, size_t s); + + +upx_bytep find(const void * b, int blen, const void * what, int wlen); +upx_bytep find_be16(const void * b, int blen, unsigned what); +upx_bytep find_be32(const void * b, int blen, unsigned what); +upx_bytep find_le16(const void * b, int blen, unsigned what); +upx_bytep find_le32(const void * b, int blen, unsigned what); + + +/************************************************************************* +// +**************************************************************************/ + +#if 0 +bool upx_isdigit(int c); +bool upx_islower(int c); +bool upx_isspace(int c); +int upx_tolower(int c); +#undef isdigit +#undef islower +#undef isspace +#undef tolower +#define isdigit upx_isdigit +#define islower upx_islower +#define isspace upx_isspace +#define tolower upx_tolower +#endif + + +#endif /* already included */ + + +/* +vi:ts=4:et +*/ + diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000000..eac3be8b73 --- /dev/null +++ b/src/version.h @@ -0,0 +1,2 @@ +#define UPX_VERSION_STRING "1.09.2" +#define UPX_VERSION_DATE "May 13th 2000" diff --git a/src/work.cpp b/src/work.cpp new file mode 100644 index 0000000000..d38c8d2af2 --- /dev/null +++ b/src/work.cpp @@ -0,0 +1,291 @@ +/* work.cpp -- main driver + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2000 Laszlo Molnar + + UPX and the UCL library are free software; you can redistribute them + and/or modify them 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; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + */ + + +#include "conf.h" +#include "file.h" +#include "packmast.h" +#include "packer.h" +#include "ui.h" + + +#if defined(__DJGPP__) +# define USE_FTIME +#elif defined(__MFX_WIN32) && defined(_MSC_VER) +# define USE__FUTIME +#elif defined(HAVE_UTIME) +# define USE_UTIME +#endif + +#if !defined(SH_DENYRW) +# define SH_DENYRW (-1) +#endif +#if !defined(SH_DENYWR) +# define SH_DENYWR (-1) +#endif + + +/************************************************************************* +// process one file +**************************************************************************/ + +void do_one_file(const char *iname, char *oname) +{ + struct stat st; + memset(&st, 0, sizeof(st)); +#if defined(HAVE_LSTAT) + int r = lstat(iname,&st); +#else + int r = stat(iname,&st); +#endif + bool need_chmod = true; + + if (r == -1) + throw FileNotFoundException(iname); + if (!S_ISREG(st.st_mode)) + throwIOException("not a regular file -- skipped"); +#if defined(__unix__) + // no special bits may be set + if ((st.st_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0) + throwIOException("file has special permissions -- skipped"); +#endif + if (st.st_size <= 0) + throwIOException("empty file -- skipped"); + + InputFile fi; + fi.st = st; + fi.sopen(iname, O_RDONLY | O_BINARY, SH_DENYWR); + +#if defined(USE_FTIME) + struct ftime fit; + getftime(fi.getFd(),&fit); +#endif + + // open output file + OutputFile fo; + if (opt->cmd == CMD_COMPRESS || opt->cmd == CMD_DECOMPRESS) + { + if (opt->to_stdout) + { + if (!fo.openStdout(O_BINARY, opt->force ? true : false)) + throwIOException("data not written to a terminal; Use `-f' to force."); + } + else + { + char tname[PATH_MAX+1]; + if (opt->output_name) + strcpy(tname,opt->output_name); + else + maketempname(tname,iname,".upx",1); + if (opt->force >= 2) + { +#if defined(HAVE_CHMOD) + (void) ::chmod(tname, 0777); +#endif + (void) ::unlink(tname); + } + int flags = O_CREAT | O_WRONLY | O_BINARY; + if (opt->force) + flags |= O_TRUNC; + else + flags |= O_EXCL; + int shmode = SH_DENYWR; +#if defined(__MFX_DOS) || defined(__MFX_WIN32) + // we can avoid the chmod() call below + int omode = st.st_mode; + fo.sopen(tname,flags,shmode,omode); + need_chmod = false; +#else + // cannot rely on open() because of umask + //int omode = st.st_mode | 0600; + int omode = 0600; + fo.sopen(tname,flags,shmode,omode); +#endif + // open succeeded - set oname + strcpy(oname,tname); + } + } + + // handle command + PackMaster pm(&fi, opt); + if (opt->cmd == CMD_COMPRESS) + pm.pack(&fo); + else if (opt->cmd == CMD_DECOMPRESS) + pm.unpack(&fo); + else if (opt->cmd == CMD_TEST) + pm.test(); + else if (opt->cmd == CMD_LIST) + pm.list(); + else if (opt->cmd == CMD_FILEINFO) + pm.fileInfo(); + else + throwInternalError("invalid command"); + + // copy time stamp + if (oname[0] && fo.isOpen()) + { +#if defined(USE_FTIME) + setftime(fo.getFd(),&fit); +#elif defined(USE__FUTIME) + struct _utimbuf u; + u.actime = st.st_atime; + u.modtime = st.st_mtime; + (void) _futime(fo.getFd(),&u); +#endif + } + + // close files + fo.closex(); + fi.closex(); + + // rename or delete files + if (oname[0] && !opt->output_name) + { + // FIXME: .exe or .cof etc. + if (!opt->backup) + { +#if defined(HAVE_CHMOD) + (void) ::chmod(iname, 0777); +#endif + File::unlink(iname); + } + else + { + // make backup + char bakname[PATH_MAX+1]; + makebakname(bakname,iname); + File::rename(iname,bakname); + } + File::rename(oname,iname); + } + + // copy file attributes + if (oname[0]) + { + oname[0] = 0; + const char *name = opt->output_name ? opt->output_name : iname; +#if defined(USE_UTIME) + // copy time stamp + struct utimbuf u; + u.actime = st.st_atime; + u.modtime = st.st_mtime; + (void) ::utime(name,&u); +#endif +#if defined(HAVE_CHMOD) + // copy permissions + if (need_chmod) + (void) ::chmod(name, st.st_mode); +#endif +#if defined(HAVE_CHOWN) + // copy the ownership + (void) ::chown(name, st.st_uid, st.st_gid); +#endif + } + + UiPacker::uiConfirmUpdate(); +} + + +/************************************************************************* +// process all files from the commandline +**************************************************************************/ + +void do_files(int i, int argc, char *argv[]) +{ + if (opt->verbose >= 1) + { + show_head(); + UiPacker::uiHeader(); + } + + for ( ; i < argc; i++) + { +#if defined(WITH_MSS) + //MSS_ENABLE_LOG_OUTPUT; + //MSS_ENTER_SCOPE; +#endif + infoHeader(); + + const char *iname = argv[i]; + char oname[PATH_MAX+1]; + oname[0] = 0; + + try { + do_one_file(iname,oname); + } catch (const Exception &e) { + if (opt->verbose >= 2 || (opt->verbose >= 1 && !e.isWarning())) + printErr(iname,&e); + if (oname[0]) + (void) ::unlink(oname); + } catch (const Error &e) { + printErr(iname,&e); + if (oname[0]) + (void) ::unlink(oname); + e_exit(EXIT_ERROR); + //throw; + } catch (const exception &e) { + printErr(iname,"unhandled exception: %s\n", prettyName(e.what())); + if (oname[0]) + (void) ::unlink(oname); + e_exit(EXIT_ERROR); + //throw; + } catch (const exception *e) { + printErr(iname,"unhandled exception: %s\n", prettyName(e->what())); + if (oname[0]) + (void) ::unlink(oname); + e_exit(EXIT_ERROR); + //throw; + } catch (...) { + printErr(iname,"internal error: unhandled exception !\n"); + if (oname[0]) + (void) ::unlink(oname); + e_exit(EXIT_ERROR); + //throw; + } + +#if defined(WITH_MSS) + //MSS_LEAVE_SCOPE; + MSS_CHECK_ALL_BLOCKS; +#endif + } + + if (opt->cmd == CMD_COMPRESS) + UiPacker::uiPackTotal(); + else if (opt->cmd == CMD_DECOMPRESS) + UiPacker::uiUnpackTotal(); + else if (opt->cmd == CMD_LIST) + UiPacker::uiListTotal(); + else if (opt->cmd == CMD_TEST) + UiPacker::uiTestTotal(); + else if (opt->cmd == CMD_FILEINFO) + UiPacker::uiFileInfoTotal(); +} + + +/* +vi:ts=4:et +*/ +