From 1a9128a473c4efa5006cc501d0f418e366b5f016 Mon Sep 17 00:00:00 2001 From: convert-repo Date: Fri, 19 May 2000 16:04:55 +0000 Subject: [PATCH] Initial commit (from git) --- BUGS | 51 + COPYING | 340 +++++ LICENSE | 138 ++ LOADER.TXT | 89 ++ Makefile | 21 + NEWS | 197 +++ PROJECTS | 92 ++ README | 143 ++ README.SRC | 153 +++ THANKS | 55 + TODO | 186 +++ doc/Makefile | 63 + doc/upx.pod | 612 +++++++++ src/Makefile | 476 +++++++ src/bele.h | 261 ++++ src/c_file.cpp | 95 ++ src/c_init.cpp | 165 +++ src/c_none.cpp | 81 ++ src/c_screen.cpp | 286 ++++ src/compress.cpp | 269 ++++ src/conf.h | 613 +++++++++ src/config_h/linux.h | 240 ++++ src/console.h | 186 +++ src/except.cpp | 138 ++ src/except.h | 226 ++++ src/fcto_ml.ch | 105 ++ src/fcto_ml2.ch | 191 +++ src/file.cpp | 379 ++++++ src/file.h | 174 +++ src/filter.cpp | 206 +++ src/filter.h | 129 ++ src/filteri.cpp | 453 +++++++ src/help.cpp | 278 ++++ src/lefile.cpp | 349 +++++ src/lefile.h | 223 ++++ src/linker.cpp | 204 +++ src/linker.h | 65 + src/main.cpp | 1134 ++++++++++++++++ src/mem.cpp | 115 ++ src/mem.h | 82 ++ src/merge.sh | 10 + src/msg.cpp | 232 ++++ src/mygetopt.cpp | 696 ++++++++++ src/mygetopt.h | 102 ++ src/p_com.cpp | 268 ++++ src/p_com.h | 69 + src/p_djgpp2.cpp | 433 ++++++ src/p_djgpp2.h | 107 ++ src/p_elf.h | 83 ++ src/p_exe.cpp | 658 +++++++++ src/p_exe.h | 109 ++ src/p_lx_elf.cpp | 504 +++++++ src/p_lx_elf.h | 84 ++ src/p_lx_sep.cpp | 85 ++ src/p_lx_sep.h | 55 + src/p_lx_sh.cpp | 223 ++++ src/p_lx_sh.h | 73 + src/p_sys.cpp | 132 ++ src/p_sys.h | 62 + src/p_tmt.cpp | 333 +++++ src/p_tmt.h | 76 ++ src/p_tos.cpp | 544 ++++++++ src/p_tos.h | 85 ++ src/p_unix.cpp | 460 +++++++ src/p_unix.h | 217 +++ src/p_vmlinux.cpp | 154 +++ src/p_vxd.h | 72 + src/p_w32pe.cpp | 2206 +++++++++++++++++++++++++++++++ src/p_w32pe.h | 229 ++++ src/p_wcle.cpp | 827 ++++++++++++ src/p_wcle.h | 92 ++ src/packer.cpp | 1193 +++++++++++++++++ src/packer.h | 244 ++++ src/packhead.cpp | 256 ++++ src/packmast.cpp | 258 ++++ src/packmast.h | 68 + src/s_djgpp2.cpp | 546 ++++++++ src/s_object.cpp | 100 ++ src/s_vcsa.cpp | 601 +++++++++ src/s_win32.cpp | 489 +++++++ src/screen.h | 111 ++ src/stdcxx.cpp | 49 + src/stdcxx.h | 83 ++ src/stub/Makefile | 322 +++++ src/stub/header.ash | 60 + src/stub/ident.ash | 37 + src/stub/ident_n.ash | 39 + src/stub/ident_s.ash | 35 + src/stub/l_com.asm | 95 ++ src/stub/l_djgpp2.asm | 93 ++ src/stub/l_exe.asm | 177 +++ src/stub/l_lx_elf.c | 385 ++++++ src/stub/l_lx_elf86.asm | 281 ++++ src/stub/l_lx_elf86.lds | 17 + src/stub/l_lx_exec.c | 495 +++++++ src/stub/l_lx_exec86.asm | 148 +++ src/stub/l_lx_sep.c | 449 +++++++ src/stub/l_lx_sep86.asm | 232 ++++ src/stub/l_lx_sep86.lds | 15 + src/stub/l_lx_sh.c | 357 +++++ src/stub/l_lx_sh86.asm | 291 ++++ src/stub/l_lx_sh86.lds | 17 + src/stub/l_sys.asm | 124 ++ src/stub/l_tmt.asm | 105 ++ src/stub/l_tos.s | 349 +++++ src/stub/l_w32pe.asm | 224 ++++ src/stub/l_wcle.asm | 139 ++ src/stub/linux.hh | 276 ++++ src/stub/macros.ash | 215 +++ src/stub/merge.sh | 10 + src/stub/scripts/app.pl | 120 ++ src/stub/scripts/bin2h.pl | 100 ++ src/stub/scripts/brandelf.pl | 47 + src/stub/scripts/o2bin.pl | 75 ++ src/stub/scripts/setfold.pl | 45 + src/stub/scripts/stripelf.pl | 77 ++ src/stub/scripts/version.pl | 40 + src/stub/stub.asm | 984 ++++++++++++++ src/stub/util/sstrip/Makefile | 7 + src/stub/util/sstrip/README | 40 + src/stub/util/sstrip/README.1ST | 70 + src/stub/util/sstrip/sstrip.c | 218 +++ src/tailor.h | 192 +++ src/ui.cpp | 659 +++++++++ src/ui.h | 116 ++ src/unupx.h | 100 ++ src/util.cpp | 593 +++++++++ src/util.h | 88 ++ src/version.h | 2 + src/work.cpp | 291 ++++ 130 files changed, 31392 insertions(+) create mode 100644 BUGS create mode 100644 COPYING create mode 100644 LICENSE create mode 100644 LOADER.TXT create mode 100644 Makefile create mode 100644 NEWS create mode 100644 PROJECTS create mode 100644 README create mode 100644 README.SRC create mode 100644 THANKS create mode 100644 TODO create mode 100644 doc/Makefile create mode 100644 doc/upx.pod create mode 100644 src/Makefile create mode 100644 src/bele.h create mode 100644 src/c_file.cpp create mode 100644 src/c_init.cpp create mode 100644 src/c_none.cpp create mode 100644 src/c_screen.cpp create mode 100644 src/compress.cpp create mode 100644 src/conf.h create mode 100644 src/config_h/linux.h create mode 100644 src/console.h create mode 100644 src/except.cpp create mode 100644 src/except.h create mode 100644 src/fcto_ml.ch create mode 100644 src/fcto_ml2.ch create mode 100644 src/file.cpp create mode 100644 src/file.h create mode 100644 src/filter.cpp create mode 100644 src/filter.h create mode 100644 src/filteri.cpp create mode 100644 src/help.cpp create mode 100644 src/lefile.cpp create mode 100644 src/lefile.h create mode 100644 src/linker.cpp create mode 100644 src/linker.h create mode 100644 src/main.cpp create mode 100644 src/mem.cpp create mode 100644 src/mem.h create mode 100644 src/merge.sh create mode 100644 src/msg.cpp create mode 100644 src/mygetopt.cpp create mode 100644 src/mygetopt.h create mode 100644 src/p_com.cpp create mode 100644 src/p_com.h create mode 100644 src/p_djgpp2.cpp create mode 100644 src/p_djgpp2.h create mode 100644 src/p_elf.h create mode 100644 src/p_exe.cpp create mode 100644 src/p_exe.h create mode 100644 src/p_lx_elf.cpp create mode 100644 src/p_lx_elf.h create mode 100644 src/p_lx_sep.cpp create mode 100644 src/p_lx_sep.h create mode 100644 src/p_lx_sh.cpp create mode 100644 src/p_lx_sh.h create mode 100644 src/p_sys.cpp create mode 100644 src/p_sys.h create mode 100644 src/p_tmt.cpp create mode 100644 src/p_tmt.h create mode 100644 src/p_tos.cpp create mode 100644 src/p_tos.h create mode 100644 src/p_unix.cpp create mode 100644 src/p_unix.h create mode 100644 src/p_vmlinux.cpp create mode 100644 src/p_vxd.h create mode 100644 src/p_w32pe.cpp create mode 100644 src/p_w32pe.h create mode 100644 src/p_wcle.cpp create mode 100644 src/p_wcle.h create mode 100644 src/packer.cpp create mode 100644 src/packer.h create mode 100644 src/packhead.cpp create mode 100644 src/packmast.cpp create mode 100644 src/packmast.h create mode 100644 src/s_djgpp2.cpp create mode 100644 src/s_object.cpp create mode 100644 src/s_vcsa.cpp create mode 100644 src/s_win32.cpp create mode 100644 src/screen.h create mode 100644 src/stdcxx.cpp create mode 100644 src/stdcxx.h create mode 100644 src/stub/Makefile create mode 100644 src/stub/header.ash create mode 100644 src/stub/ident.ash create mode 100644 src/stub/ident_n.ash create mode 100644 src/stub/ident_s.ash create mode 100644 src/stub/l_com.asm create mode 100644 src/stub/l_djgpp2.asm create mode 100644 src/stub/l_exe.asm create mode 100644 src/stub/l_lx_elf.c create mode 100644 src/stub/l_lx_elf86.asm create mode 100644 src/stub/l_lx_elf86.lds create mode 100644 src/stub/l_lx_exec.c create mode 100644 src/stub/l_lx_exec86.asm create mode 100644 src/stub/l_lx_sep.c create mode 100644 src/stub/l_lx_sep86.asm create mode 100644 src/stub/l_lx_sep86.lds create mode 100644 src/stub/l_lx_sh.c create mode 100644 src/stub/l_lx_sh86.asm create mode 100644 src/stub/l_lx_sh86.lds create mode 100644 src/stub/l_sys.asm create mode 100644 src/stub/l_tmt.asm create mode 100644 src/stub/l_tos.s create mode 100644 src/stub/l_w32pe.asm create mode 100644 src/stub/l_wcle.asm create mode 100644 src/stub/linux.hh create mode 100644 src/stub/macros.ash create mode 100644 src/stub/merge.sh create mode 100644 src/stub/scripts/app.pl create mode 100644 src/stub/scripts/bin2h.pl create mode 100644 src/stub/scripts/brandelf.pl create mode 100644 src/stub/scripts/o2bin.pl create mode 100644 src/stub/scripts/setfold.pl create mode 100644 src/stub/scripts/stripelf.pl create mode 100644 src/stub/scripts/version.pl create mode 100644 src/stub/stub.asm create mode 100644 src/stub/util/sstrip/Makefile create mode 100644 src/stub/util/sstrip/README create mode 100644 src/stub/util/sstrip/README.1ST create mode 100644 src/stub/util/sstrip/sstrip.c create mode 100644 src/tailor.h create mode 100644 src/ui.cpp create mode 100644 src/ui.h create mode 100644 src/unupx.h create mode 100644 src/util.cpp create mode 100644 src/util.h create mode 100644 src/version.h create mode 100644 src/work.cpp diff --git a/BUGS b/BUGS new file mode 100644 index 00000000..b0cbfdfc --- /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 00000000..b4951ab7 --- /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 00000000..444ded8d --- /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 00000000..f46fcef5 --- /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 00000000..bba625f9 --- /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 00000000..5605a716 --- /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 00000000..7ed132a5 --- /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 00000000..0fb59f27 --- /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 00000000..6e6b4c3e --- /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 00000000..cbbf78da --- /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 00000000..f9e40986 --- /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 00000000..725f5b83 --- /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 00000000..9b82dea5 --- /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 00000000..a49b2957 --- /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 00000000..7d106ed8 --- /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 00000000..6ed66324 --- /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 00000000..1b43f2ca --- /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 00000000..43b61587 --- /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 00000000..afdd2fa9 --- /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 00000000..ed2964e9 --- /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 00000000..f45a6d0a --- /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 00000000..354b2059 --- /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 00000000..ac4fcfda --- /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 00000000..417a1157 --- /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 00000000..34479b2f --- /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 00000000..b0f13815 --- /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 00000000..cce46928 --- /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 00000000..9d00eea2 --- /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 00000000..2b228be0 --- /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 00000000..f2cbe8b3 --- /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 00000000..376a5585 --- /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 00000000..2d700d5b --- /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 00000000..f18685a4 --- /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 00000000..e2e7e6bf --- /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 00000000..935ad5bd --- /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 00000000..3aa79793 --- /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 00000000..a75edfdd --- /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 00000000..e96360cc --- /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 00000000..9b6e5195 --- /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 00000000..ab2424cd --- /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 00000000..567d5c92 --- /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 00000000..cc74413c --- /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 00000000..da1dba38 --- /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 00000000..56970dec --- /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 00000000..d09650af --- /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 00000000..8b3b244b --- /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 00000000..4ade2505 --- /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 00000000..8981cdf2 --- /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 00000000..be76ad0d --- /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 00000000..5103e920 --- /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 00000000..422d16a7 --- /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 00000000..7436f3b8 --- /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 00000000..c7dffc0b --- /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 00000000..9461b427 --- /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 00000000..e64fafc2 --- /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 00000000..ffdd2a81 --- /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 00000000..bac2314c --- /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 00000000..fa9b54f6 --- /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 00000000..0e17aec7 --- /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 00000000..2e9b09fb --- /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 00000000..c9bfaedf --- /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 00000000..8f16c1a6 --- /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 00000000..23717df8 --- /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 00000000..181539d3 --- /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 00000000..3dd636e9 --- /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 00000000..dba065f3 --- /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 00000000..6cb6f5ca --- /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 00000000..56843060 --- /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 00000000..10f48c63 --- /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 00000000..c50a38ae --- /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 00000000..342b8e8d --- /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 00000000..f2bd9590 --- /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 00000000..ebe71204 --- /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 00000000..08f74436 --- /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 00000000..765a80bc --- /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 00000000..3b76fb3f --- /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 00000000..dba60464 --- /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 00000000..0aff2575 --- /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 00000000..c2173a89 --- /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 00000000..ef4e5cd6 --- /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 00000000..6bd4f6dd --- /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 00000000..7219db85 --- /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 00000000..2fb34a04 --- /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 00000000..5addfb87 --- /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 00000000..0b7fc18d --- /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 00000000..9d293cfd --- /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 00000000..8ba82b54 --- /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 00000000..c2c9187b --- /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 00000000..ddb36599 --- /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 00000000..734e1113 --- /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 00000000..7bc41a3f --- /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 00000000..5fc0a7ff --- /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 00000000..d37d92fe --- /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 00000000..e5ae76e3 --- /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 00000000..79cfcf97 --- /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 00000000..f671dd28 --- /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 00000000..40049ddd --- /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 00000000..a7d1ff6d --- /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 00000000..19f5cb2c --- /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 00000000..ae6aca96 --- /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 00000000..9a78510a --- /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 00000000..b3b1e1a8 --- /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 00000000..7f2bfefc --- /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 00000000..317a8558 --- /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 00000000..db931379 --- /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 00000000..53750ff6 --- /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 00000000..68cdd246 --- /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 00000000..83049cd3 --- /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 00000000..446020b2 --- /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 00000000..d878d07f --- /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 00000000..548271de --- /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 00000000..fd4356b2 --- /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 00000000..922e2346 --- /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 00000000..6ec7b10f --- /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 00000000..5424d67b --- /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 00000000..5fd59607 --- /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 00000000..3ecd66bd --- /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 00000000..cc641f54 --- /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 00000000..dad4c92a --- /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 00000000..b96c1716 --- /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 00000000..2cbb363d --- /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 00000000..e109e8eb --- /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 00000000..9fb2500c --- /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 00000000..94e036a2 --- /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 00000000..ab2303c6 --- /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 00000000..a69271d9 --- /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 00000000..bf630ddc --- /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 00000000..194e2943 --- /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 00000000..eac3be8b --- /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 00000000..d38c8d2a --- /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 +*/ +