1 /* p_exe.cpp --
2 
3    This file is part of the UPX executable compressor.
4 
5    Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
6    Copyright (C) 1996-2020 Laszlo Molnar
7    All Rights Reserved.
8 
9    UPX and the UCL library are free software; you can redistribute them
10    and/or modify them under the terms of the GNU General Public License as
11    published by the Free Software Foundation; either version 2 of
12    the License, or (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; see the file COPYING.
21    If not, write to the Free Software Foundation, Inc.,
22    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24    Markus F.X.J. Oberhumer              Laszlo Molnar
25    <markus@oberhumer.com>               <ezerotven+github@gmail.com>
26  */
27 
28 
29 #include "conf.h"
30 #include "file.h"
31 #include "filter.h"
32 #include "packer.h"
33 #include "p_exe.h"
34 #include "linker.h"
35 
36 static const
37 #include "stub/i086-dos16.exe.h"
38 
39 #define RSFCRI          4096    // Reserved Space For Compressed Relocation Info
40 #define MAXMATCH        0x2000
41 #define MAXRELOCS       (0x8000-MAXMATCH)
42 
43 #define DI_LIMIT        0xff00  // see the assembly why
44 
45 
46 /*************************************************************************
47 //
48 **************************************************************************/
49 
PackExe(InputFile * f)50 PackExe::PackExe(InputFile *f) :
51     super(f)
52 {
53     bele = &N_BELE_RTP::le_policy;
54     COMPILE_TIME_ASSERT(sizeof(exe_header_t) == 32)
55     COMPILE_TIME_ASSERT_ALIGNED1(exe_header_t)
56     ih_exesize = ih_imagesize = ih_overlay = 0;
57     stack_for_lzma = 0;
58     use_clear_dirty_stack = false;
59 }
60 
61 
getCompressionMethods(int method,int level) const62 const int *PackExe::getCompressionMethods(int method, int level) const
63 {
64     bool small = ih_imagesize <= 256*1024;
65     // disable lzma for "--brute" unless explicitly given "--lzma"
66     // WARNING: this side effect persists for later files!
67     if (opt->all_methods_use_lzma && !opt->method_lzma_seen)
68         opt->all_methods_use_lzma = false;
69     return Packer::getDefaultCompressionMethods_8(method, level, small);
70 }
71 
72 
getFilters() const73 const int *PackExe::getFilters() const
74 {
75     return NULL;
76 }
77 
78 
fillExeHeader(struct exe_header_t * eh) const79 int PackExe::fillExeHeader(struct exe_header_t *eh) const
80 {
81 #define oh  (*eh)
82     // fill new exe header
83     int flag = 0;
84     if (!opt->dos_exe.no_reloc && !M_IS_LZMA(ph.method))
85         flag |= USEJUMP;
86     if (ih.relocs == 0)
87         flag |= NORELOC;
88 
89     memset(&oh,0,sizeof(oh));
90     oh.ident = 'M' + 'Z' * 256;
91     oh.headsize16 = 2;
92 
93     unsigned minsp = 0x200;
94     if (M_IS_LZMA(ph.method))
95         minsp = stack_for_lzma;
96     minsp = ALIGN_UP(minsp, 16u);
97     assert(minsp < 0xff00);
98     if (oh.sp > minsp)
99         minsp = oh.sp;
100     if (minsp < 0xff00 - 2)
101         minsp = ALIGN_UP(minsp, 2u);
102     oh.sp = minsp;
103 
104     unsigned destpara = (ph.u_len + ph.overlap_overhead - ph.c_len + 31) / 16;
105     oh.ss = ph.c_len/16 + destpara;
106     if (ih.ss*16 + ih.sp < 0x100000 && ih.ss > oh.ss && ih.sp > 0x200)
107         oh.ss = ih.ss;
108     if (oh.ss*16 + 0x50 < ih.ss*16 + ih.sp
109         && oh.ss*16 + 0x200 > ih.ss*16 + ih.sp)
110         oh.ss += 0x20;
111 
112     if (oh.ss != ih.ss)
113         flag |= SS;
114     if (oh.sp != ih.sp || M_IS_LZMA(ph.method))
115         flag |= SP;
116     return flag;
117 #undef oh
118 }
119 
addLoaderEpilogue(int flag)120 void PackExe::addLoaderEpilogue(int flag)
121 {
122     addLoader("EXEMAIN5", NULL);
123     if (relocsize)
124         addLoader(ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff) >= relocsize ? "EXENOADJ" : "EXEADJUS",
125                   "EXERELO1",
126                   has_9a ? "EXEREL9A" : "",
127                   "EXERELO2",
128                   ih_exesize > 0xFE00 ? "EXEREBIG" : "",
129                   "EXERELO3",
130                   NULL
131                  );
132     addLoader("EXEMAIN8",
133               device_driver ? "DEVICEEND" : "",
134               (flag & SS) ? "EXESTACK" : "",
135               (flag & SP) ? "EXESTASP" : "",
136               (flag & USEJUMP) ? "EXEJUMPF" : "",
137               NULL
138              );
139     if (!(flag & USEJUMP))
140         addLoader(ih.cs ? "EXERCSPO" : "",
141                   "EXERETIP",
142                   NULL
143                  );
144 
145     linker->defineSymbol("original_cs", ih.cs);
146     linker->defineSymbol("original_ip", ih.ip);
147     linker->defineSymbol("original_sp", ih.sp);
148     linker->defineSymbol("original_ss", ih.ss);
149     linker->defineSymbol("reloc_size",
150                          (ph.u_len <= DI_LIMIT || (ph.u_len & 0x7fff)
151                           >= relocsize ? 0 : MAXRELOCS) - relocsize);
152 }
153 
buildLoader(const Filter *)154 void PackExe::buildLoader(const Filter *)
155 {
156     // get flag
157     exe_header_t dummy_oh;
158     int flag = fillExeHeader(&dummy_oh);
159 
160     initLoader(stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe));
161 
162     if (M_IS_LZMA(ph.method))
163     {
164         addLoader("LZMA_DEC00",
165                   opt->small ? "LZMA_DEC10" : "LZMA_DEC20",
166                   "LZMA_DEC30",
167                   use_clear_dirty_stack ? "LZMA_DEC31" : "",
168                   "LZMA_DEC32",
169                   ph.u_len > 0xffff ? "LZMA_DEC33" : "",
170                   NULL
171                  );
172 
173         addLoaderEpilogue(flag);
174         defineDecompressorSymbols();
175         const unsigned lsize0 = getLoaderSize();
176 
177         // Lzma decompression code starts at ss:0x10, and its size is
178         // lsize bytes. It also needs getDecompressorWrkmemSize() bytes
179         // during uncompression. It also uses some stack, so 0x100
180         // more bytes are allocated
181         stack_for_lzma = 0x10 + lsize0 + getDecompressorWrkmemSize() + 0x100;
182         stack_for_lzma = ALIGN_UP(stack_for_lzma, 16u);
183 
184         unsigned clear_dirty_stack_low = 0x10 + lsize0;
185         clear_dirty_stack_low = ALIGN_UP(clear_dirty_stack_low, 2u);
186         if (use_clear_dirty_stack)
187             linker->defineSymbol("clear_dirty_stack_low", clear_dirty_stack_low);
188 
189         relocateLoader();
190         const unsigned lsize = getLoaderSize();
191         assert(lsize0 == lsize);
192         MemBuffer loader(lsize);
193         memcpy(loader, getLoader(), lsize);
194 
195         MemBuffer compressed_lzma;
196         compressed_lzma.allocForCompression(lsize);
197         unsigned c_len_lzma = MemBuffer::getSizeForCompression(lsize);
198         int r = upx_compress(loader, lsize, compressed_lzma, &c_len_lzma,
199                              NULL, M_NRV2B_LE16, 9, NULL, NULL);
200         assert(r == UPX_E_OK); assert(c_len_lzma < lsize);
201 
202         info("lzma+relocator code compressed: %u -> %u", lsize, c_len_lzma);
203         // reinit the loader
204         initLoader(stub_i086_dos16_exe, sizeof(stub_i086_dos16_exe));
205         // prepare loader
206         if (device_driver)
207             addLoader("DEVICEENTRY,LZMADEVICE,DEVICEENTRY2", NULL);
208 
209         linker->addSection("COMPRESSED_LZMA", compressed_lzma, c_len_lzma, 0);
210         addLoader("LZMAENTRY,NRV2B160,NRVDDONE,NRVDECO1,NRVGTD00,NRVDECO2",
211                   NULL);
212 
213     }
214     else if (device_driver)
215         addLoader("DEVICEENTRY,DEVICEENTRY2", NULL);
216 
217     addLoader("EXEENTRY",
218               M_IS_LZMA(ph.method) && device_driver ? "LONGSUB" : "SHORTSUB",
219               "JNCDOCOPY",
220               relocsize ? "EXERELPU" : "",
221               "EXEMAIN4",
222               M_IS_LZMA(ph.method) ? "" : "EXEMAIN4B",
223               "EXEMAIN4C",
224               M_IS_LZMA(ph.method) ? "COMPRESSED_LZMA_START,COMPRESSED_LZMA" : "",
225               "+G5DXXXX,UPX1HEAD,EXECUTPO",
226               NULL
227              );
228     if (ph.method == M_NRV2B_8)
229         addLoader("NRV2B16S",               // decompressor
230                   ph.u_len > DI_LIMIT ? "N2B64K01" : "",
231                   "NRV2BEX1",
232                   opt->cpu == opt->CPU_8086 ? "N2BX8601" : "N2B28601",
233                   "NRV2BEX2",
234                   opt->cpu == opt->CPU_8086 ? "N2BX8602" : "N2B28602",
235                   "NRV2BEX3",
236                   ph.c_len > 0xffff ? "N2B64K02" : "",
237                   "NRV2BEX9",
238                   NULL
239                  );
240     else if (ph.method == M_NRV2D_8)
241         addLoader("NRV2D16S",
242                   ph.u_len > DI_LIMIT ? "N2D64K01" : "",
243                   "NRV2DEX1",
244                   opt->cpu == opt->CPU_8086 ? "N2DX8601" : "N2D28601",
245                   "NRV2DEX2",
246                   opt->cpu == opt->CPU_8086 ? "N2DX8602" : "N2D28602",
247                   "NRV2DEX3",
248                   ph.c_len > 0xffff ? "N2D64K02" : "",
249                   "NRV2DEX9",
250                   NULL
251                  );
252     else if (ph.method == M_NRV2E_8)
253         addLoader("NRV2E16S",
254                   ph.u_len > DI_LIMIT ? "N2E64K01" : "",
255                   "NRV2EEX1",
256                   opt->cpu == opt->CPU_8086 ? "N2EX8601" : "N2E28601",
257                   "NRV2EEX2",
258                   opt->cpu == opt->CPU_8086 ? "N2EX8602" : "N2E28602",
259                   "NRV2EEX3",
260                   ph.c_len > 0xffff ? "N2E64K02" : "",
261                   "NRV2EEX9",
262                   NULL
263                  );
264     else if M_IS_LZMA(ph.method)
265         return;
266     else
267         throwInternalError("unknown compression method");
268 
269     addLoaderEpilogue(flag);
270 }
271 
272 
273 /*************************************************************************
274 //
275 **************************************************************************/
276 
readFileHeader()277 int PackExe::readFileHeader()
278 {
279     ih_exesize = ih_imagesize = ih_overlay = 0;
280     fi->readx(&ih,sizeof(ih));
281     if (ih.ident != 'M' + 'Z'*256 && ih.ident != 'Z' + 'M'*256)
282         return 0;
283     ih_exesize = ih.m512 + ih.p512*512 - (ih.m512 ? 512 : 0);
284     if (!ih_exesize) {
285         ih_exesize = file_size;
286     }
287     ih_imagesize = ih_exesize - ih.headsize16*16;
288     ih_overlay = file_size - ih_exesize;
289     if (file_size < (int)sizeof(ih)
290     ||  ((ih.m512 | ih.p512) && ih.m512+ih.p512*512u < sizeof (ih)))
291         throwCantPack("illegal exe header");
292     if (file_size < (off_t)ih_exesize || ih_imagesize <= 0 || ih_imagesize > ih_exesize)
293         throwCantPack("exe header corrupted");
294 #if 0
295     printf("dos/exe header: %d %d %d\n", ih_exesize, ih_imagesize, ih_overlay);
296 #endif
297     return UPX_F_DOS_EXE;
298 }
299 
300 
canPack()301 bool PackExe::canPack()
302 {
303     if (fn_has_ext(fi->getName(),"sys"))
304         return false;
305     if (!readFileHeader())
306         return false;
307     if (file_size < 1024)
308         throwCantPack("file is too small");
309     fi->seek(0x3c,SEEK_SET);
310     LE32 offs;
311     fi->readx(&offs,sizeof (offs));
312     if (ih.relocoffs >= 0x40 && offs)
313     {
314         if (opt->dos_exe.force_stub)
315             opt->overlay = opt->COPY_OVERLAY;
316         else
317             throwCantPack("can't pack new-exe");
318     }
319     return true;
320 }
321 
322 
323 /*************************************************************************
324 //
325 **************************************************************************/
326 
327 static
optimize_relocs(upx_byte * b,const unsigned size,const upx_byte * relocs,const unsigned nrelocs,upx_byte * crel,bool * has_9a)328 unsigned optimize_relocs(upx_byte *b, const unsigned size,
329                          const upx_byte *relocs, const unsigned nrelocs,
330                          upx_byte *crel, bool *has_9a)
331 {
332     if (opt->exact)
333         throwCantPackExact();
334 
335     upx_byte * const crel_save = crel;
336     unsigned i;
337     unsigned seg_high = 0;
338 #if 0
339     unsigned seg_low = 0xffffffff;
340     unsigned off_low = 0xffffffff;
341     unsigned off_high = 0;
342     unsigned linear_low = 0xffffffff;
343     unsigned linear_high = 0;
344 #endif
345 
346     // pass 1 - find 0x9a bounds
347     for (i = 0; i < nrelocs; i++)
348     {
349         unsigned addr = get_le32(relocs+4*i);
350         if (addr >= size - 1)
351             throwCantPack("unexpected relocation 1");
352         if (addr >= 3 && b[addr-3] == 0x9a)
353         {
354             unsigned seg = get_le16(b+addr);
355             if (seg > seg_high)
356                 seg_high = seg;
357 #if 0
358             if (seg < seg_low)
359                 seg_low = seg;
360             unsigned off = get_le16(b+addr-2);
361             if (off < off_low)
362                 off_low = off;
363             if (off > off_high)
364                 off_high = off;
365             unsigned l = (seg << 4) + off;
366             if (l < linear_low)
367                 linear_low = l;
368             if (l > linear_high)
369                 linear_high = l;
370 #endif
371         }
372     }
373     //printf("%d %d\n", seg_low, seg_high);
374     //printf("%d %d\n", off_low, off_high);
375     //printf("%d %d\n", linear_low, linear_high);
376 
377 
378     // pass 2 - reloc
379 
380     crel += 4; // to be filled in later
381 
382     unsigned ones = 0;
383     unsigned es = 0, di, t;
384     i = 0;
385     do
386     {
387         unsigned addr = get_le32(relocs+4*i);
388         set_le16(crel,di = addr & 0x0f);
389         set_le16(crel+2,(addr >> 4) - es);
390         es = addr >> 4;
391         crel += 4;
392 
393         for (++i; i < nrelocs; i++)
394         {
395             addr = get_le32(relocs+4*i);
396             //printf ("%x\n",es*16+di);
397             if ((addr - es*16 > 0xfffe)
398                 || (i == nrelocs - 1 && addr - es * 16 > 0xff00)
399                )
400             {
401                 // segment change
402                 t = 1+(0xffff-di)/254;
403                 memset(crel,1,t);
404                 crel += t;
405                 ones += t-1; // -1 is used to help the assembly stuff
406                 break;
407             }
408             unsigned offs = addr - es*16;
409             if (offs >= 3 && b[es*16 + offs-3] == 0x9a && offs > di + 3)
410             {
411                 for (t = di; t < offs-3; t++)
412                     if (b[es*16+t] == 0x9a && get_le16(b+es*16+t+3) <= seg_high)
413                         break;
414                 if (t == offs-3)
415                 {
416                     // code 0: search for 0x9a
417                     *crel++ = 0;
418                     di = offs;
419                     *has_9a = true;
420                     continue;
421                 }
422             }
423             t = offs - di;
424             if (t < 2)
425                 throwCantPack("unexpected relocation 2");
426 
427             while (t >= 256)
428             {
429                 // code 1: add 254, don't reloc
430                 *crel++ = 1;
431                 t -= 254;
432                 ones++;
433             }
434             *crel++ = (unsigned char) t;
435             di = offs;
436         }
437     } while (i < nrelocs);
438     *crel++ = 1;
439     ones++;
440     set_le16 (crel_save,ones);
441     set_le16 (crel_save+2,seg_high);
442 
443     //OutputFile::dump("x.rel", crel_save, crel - crel_save);
444     return (unsigned) (crel - crel_save);
445 }
446 
447 
448 /*************************************************************************
449 //
450 **************************************************************************/
451 
pack(OutputFile * fo)452 void PackExe::pack(OutputFile *fo)
453 {
454     unsigned ic;
455 
456     if (ih.relocs > MAXRELOCS)
457         throwCantPack("too many relocations");
458     checkOverlay(ih_overlay);
459 
460     // alloc buffers
461     relocsize = RSFCRI + 4*ih.relocs;
462     ibuf.alloc(ih_imagesize+16+relocsize+2);
463     obuf.allocForCompression(ih_imagesize+16+relocsize+2);
464 
465     // read image
466     fi->seek(ih.headsize16*16,SEEK_SET);
467     fi->readx(ibuf,ih_imagesize);
468 
469     checkAlreadyPacked(ibuf, UPX_MIN(ih_imagesize, 127u));
470 
471     device_driver = get_le32(ibuf) == 0xffffffffu;
472 
473     // relocations
474     has_9a = false;
475     upx_byte *w = ibuf + ih_imagesize;
476     if (ih.relocs)
477     {
478         upx_byte *wr = w + RSFCRI;
479 
480         fi->seek(ih.relocoffs,SEEK_SET);
481         fi->readx(wr,4*ih.relocs);
482 
483         for (ic = 0; ic < ih.relocs; ic++)
484         {
485             unsigned jc = get_le32(wr+4*ic);
486             set_le32(wr+4*ic, ((jc>>16)*16+(jc&0xffff)) & 0xfffff);
487         }
488         qsort(wr,ih.relocs,4,le32_compare);
489         relocsize = optimize_relocs(ibuf, ih_imagesize, wr, ih.relocs, w, &has_9a);
490         set_le16(w+relocsize, relocsize+2);
491         relocsize += 2;
492         if (relocsize > MAXRELOCS)
493             throwCantPack("too many relocations");
494 #if 0
495         upx_byte out[9*relocsize/8+1024];
496         unsigned in_len = relocsize;
497         unsigned out_len = 0;
498         ucl_nrv2b_99_compress(w, in_len, out, &out_len, NULL, 9, NULL, NULL);
499         printf("reloc compress: %d -> %d\n", in_len, out_len);
500 #endif
501     }
502     else
503     {
504         relocsize = 0;
505     }
506 
507     // prepare packheader
508     ph.u_len = ih_imagesize + relocsize;
509     // prepare filter
510     Filter ft(ph.level);
511     // compress (max_match = 8192)
512     upx_compress_config_t cconf; cconf.reset();
513     cconf.conf_ucl.max_match = MAXMATCH;
514     cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
515     compressWithFilters(&ft, 32, &cconf);
516 
517     if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method))
518         if (ph.max_run_found + ph.max_match_found > 0x8000)
519             throwCantPack("decompressor limit exceeded, send a bugreport");
520 
521 #if TESTING
522     if (opt->debug.debug_level)
523     {
524         printf("image+relocs %d -> %d\n",ih_imagesize+relocsize,ph.c_len);
525         printf("offsets: %d - %d\nmatches: %d - %d\nruns: %d - %d\n",
526                0/*ph.min_offset_found*/,ph.max_offset_found,
527                0/*ph.min_match_found*/,ph.max_match_found,
528                0/*ph.min_run_found*/,ph.max_run_found);
529     }
530 #endif
531 
532     int flag = fillExeHeader(&oh);
533 
534     const unsigned lsize = getLoaderSize();
535     MemBuffer loader(lsize);
536     memcpy(loader,getLoader(),lsize);
537     //OutputFile::dump("xxloader.dat", loader, lsize);
538 
539     // patch loader
540     const unsigned packedsize = ph.c_len;
541     const unsigned e_len = getLoaderSectionStart("EXECUTPO");
542     const unsigned d_len = lsize - e_len;
543     assert((e_len&15) == 0);
544 
545     const unsigned copysize = (1+packedsize+d_len) & ~1;
546     const unsigned firstcopy = copysize%0x10000 ? copysize%0x10000 : 0x10000;
547 
548     // set oh.min & oh.max
549     ic = ih.min*16 + ih_imagesize;
550     if (ic < oh.ss*16u + oh.sp)
551         ic = oh.ss*16u + oh.sp;
552     oh.min = (ic - (packedsize + lsize)) / 16;
553     ic = oh.min + (ih.max - ih.min);
554     oh.max = ic < 0xffff && ih.max != 0xffff ? ic : 0xffff;
555 
556     // set extra info
557     unsigned char extra_info[9];
558     unsigned eisize = 0;
559     if (oh.ss != ih.ss)
560     {
561         set_le16(extra_info+eisize,ih.ss);
562         eisize += 2;
563         assert((flag & SS) != 0);   // set in fillExeHeader()
564     }
565     if (oh.sp != ih.sp)
566     {
567         set_le16(extra_info+eisize,ih.sp);
568         eisize += 2;
569         assert((flag & SP) != 0);   // set in fillExeHeader()
570     }
571     if (ih.min != oh.min)
572     {
573         set_le16(extra_info+eisize,ih.min);
574         eisize += 2;
575         flag |= MINMEM;
576     }
577     if (ih.max != oh.max)
578     {
579         set_le16(extra_info+eisize,ih.max);
580         eisize += 2;
581         flag |= MAXMEM;
582     }
583     extra_info[eisize++] = (unsigned char) flag;
584 
585     if (M_IS_NRV2B(ph.method) || M_IS_NRV2D(ph.method) || M_IS_NRV2E(ph.method))
586         linker->defineSymbol("bx_magic", 0x7FFF + 0x10 * ((packedsize & 15) + 1));
587 
588     unsigned decompressor_entry = 1 + (packedsize & 15);
589     if (M_IS_LZMA(ph.method))
590         decompressor_entry = 0x10;
591     linker->defineSymbol("decompressor_entry", decompressor_entry);
592 
593     // patch loader
594     if (flag & USEJUMP)
595     {
596         // I use a relocation entry to set the original cs
597         unsigned n = getLoaderSectionStart("EXEJUMPF") + 1;
598         n += packedsize + 2;
599         oh.relocs = 1;
600         oh.firstreloc = (n & 0xf) + ((n >> 4) << 16);
601     }
602     else
603     {
604         oh.relocs = 0;
605         oh.firstreloc = ih.cs * 0x10000 + ih.ip;
606     }
607 
608     // g++ 3.1 does not like the following line...
609 //    oh.relocoffs = offsetof(exe_header_t, firstreloc);
610     oh.relocoffs = ptr_diff(&oh.firstreloc, &oh);
611 
612     linker->defineSymbol("destination_segment", oh.ss - ph.c_len / 16 - e_len / 16);
613     linker->defineSymbol("source_segment", e_len / 16 + (copysize - firstcopy) / 16);
614     linker->defineSymbol("copy_offset", firstcopy - 2);
615     linker->defineSymbol("words_to_copy",firstcopy / 2);
616 
617     linker->defineSymbol("exe_stack_sp", oh.sp);
618     linker->defineSymbol("exe_stack_ss", oh.ss);
619     linker->defineSymbol("interrupt", get_le16(ibuf + 8));
620     linker->defineSymbol("attribute", get_le16(ibuf + 4));
621     linker->defineSymbol("orig_strategy", get_le16(ibuf + 6));
622 
623     const unsigned outputlen = sizeof(oh)+lsize+packedsize+eisize;
624     oh.m512 = outputlen & 511;
625     oh.p512 = (outputlen + 511) >> 9;
626 
627     const char *exeentry = M_IS_LZMA(ph.method) ? "LZMAENTRY" : "EXEENTRY";
628     oh.ip = device_driver ? getLoaderSection(exeentry) - 2 : 0;
629 
630     defineDecompressorSymbols();
631     relocateLoader();
632     memcpy(loader, getLoader(), lsize);
633     patchPackHeader(loader,e_len);
634 
635 //fprintf(stderr,"\ne_len=%x d_len=%x c_len=%x oo=%x ulen=%x destp=%x copys=%x images=%x",e_len,d_len,packedsize,ph.overlap_overhead,ph.u_len,destpara,copysize,ih_imagesize);
636 
637     // write header + write loader + compressed file
638 #if TESTING
639     if (opt->debug.debug_level)
640         printf("\n%d %d %d %d\n",(int)sizeof(oh),e_len,packedsize,d_len);
641 #endif
642     fo->write(&oh,sizeof(oh));
643     fo->write(loader,e_len);            // entry
644     fo->write(obuf,packedsize);
645     fo->write(loader+e_len,d_len);      // decompressor
646     fo->write(extra_info,eisize);
647     assert(eisize <= 9);
648 #if 0
649     printf("%-13s: program hdr  : %8ld bytes\n", getName(), (long) sizeof(oh));
650     printf("%-13s: entry        : %8ld bytes\n", getName(), (long) e_len);
651     printf("%-13s: compressed   : %8ld bytes\n", getName(), (long) packedsize);
652     printf("%-13s: decompressor : %8ld bytes\n", getName(), (long) d_len);
653     printf("%-13s: extra info   : %8ld bytes\n", getName(), (long) eisize);
654 #endif
655 
656     // verify
657     verifyOverlappingDecompression();
658 
659     // copy the overlay
660     copyOverlay(fo, ih_overlay, &obuf);
661 //fprintf (stderr,"%x %x\n",relocsize,ph.u_len);
662 
663     // finally check the compression ratio
664     if (!checkFinalCompressionRatio(fo))
665         throwNotCompressible();
666 }
667 
668 
669 /*************************************************************************
670 //
671 **************************************************************************/
672 
canUnpack()673 int PackExe::canUnpack()
674 {
675     if (!readFileHeader())
676         return false;
677     const off_t off = ih.headsize16 * 16;
678     fi->seek(off, SEEK_SET);
679     bool b = readPackHeader(4096);
680     return b && (off + (off_t) ph.c_len <= file_size);
681 }
682 
683 
684 /*************************************************************************
685 //
686 **************************************************************************/
687 
unpack(OutputFile * fo)688 void PackExe::unpack(OutputFile *fo)
689 {
690     ibuf.alloc(file_size);
691     obuf.allocForUncompression(ph.u_len);
692 
693     // read the file
694     fi->seek(ih.headsize16*16,SEEK_SET);
695     fi->readx(ibuf,ih_imagesize);
696 
697     // get compressed data offset
698     unsigned e_len = ph.buf_offset + ph.getPackHeaderSize();
699     if (ih_imagesize <= e_len + ph.c_len)
700         throwCantUnpack("file damaged");
701 
702     checkOverlay(ih_overlay);
703 
704     // decompress
705     decompress(ibuf+e_len,obuf);
706 
707     unsigned imagesize = ih_imagesize;
708     imagesize--;
709     const unsigned char flag = ibuf[imagesize];
710 
711     unsigned relocn = 0;
712     upx_byte *relocs = obuf + ph.u_len;
713 
714     MemBuffer wrkmem;
715     if (!(flag & NORELOC))
716     {
717         relocs -= get_le16(obuf+ph.u_len-2);
718         ph.u_len -= 2;
719 
720         wrkmem.alloc(4*MAXRELOCS);
721         unsigned es = 0, ones = get_le16(relocs);
722         const unsigned seghi = get_le16(relocs+2);
723         const upx_byte *p = relocs + 4;
724 
725         while (ones)
726         {
727             unsigned di = get_le16(p);
728             es += get_le16(p+2);
729             bool dorel = true;
730             for (p += 4; ones && di < 0x10000; p++)
731             {
732                 if (dorel)
733                 {
734                     set_le16(wrkmem+4*relocn,di);
735                     set_le16(wrkmem+2+4*relocn++,es);
736                     //printf ("%x\n",es*16+di);
737                 }
738                 dorel = true;
739                 if (*p == 0)
740                 {
741                     const upx_byte *q;
742                     for (q = obuf+es*16+di; !(*q == 0x9a && get_le16(q+3) <= seghi); q++)
743                         ;
744                     di = ptr_diff(q, obuf+es*16) + 3;
745                 }
746                 else if (*p == 1)
747                 {
748                     di += 254;
749                     if (di < 0x10000)
750                         ones--;
751                     dorel = false;
752                 }
753                 else
754                     di += *p;
755             }
756         }
757     }
758 
759     // fill new exe header
760     memset(&oh,0,sizeof(oh));
761     oh.ident = 'M' + 'Z'*256;
762 
763     if (relocn)
764     {
765         oh.relocs = relocn;
766         while (relocn & 3)
767             set_le32(wrkmem+4*relocn++,0);
768     }
769 
770     unsigned outputlen = ptr_diff(relocs, obuf) + sizeof(oh) + relocn*4;
771     oh.m512 = outputlen & 511;
772     oh.p512 = (outputlen + 511) >> 9;
773     oh.headsize16 = 2+relocn/4;
774 
775     oh.max = ih.max;
776     oh.min = ih.min;
777     oh.sp = ih.sp;
778     oh.ss = ih.ss;
779 
780     if (flag & MAXMEM)
781         { imagesize -= 2; oh.max = get_le16(ibuf+imagesize); }
782     if (flag & MINMEM)
783         { imagesize -= 2; oh.min = get_le16(ibuf+imagesize); }
784     if (flag & SP)
785         { imagesize -= 2; oh.sp = get_le16(ibuf+imagesize); }
786     if (flag & SS)
787         { imagesize -= 2; oh.ss = get_le16(ibuf+imagesize); }
788 
789     unsigned ip = (flag & USEJUMP) ? get_le32(ibuf+imagesize-4) : (unsigned) ih.firstreloc;
790     oh.ip = ip & 0xffff;
791     oh.cs = ip >> 16;
792 
793     oh.relocoffs = sizeof(oh);
794     oh.firstreloc = 0;
795     if (!fo)
796         return;
797 
798     // write header + relocations + uncompressed file
799     fo->write(&oh,sizeof(oh));
800     if (relocn)
801         fo->write(wrkmem,relocn*4);
802     fo->write(obuf, ptr_diff(relocs, obuf));
803 
804     // copy the overlay
805     copyOverlay(fo, ih_overlay, &obuf);
806 }
807 
808 
newLinker() const809 Linker* PackExe::newLinker() const
810 {
811     return new ElfLinkerX86();
812 }
813 
814 /*
815 
816 memory layout at decompression time
817 ===================================
818 
819 normal exe
820 ----------
821 
822 a, at load time
823 
824 (e - copying code, C - compressed data, d - decompressor+relocator,
825  x - not specified, U - uncompressed code+data, R uncompressed relocation)
826 
827 eeCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd
828 ^ CS:0                                       ^ SS:0
829 
830 b, after copying
831 
832 xxxxxxxxxxxxxxxCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCdddd
833 ^ES:DI=0       ^ DS:SI=0                     ^ CS=SS, IP in range 0..0xf
834 
835 c, after uncompression
836 
837 UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURRdddd
838                                            ^ ES:DI
839 
840 device driver
841 -------------
842 
843 the file has 2 entry points, CS:0 in device driver mode, and
844 CS:exe_as_device_entry in normal mode. the code in section DEVICEENTRY
845 sets up the same environment for section EXEENTRY, as it would see in normal
846 execution mode.
847 
848 lzma uncompression for normal exes
849 ----------------------------------
850 
851 (n - nrv2b uncompressor, l - nrv2b compressed lzma + relocator code)
852 
853 a, at load time
854 
855 nneelllCCCCCCCCCCCCCCCCCCCCCCCCC
856 
857 ^ CS:0                                       ^ SS:0
858 
859 b, after nrv2b
860 
861 nneelllCCCCCCCCCCCCCCCCCCCCCCCCC             dddd
862 ^ CS:0                                       ^ SS:0x10
863 
864 after this, normal ee code runs
865 
866 lzma + device driver
867 --------------------
868 
869 (D - device driver adapter)
870 
871 a, at load time
872 
873 DDnneelllCCCCCCCCCCCCCCCCCCCCCCCCC
874 
875 */
876 
877 /* vim:set ts=4 sw=4 et: */
878