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