Line | Count | Source |
1 | | /* pefile.cpp -- |
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 "pefile.h" |
33 | | #include "linker.h" |
34 | | |
35 | 0 | #define FILLVAL 0 |
36 | 406 | #define import my_import // "import" is a keyword since C++20 |
37 | | |
38 | | /************************************************************************* |
39 | | // |
40 | | **************************************************************************/ |
41 | | |
42 | 0 | #define IPTR_VAR(type, var, first) SPAN_S_VAR(type, var, first, ibuf) |
43 | 18.9k | #define OPTR_VAR(type, var, first) SPAN_S_VAR(type, var, first, obuf) |
44 | | #define IPTR_VAR_OFFSET(type, var, offset) \ |
45 | 42 | SPAN_S_VAR(type, var, ibuf + (offset), ibuf.getSize() - (offset), ibuf + (offset)) |
46 | | |
47 | 122 | static void xcheck(const void *p) may_throw { |
48 | 122 | if very_unlikely (p == nullptr) |
49 | 4 | throwCantUnpack("xcheck unexpected nullptr pointer; take care!"); |
50 | 122 | } |
51 | 92 | static void xcheck_noexcept(const void *p) noexcept { assert_noexcept(p != nullptr); } |
52 | 4.06k | static void xcheck(const void *p, size_t plen, const void *b, size_t blen) may_throw { |
53 | 4.06k | const charptr pp = (const charptr) p; |
54 | 4.06k | const charptr bb = (const charptr) b; |
55 | 4.06k | if very_unlikely (pp < bb || pp > bb + blen || pp + plen > bb + blen) |
56 | 2 | throwCantUnpack("xcheck pointer out of range; take care!"); |
57 | 4.06k | } |
58 | 227 | #define ICHECK(p, bytes) xcheck(raw_bytes(p, 0), bytes, ibuf, ibuf.getSize()) |
59 | 3.85k | #define OCHECK(p, bytes) xcheck(raw_bytes(p, 0), bytes, obuf, obuf.getSize()) |
60 | | |
61 | | // #define imemset(a,b,c) ICHECK(a,c), memset(a,b,c) |
62 | | // #define omemset(a,b,c) OCHECK(a,c), memset(a,b,c) |
63 | | // #define imemcpy(a,b,c) ICHECK(a,c), memcpy(a,b,c) |
64 | 132 | #define omemcpy(a, b, c) OCHECK(a, c), memcpy(a, b, c) |
65 | 3.67k | #define omemmove(a, b, c) OCHECK(a, c), memmove(a, b, c) |
66 | | |
67 | | /************************************************************************* |
68 | | // |
69 | | **************************************************************************/ |
70 | | |
71 | 40.0k | PeFile::PeFile(InputFile *f) : super(f) { |
72 | 40.0k | bele = &N_BELE_RTP::le_policy; |
73 | 40.0k | COMPILE_TIME_ASSERT(sizeof(ddirs_t) == 8) |
74 | 40.0k | COMPILE_TIME_ASSERT(sizeof(import_desc) == 20) |
75 | 40.0k | COMPILE_TIME_ASSERT(sizeof(pe_section_t) == 40) |
76 | 40.0k | COMPILE_TIME_ASSERT_ALIGNED1(ddirs_t) |
77 | 40.0k | COMPILE_TIME_ASSERT_ALIGNED1(import_desc) |
78 | 40.0k | COMPILE_TIME_ASSERT_ALIGNED1(pe_section_t) |
79 | 40.0k | COMPILE_TIME_ASSERT(RT_LAST == TABLESIZE(opt->win32_pe.compress_rt)) |
80 | | |
81 | 40.0k | isection = nullptr; |
82 | 40.0k | oimport = nullptr; |
83 | 40.0k | oimpdlls = nullptr; |
84 | 40.0k | orelocs = nullptr; |
85 | 40.0k | oexport = nullptr; |
86 | 40.0k | otls = nullptr; |
87 | 40.0k | oresources = nullptr; |
88 | 40.0k | oxrelocs = nullptr; |
89 | 40.0k | icondir_offset = 0; |
90 | 40.0k | icondir_count = 0; |
91 | 40.0k | importbyordinal = false; |
92 | 40.0k | kernel32ordinal = false; |
93 | 40.0k | tlsindex = 0; |
94 | 40.0k | big_relocs = 0; |
95 | 40.0k | sorelocs = 0; |
96 | 40.0k | soxrelocs = 0; |
97 | 40.0k | sotls = 0; |
98 | 40.0k | ilinker = nullptr; |
99 | 40.0k | use_tls_callbacks = false; |
100 | 40.0k | oloadconf = nullptr; |
101 | 40.0k | soloadconf = 0; |
102 | 40.0k | dbgCET = nullptr; |
103 | | |
104 | 40.0k | isdll = false; |
105 | 40.0k | isrtm = false; |
106 | 40.0k | isefi = false; |
107 | 40.0k | use_dep_hack = true; |
108 | 40.0k | use_clear_dirty_stack = true; |
109 | 40.0k | use_stub_relocs = true; |
110 | 40.0k | } |
111 | | |
112 | 105 | bool PeFile::testUnpackVersion(int version) const { |
113 | 105 | if (version != ph_version && ph_version != -1) |
114 | 0 | throwCantUnpack("program has been modified; run a virus checker!"); |
115 | 105 | if (!canUnpackVersion(version)) |
116 | 3 | throwCantUnpack("this program is packed with an obsolete version and cannot be unpacked"); |
117 | 102 | return true; |
118 | 105 | } |
119 | | |
120 | | /************************************************************************* |
121 | | // util |
122 | | **************************************************************************/ |
123 | | |
124 | | // early check of machine to generate a helpful error message |
125 | | // FIXME/TODO: proper check for ARM64EC |
126 | | // FIXME/TODO: proper check for ARM64X "universal" binary |
127 | | // CHPE Compiled Hybrid PE: Microsoft internal only? |
128 | | // CHPEV2 Compiled Hybrid PE: ARM64EC, ARM64X |
129 | 589 | /*static*/ int PeFile::checkMachine(unsigned cpu) { |
130 | | // unsupported |
131 | 589 | if (cpu == IMAGE_FILE_MACHINE_IA64) |
132 | 1 | throwCantPack("win64/ia64 is not supported"); |
133 | 588 | if (cpu == IMAGE_FILE_MACHINE_LOONGARCH64) |
134 | 1 | throwCantPack("win64/loong64 is not supported"); |
135 | 587 | if (cpu == IMAGE_FILE_MACHINE_RISCV64) |
136 | 1 | throwCantPack("win64/riscv64 is not supported"); |
137 | | |
138 | | // known but not (yet?) supported |
139 | 586 | if (cpu == IMAGE_FILE_MACHINE_ARMNT) |
140 | 1 | throwCantPack("win32/armnt is not supported"); // obsolete |
141 | 585 | if (cpu == IMAGE_FILE_MACHINE_ARM64) |
142 | 1 | throwCantPack("win64/arm64 is not yet supported"); |
143 | | // FIXME: it seems that arm64ec actually uses MACHINE_AMD64 ??? |
144 | 584 | if (cpu == IMAGE_FILE_MACHINE_ARM64EC) |
145 | 1 | throwCantPack("win64/arm64ec is not yet supported"); |
146 | | |
147 | | // supported |
148 | 583 | if (cpu == IMAGE_FILE_MACHINE_AMD64) |
149 | 42 | return UPX_F_W64PE_AMD64; |
150 | 541 | if (cpu == IMAGE_FILE_MACHINE_ARM || cpu == IMAGE_FILE_MACHINE_THUMB) |
151 | 94 | return UPX_F_WINCE_ARM; |
152 | 447 | if (cpu >= IMAGE_FILE_MACHINE_I386 && cpu <= 0x150) // what is this 0x150 ??? |
153 | 407 | return UPX_F_W32PE_I386; |
154 | | |
155 | | // other or unknown (alpha, mips, powerpc, sh, etc.) |
156 | 40 | throwCantPack("pefile: unsupported machine %#x", cpu); |
157 | 0 | return 0; // pacify msvc |
158 | 447 | } |
159 | | |
160 | 40.0k | int PeFile::readFileHeader() { |
161 | 40.0k | struct alignas(1) ExeHeader final { |
162 | 40.0k | LE16 mz; |
163 | 40.0k | LE16 m512; |
164 | 40.0k | LE16 p512; |
165 | 40.0k | char _[18]; |
166 | 40.0k | LE16 relocoffs; |
167 | 40.0k | char __[34]; |
168 | 40.0k | LE32 nexepos; |
169 | 40.0k | }; |
170 | | |
171 | 40.0k | COMPILE_TIME_ASSERT(sizeof(ExeHeader) == 64) |
172 | 40.0k | COMPILE_TIME_ASSERT_ALIGNED1(ExeHeader) |
173 | 40.0k | COMPILE_TIME_ASSERT(sizeof(((ExeHeader *) nullptr)->_) == 18) |
174 | 40.0k | COMPILE_TIME_ASSERT(sizeof(((ExeHeader *) nullptr)->__) == 34) |
175 | | |
176 | 40.0k | ExeHeader h; |
177 | 40.0k | int ic; |
178 | 40.0k | pe_offset = 0; |
179 | | |
180 | 45.7k | for (ic = 0; ic < 20; ic++) { |
181 | 45.5k | fi->seek(pe_offset, SEEK_SET); |
182 | 45.5k | fi->readx(&h, sizeof(h)); |
183 | | |
184 | 45.5k | if (h.mz == 'M' + 'Z' * 256) // dos exe |
185 | 5.85k | { |
186 | 5.85k | if (h.nexepos && h.nexepos < sizeof(ExeHeader)) { |
187 | | // Overlapping MZ and PE headers by 'leanify', etc. |
188 | 2 | char buf[64]; |
189 | 2 | snprintf(buf, sizeof(buf), "PE and MZ header overlap: %#x < %#x", |
190 | 2 | (unsigned) h.nexepos, (unsigned) sizeof(ExeHeader)); |
191 | 2 | throwCantPack(buf); |
192 | 2 | } |
193 | 5.85k | const unsigned delta = (h.relocoffs >= 0x40) |
194 | 5.85k | ? h.nexepos // new format exe |
195 | 5.85k | : (h.p512 * 512 + h.m512 - h.m512 ? 512 : h.nexepos); |
196 | | |
197 | 5.85k | if ((pe_offset + delta) < delta // wrap-around |
198 | 5.85k | || (pe_offset + delta) > file_size_u) { |
199 | 88 | char buf[64]; |
200 | 88 | snprintf(buf, sizeof(buf), "bad PE delta %#x at offset %#x", delta, pe_offset); |
201 | 88 | throwCantPack(buf); |
202 | 88 | } |
203 | 5.76k | pe_offset += delta; |
204 | 39.6k | } else if (get_le32((const byte *) &h) == 'P' + 'E' * 256) |
205 | 603 | break; |
206 | 39.0k | else |
207 | 39.0k | return 0; |
208 | 45.5k | } |
209 | 852 | if (ic == 20) |
210 | 249 | return 0; |
211 | 603 | fi->seek(pe_offset, SEEK_SET); |
212 | 603 | readPeHeader(); |
213 | 603 | return getFormat(); |
214 | 852 | } |
215 | | |
216 | | /************************************************************************* |
217 | | // interval handling |
218 | | **************************************************************************/ |
219 | | |
220 | 0 | PeFile::Interval::Interval(SPAN_P(byte) b) : base(b) {} |
221 | | |
222 | 0 | PeFile::Interval::~Interval() noexcept { ::free(ivarr); } |
223 | | |
224 | 0 | int __acc_cdecl_qsort PeFile::Interval::compare(const void *p1, const void *p2) { |
225 | 0 | const interval *i1 = (const interval *) p1; |
226 | 0 | const interval *i2 = (const interval *) p2; |
227 | 0 | if (i1->start < i2->start) |
228 | 0 | return -1; |
229 | 0 | if (i1->start > i2->start) |
230 | 0 | return 1; |
231 | 0 | if (i1->len < i2->len) |
232 | 0 | return 1; |
233 | 0 | if (i1->len > i2->len) |
234 | 0 | return -1; |
235 | 0 | return 0; |
236 | 0 | } |
237 | | |
238 | 0 | void PeFile::Interval::add_interval(unsigned start, unsigned len) { |
239 | 0 | if (ivnum == ivcapacity) { |
240 | 0 | ivcapacity += 15; |
241 | 0 | ivarr = (interval *) ::realloc(ivarr, mem_size(sizeof(interval), ivcapacity)); |
242 | 0 | assert_noexcept(ivarr != nullptr); |
243 | 0 | } |
244 | 0 | ivarr[ivnum].start = start; |
245 | 0 | ivarr[ivnum].len = len; |
246 | 0 | ivnum += 1; |
247 | 0 | } |
248 | | |
249 | 0 | void PeFile::Interval::add_interval(const void *start, unsigned len) { |
250 | 0 | add_interval(ptr_udiff_bytes(start, base), len); |
251 | 0 | } |
252 | | |
253 | 0 | void PeFile::Interval::add_interval(const void *start, const void *end) { |
254 | 0 | add_interval(ptr_udiff_bytes(start, base), ptr_udiff_bytes(end, start)); |
255 | 0 | } |
256 | | |
257 | 0 | void PeFile::Interval::add_interval(const Interval *other) { |
258 | 0 | for (unsigned ic = 0; ic < other->ivnum; ic++) |
259 | 0 | add_interval(other->ivarr[ic].start, other->ivarr[ic].len); |
260 | 0 | } |
261 | | |
262 | 0 | void PeFile::Interval::flatten() { |
263 | 0 | if (!ivnum) |
264 | 0 | return; |
265 | 0 | upx_qsort(ivarr, ivnum, sizeof(ivarr[0]), Interval::compare); |
266 | 0 | for (unsigned ic = 0; ic < ivnum - 1; ic++) { |
267 | 0 | unsigned jc; |
268 | 0 | for (jc = ic + 1; jc < ivnum && ivarr[ic].start + ivarr[ic].len >= ivarr[jc].start; jc++) |
269 | 0 | if (ivarr[ic].start + ivarr[ic].len < ivarr[jc].start + ivarr[jc].len) |
270 | 0 | ivarr[ic].len = ivarr[jc].start + ivarr[jc].len - ivarr[ic].start; |
271 | 0 | if (jc > ic + 1) { |
272 | 0 | memmove(ivarr + ic + 1, ivarr + jc, sizeof(interval) * (ivnum - jc)); |
273 | 0 | ivnum -= jc - ic - 1; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | 0 | void PeFile::Interval::clear() { |
279 | 0 | for (unsigned ic = 0; ic < ivnum; ic++) |
280 | 0 | memset(base + ivarr[ic].start, 0, ivarr[ic].len); |
281 | 0 | } |
282 | | |
283 | 0 | void PeFile::Interval::dump() const { |
284 | 0 | printf("%d intervals:\n", ivnum); |
285 | 0 | for (unsigned ic = 0; ic < ivnum; ic++) |
286 | 0 | printf("%x %x\n", ivarr[ic].start, ivarr[ic].len); |
287 | 0 | } |
288 | | |
289 | | /************************************************************************* |
290 | | // relocation handling |
291 | | **************************************************************************/ |
292 | | |
293 | | // do NOT allow --force to override reloc checks |
294 | | static constexpr bool ALWAYS_CHECK_STRICT_RELOCS = true; |
295 | | |
296 | 4 | void PeFile::Reloc::RelocationBlock::reset() noexcept { |
297 | 4 | rel = nullptr; // SPAN_0 |
298 | 4 | rel1 = nullptr; // SPAN_0 |
299 | 4 | count = 0; |
300 | 4 | } |
301 | | |
302 | | // TODO later: remove this in-place memory optimization hack and use an extra array |
303 | | static constexpr size_t RELOC_INPLACE_OFFSET = 64 * 1024; |
304 | | |
305 | | static constexpr size_t RELOC_ENTRY_SIZE = 5; // encoded size in bytes; actual encoding is private |
306 | 18.7k | static void reloc_entry_encode(SPAN_P(byte) buf, unsigned pos, unsigned reloc_type) { |
307 | 18.7k | if (reloc_type == 0 || reloc_type >= 16) |
308 | 0 | throwCantPack("bad reloc_type %#x %u", pos, reloc_type); |
309 | 18.7k | set_ne32(buf, pos); |
310 | 18.7k | buf[4] = (upx_uint8_t) reloc_type; |
311 | 18.7k | } |
312 | 18.7k | static void reloc_entry_decode(SPAN_P(const byte) buf, unsigned *pos, unsigned *reloc_type) { |
313 | 18.7k | *pos = get_ne32(buf); |
314 | 18.7k | *reloc_type = buf[4]; |
315 | 18.7k | assert(*reloc_type > 0 && *reloc_type < 16); |
316 | 18.7k | } |
317 | 122k | static int __acc_cdecl_qsort reloc_entry_compare(const void *a, const void *b) { |
318 | 122k | const unsigned pos1 = get_ne32(a); |
319 | 122k | const unsigned pos2 = get_ne32(b); |
320 | 122k | if (pos1 != pos2) |
321 | 122k | return pos1 < pos2 ? -1 : 1; |
322 | 0 | const unsigned reloc_type1 = ((const upx_uint8_t *) a)[4]; |
323 | 0 | const unsigned reloc_type2 = ((const upx_uint8_t *) b)[4]; |
324 | 0 | if (reloc_type1 != reloc_type2) |
325 | 0 | return reloc_type1 < reloc_type2 ? -1 : 1; |
326 | 0 | return 0; |
327 | 0 | } |
328 | | |
329 | 2 | PeFile::Reloc::~Reloc() noexcept { |
330 | 2 | COMPILE_TIME_ASSERT(sizeof(BaseReloc) == 8) |
331 | 2 | COMPILE_TIME_ASSERT_ALIGNED1(BaseReloc) |
332 | 2 | if (start_did_alloc) // don't leak memory on exceptions |
333 | 0 | delete[] start; |
334 | 2 | } |
335 | | |
336 | | // constructor for compression only |
337 | 0 | PeFile::Reloc::Reloc(byte *ptr, unsigned bytes) { |
338 | 0 | assert(opt->cmd == CMD_COMPRESS); |
339 | 0 | start_size_in_bytes = mem_size(1, bytes); |
340 | 0 | start = ptr; |
341 | 0 | initSpans(); |
342 | | // fill counts |
343 | 0 | unsigned pos, reloc_type; |
344 | 0 | while (next(pos, reloc_type)) |
345 | 0 | counts[reloc_type]++; |
346 | 0 | } |
347 | | |
348 | 2 | PeFile::Reloc::Reloc(unsigned relocnum) { |
349 | 2 | start_size_in_bytes = mem_size(RELOC_ENTRY_SIZE, relocnum, RELOC_INPLACE_OFFSET, 8192); |
350 | 2 | start = new byte[start_size_in_bytes]; // => transfer ownership to oxrelocs[] in finish() |
351 | 2 | start_did_alloc = true; |
352 | 2 | initSpans(); |
353 | 2 | } |
354 | | |
355 | 2 | void PeFile::Reloc::initSpans() { |
356 | 2 | start_buf = SPAN_S_MAKE(byte, start, start_size_in_bytes); // => now is a SPAN_S |
357 | 2 | rb.rel = SPAN_TYPE_CAST(BaseReloc, start_buf); // SPAN_0 |
358 | 2 | rb.rel1 = SPAN_TYPE_CAST(LE16, start_buf); // SPAN_0 |
359 | 2 | rb.reset(); |
360 | 2 | } |
361 | | |
362 | | // explicitly check values so that we get better error messages (instead of a cryptic SPAN failure) |
363 | 0 | bool PeFile::Reloc::readFromRelocationBlock(byte *next_rb) { // set rb |
364 | 0 | assert(!start_did_alloc); |
365 | 0 | const unsigned off = ptr_udiff_bytes(next_rb, start); |
366 | 0 | assert((off & 1) == 0); |
367 | 0 | rb.reset(); |
368 | 0 | if (off >= start_size_in_bytes) { // permissive: use ">=" instead of strict "==" |
369 | 0 | if (off > start_size_in_bytes) { |
370 | | // MAYBE TODO later: could add a warning here? |
371 | 0 | } |
372 | 0 | return false; // EOF |
373 | 0 | } |
374 | 0 | if (start_size_in_bytes - off < 8) |
375 | 0 | throwCantPack("relocs overflow"); |
376 | 0 | const unsigned sob = get_le32(start_buf + (off + 4)); // size_of_block |
377 | 0 | #if 1 |
378 | | // ignore a dubious single empty relocation block with sob == 0 |
379 | 0 | if (sob == 0 && (off == 0 && start_size_in_bytes == 8)) |
380 | 0 | return false; // EOF |
381 | 0 | #endif |
382 | 0 | if (ALWAYS_CHECK_STRICT_RELOCS) { |
383 | 0 | if (sob < 8) |
384 | 0 | throwCantPack("bad reloc size_of_block %u", sob); |
385 | 0 | if (start_size_in_bytes - off < sob) |
386 | 0 | throwCantPack("overflow reloc size_of_block %u", sob); |
387 | 0 | if ((sob & 1) != 0) |
388 | 0 | throwCantPack("odd reloc size_of_block %u", sob); |
389 | 0 | } else if (!opt->force) { |
390 | 0 | if (sob < 8) |
391 | 0 | throwCantPack("bad reloc size_of_block %u (try --force)", sob); |
392 | 0 | if (start_size_in_bytes - off < sob) |
393 | 0 | throwCantPack("overflow reloc size_of_block %u (try --force)", sob); |
394 | 0 | if ((sob & 1) != 0) |
395 | 0 | throwCantPack("odd reloc size_of_block %u (try --force)", sob); |
396 | 0 | } |
397 | | // success |
398 | 0 | rb.rel = (BaseReloc *) next_rb; // SPAN checked |
399 | 0 | rb.rel1 = (LE16 *) (next_rb + 8); // SPAN checked |
400 | 0 | rb.count = sob < 8 ? 0 : (sob - 8) / sizeof(LE16); |
401 | 0 | return true; |
402 | 0 | } |
403 | | |
404 | 0 | bool PeFile::Reloc::next(unsigned &result_pos, unsigned &result_reloc_type) { |
405 | 0 | assert(!start_did_alloc); |
406 | 0 | for (;;) { |
407 | | // search current block |
408 | 0 | while (rb.count > 0) { |
409 | 0 | rb.count -= 1; |
410 | 0 | const unsigned value = *rb.rel1++; |
411 | 0 | result_pos = rb.rel->virtual_address + (value & 0xfff); |
412 | 0 | result_reloc_type = (value >> 12) & 0xf; |
413 | 0 | NO_printf("Reloc::next %#x %d\n", result_pos, result_reloc_type); |
414 | 0 | if (result_reloc_type != IMAGE_REL_BASED_IGNORE) |
415 | 0 | return true; // success |
416 | 0 | } |
417 | | // advance to next block |
418 | 0 | byte *next_rb = (rb.rel == nullptr) ? start : (byte *) raw_bytes(rb.rel1, 0); |
419 | 0 | if (!readFromRelocationBlock(next_rb)) { |
420 | 0 | rb.reset(); // rewind |
421 | 0 | return false; // EOF |
422 | 0 | } |
423 | 0 | } |
424 | 0 | } |
425 | | |
426 | 18.7k | void PeFile::Reloc::add_reloc(unsigned pos, unsigned reloc_type) { |
427 | 18.7k | assert(start_did_alloc); |
428 | | // assert(reloc_type != IMAGE_REL_BASED_IGNORE); |
429 | 18.7k | if (reloc_type == IMAGE_REL_BASED_IGNORE) |
430 | 0 | return; |
431 | 18.7k | auto entry_ptr = start_buf + mem_size(RELOC_ENTRY_SIZE, counts[0], RELOC_INPLACE_OFFSET); |
432 | 18.7k | reloc_entry_encode(entry_ptr, pos, reloc_type); |
433 | 18.7k | counts[0] += 1; |
434 | 18.7k | } |
435 | | |
436 | 2 | void PeFile::Reloc::finish(byte *(&result_ptr), unsigned &result_size) { |
437 | 2 | assert(start_did_alloc); |
438 | | // sort in-place relocs |
439 | 2 | upx_qsort( |
440 | 2 | raw_index_bytes(start_buf, RELOC_INPLACE_OFFSET, mem_size(RELOC_ENTRY_SIZE, counts[0])), |
441 | 2 | counts[0], RELOC_ENTRY_SIZE, reloc_entry_compare); |
442 | | |
443 | 193 | auto finish_block = [](SPAN_S(BaseReloc) rel) -> byte * { |
444 | 193 | unsigned sob = rel->size_of_block; |
445 | 193 | assert(sob >= 10 && (sob & 1) == 0); |
446 | 193 | auto end = SPAN_TYPE_CAST(byte, rel) + sob; |
447 | 379 | while ((sob & 3) != 0) { // UPX: we want align by 4 here |
448 | 186 | *end++ = 0; // clear byte (i.e. write IMAGE_REL_BASED_IGNORE) |
449 | 186 | sob += 1; |
450 | 186 | } |
451 | 193 | rel->size_of_block = sob; |
452 | 193 | return raw_bytes(end, 0); |
453 | 193 | }; |
454 | | |
455 | 2 | rb.reset(); |
456 | 2 | unsigned prev_pos = 0; |
457 | 2 | unsigned current_page = 0; |
458 | 18.7k | for (unsigned ic = 0; ic < counts[0]; ic++) { |
459 | 18.7k | const auto entry_ptr = start_buf + mem_size(RELOC_ENTRY_SIZE, ic, RELOC_INPLACE_OFFSET); |
460 | 18.7k | unsigned pos, reloc_type; |
461 | 18.7k | reloc_entry_decode(entry_ptr, &pos, &reloc_type); |
462 | 18.7k | if (ic > 0 && pos == prev_pos) { |
463 | 0 | if (ALWAYS_CHECK_STRICT_RELOCS) |
464 | 0 | throwCantPack("duplicate relocs"); |
465 | 0 | else if (!opt->force) |
466 | 0 | throwCantPack("duplicate relocs (try --force)"); |
467 | 0 | } |
468 | 18.7k | prev_pos = pos; |
469 | 18.7k | if (ic == 0 || pos - current_page >= 0x1000) { |
470 | 193 | current_page = pos & ~0xfff; // page start |
471 | | // prepare next block for writing |
472 | 193 | byte *next_rb = (rb.rel == nullptr) ? start : finish_block(rb.rel); |
473 | 193 | rb.rel = (BaseReloc *) next_rb; |
474 | 193 | rb.rel1 = (LE16 *) (next_rb + 8); |
475 | 193 | rb.rel->virtual_address = current_page; |
476 | 193 | rb.rel->size_of_block = 8; |
477 | 193 | } |
478 | | // check for in-place overflow |
479 | 18.7k | if (ptr_diff_bytes(rb.rel1, entry_ptr) >= 0) { |
480 | | // info: if this is indeed a valid file we must increase RELOC_INPLACE_OFFSET |
481 | 0 | throwCantPack("too many inplace relocs"); |
482 | 0 | } |
483 | | // write LE16 IMAGE_BASE_RELOCATION relocation |
484 | 18.7k | *rb.rel1++ = (reloc_type << 12) | (pos & 0xfff); |
485 | 18.7k | rb.rel->size_of_block += 2; |
486 | 18.7k | } |
487 | 2 | result_size = 0; // result_size can be 0 in 64-bit mode |
488 | 2 | if (rb.rel != nullptr) |
489 | 2 | result_size = ptr_udiff_bytes(finish_block(rb.rel), start); |
490 | 2 | assert((result_size & 3) == 0); |
491 | | // transfer ownership |
492 | 2 | assert(start_did_alloc); |
493 | 0 | result_ptr = start; |
494 | 2 | start_did_alloc = false; |
495 | 2 | #if DEBUG || 1 // safety, as we are really finished |
496 | 2 | ptr_invalidate_and_poison(start); |
497 | 2 | start_size_in_bytes = 0; |
498 | 2 | SPAN_INVALIDATE(start_buf); |
499 | 2 | SPAN_INVALIDATE(rb.rel); |
500 | 2 | SPAN_INVALIDATE(rb.rel1); |
501 | 2 | rb.count = 0xdeaddead; |
502 | 2 | #endif |
503 | 2 | } |
504 | | |
505 | 0 | void PeFile32::processRelocs() { // pass1 |
506 | 0 | big_relocs = 0; |
507 | |
|
508 | 0 | const unsigned skip1 = IDADDR(PEDIR_BASERELOC); |
509 | 0 | const unsigned take1 = IDSIZE(PEDIR_BASERELOC); |
510 | 0 | Reloc rel(ibuf.subref("bad reloc %#x", skip1, take1), take1); |
511 | 0 | const unsigned *const counts = rel.getcounts(); |
512 | 0 | unsigned relocnum = 0; |
513 | |
|
514 | 0 | for (unsigned ic = 1; ic < 16; ic++) |
515 | 0 | relocnum += counts[ic]; |
516 | 0 | for (unsigned ic = 0; ic < 16; ic++) |
517 | 0 | NO_printf("reloc counts[%u] %u\n", ic, counts[ic]); |
518 | |
|
519 | 0 | if (opt->win32_pe.strip_relocs || relocnum == 0) { |
520 | 0 | if (IDSIZE(PEDIR_BASERELOC)) { |
521 | 0 | ibuf.fill(IDADDR(PEDIR_BASERELOC), IDSIZE(PEDIR_BASERELOC), FILLVAL); |
522 | 0 | const unsigned old_objs = ih.objects; |
523 | 0 | ih.objects = tryremove(IDADDR(PEDIR_BASERELOC), ih.objects); |
524 | 0 | if (old_objs != ih.objects && 1) { // was removed |
525 | 0 | IDADDR(PEDIR_BASERELOC) = 0; |
526 | 0 | IDSIZE(PEDIR_BASERELOC) = 0; |
527 | 0 | const unsigned oam1 = ih.objectalign - 1; |
528 | 0 | ih.imagesize = |
529 | 0 | (isection[-1 + ih.objects].vsize + isection[-1 + ih.objects].vaddr + oam1) & |
530 | 0 | ~oam1; |
531 | 0 | } |
532 | 0 | } |
533 | 0 | mb_orelocs.alloc(1); |
534 | 0 | mb_orelocs.clear(); |
535 | 0 | orelocs = SPAN_S_MAKE(byte, mb_orelocs); // => orelocs now is a SPAN_S |
536 | 0 | sorelocs = 0; |
537 | 0 | return; |
538 | 0 | } |
539 | | |
540 | 0 | for (unsigned ic = IMAGE_REL_BASED_HIGHADJ; ic < 16; ic++) |
541 | 0 | if (counts[ic]) |
542 | 0 | infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]); |
543 | |
|
544 | 0 | LE32 *fix[4] = {}; |
545 | 0 | auto fix_deleter = upx::ArrayDeleter(fix, 0); // don't leak memory |
546 | 0 | for (unsigned ic = 0; ic <= IMAGE_REL_BASED_HIGHLOW; ic++) { |
547 | 0 | fix[ic] = New(LE32, counts[ic]); |
548 | 0 | fix_deleter.count += 1; |
549 | 0 | } |
550 | |
|
551 | 0 | unsigned xcounts[4] = {}; |
552 | 0 | memset(xcounts, 0, sizeof(xcounts)); |
553 | | |
554 | | // prepare sorting |
555 | 0 | unsigned pos, reloc_type; |
556 | 0 | while (rel.next(pos, reloc_type)) { |
557 | | // FIXME add check for relocations which try to modify the |
558 | | // PE header or other relocation records |
559 | 0 | if (pos >= ih.imagesize) |
560 | 0 | continue; // skip out-of-bounds record |
561 | 0 | if (reloc_type <= IMAGE_REL_BASED_HIGHLOW) |
562 | 0 | fix[reloc_type][xcounts[reloc_type]++] = pos - rvamin; |
563 | 0 | } |
564 | | |
565 | | // remove duplicated records |
566 | 0 | for (unsigned ic = 1; ic <= IMAGE_REL_BASED_HIGHLOW; ic++) { |
567 | 0 | upx_qsort(fix[ic], xcounts[ic], 4, le32_compare); |
568 | 0 | unsigned prev = ~0u; |
569 | 0 | unsigned jc = 0; |
570 | 0 | for (unsigned kc = 0; kc < xcounts[ic]; kc++) |
571 | 0 | if (fix[ic][kc] != prev) |
572 | 0 | prev = fix[ic][jc++] = fix[ic][kc]; |
573 | |
|
574 | 0 | NO_printf("reloc xcounts[%u] %u->%u\n", ic, xcounts[ic], jc); |
575 | 0 | xcounts[ic] = jc; |
576 | 0 | } |
577 | | |
578 | | // preprocess "type 3" relocation records |
579 | 0 | for (unsigned ic = 0; ic < xcounts[IMAGE_REL_BASED_HIGHLOW]; ic++) { |
580 | 0 | pos = fix[3][ic] + rvamin; |
581 | 0 | unsigned w = get_le32(ibuf.subref("bad reloc type 3 %#x", pos, sizeof(LE32))); |
582 | 0 | set_le32(ibuf + pos, w - ih.imagebase - rvamin); |
583 | 0 | } |
584 | |
|
585 | 0 | ibuf.fill(IDADDR(PEDIR_BASERELOC), IDSIZE(PEDIR_BASERELOC), FILLVAL); |
586 | 0 | mb_orelocs.alloc(mem_size(4, relocnum, 8192)); // 8192 - safety |
587 | 0 | orelocs = SPAN_S_MAKE(byte, mb_orelocs); // => orelocs now is a SPAN_S |
588 | 0 | sorelocs = optimizeReloc(xcounts[3], (byte *) fix[3], orelocs, ibuf + rvamin, ibufgood - rvamin, |
589 | 0 | 32, true, &big_relocs); |
590 | | |
591 | | // Malware that hides behind UPX often has PE header info that is |
592 | | // deliberately corrupt. Sometimes it is even tuned to cause us trouble! |
593 | | // Use an extra check to avoid AccessViolation (SIGSEGV) when appending |
594 | | // the relocs into one array. |
595 | 0 | if ((sizeof(LE32) * relocnum + 8192) < |
596 | 0 | (sorelocs + |
597 | 0 | sizeof(LE32) * (2 + xcounts[IMAGE_REL_BASED_LOW] + xcounts[IMAGE_REL_BASED_HIGH]))) |
598 | 0 | throwCantUnpack("Invalid relocs"); |
599 | | |
600 | | // append relocs type "LOW" then "HIGH" |
601 | 0 | for (unsigned ic = IMAGE_REL_BASED_LOW; ic >= IMAGE_REL_BASED_HIGH; ic--) { |
602 | 0 | memcpy(orelocs + sorelocs, fix[ic], sizeof(LE32) * xcounts[ic]); |
603 | 0 | sorelocs += sizeof(LE32) * xcounts[ic]; |
604 | 0 | set_le32(orelocs + sorelocs, 0); |
605 | 0 | if (xcounts[ic]) { |
606 | 0 | sorelocs += 4; |
607 | 0 | big_relocs |= 2 * ic; |
608 | 0 | } |
609 | 0 | } |
610 | |
|
611 | 0 | info("Relocations: original size: %u bytes, preprocessed size: %u bytes", |
612 | 0 | (unsigned) IDSIZE(PEDIR_BASERELOC), sorelocs); |
613 | 0 | } |
614 | | |
615 | | // FIXME - this is too similar to PeFile32::processRelocs |
616 | 0 | void PeFile64::processRelocs() { // pass1 |
617 | 0 | big_relocs = 0; |
618 | |
|
619 | 0 | const unsigned skip1 = IDADDR(PEDIR_BASERELOC); |
620 | 0 | const unsigned take1 = IDSIZE(PEDIR_BASERELOC); |
621 | 0 | Reloc rel(ibuf.subref("bad reloc %#x", skip1, take1), take1); |
622 | 0 | const unsigned *const counts = rel.getcounts(); |
623 | 0 | unsigned relocnum = 0; |
624 | |
|
625 | 0 | for (unsigned ic = 1; ic < 16; ic++) |
626 | 0 | relocnum += counts[ic]; |
627 | 0 | for (unsigned ic = 0; ic < 16; ic++) |
628 | 0 | NO_printf("reloc counts[%u] %u\n", ic, counts[ic]); |
629 | |
|
630 | 0 | if (opt->win32_pe.strip_relocs || relocnum == 0) { |
631 | 0 | if (IDSIZE(PEDIR_BASERELOC)) { |
632 | 0 | ibuf.fill(IDADDR(PEDIR_BASERELOC), IDSIZE(PEDIR_BASERELOC), FILLVAL); |
633 | 0 | const unsigned old_objs = ih.objects; |
634 | 0 | ih.objects = tryremove(IDADDR(PEDIR_BASERELOC), ih.objects); |
635 | 0 | if (old_objs != ih.objects && 1) { // was removed |
636 | 0 | IDADDR(PEDIR_BASERELOC) = 0; |
637 | 0 | IDSIZE(PEDIR_BASERELOC) = 0; |
638 | 0 | const unsigned oam1 = ih.objectalign - 1; |
639 | 0 | ih.imagesize = |
640 | 0 | (isection[-1 + ih.objects].vsize + isection[-1 + ih.objects].vaddr + oam1) & |
641 | 0 | ~oam1; |
642 | 0 | } |
643 | 0 | } |
644 | 0 | mb_orelocs.alloc(1); |
645 | 0 | mb_orelocs.clear(); |
646 | 0 | orelocs = SPAN_S_MAKE(byte, mb_orelocs); // => orelocs now is a SPAN_S |
647 | 0 | sorelocs = 0; |
648 | 0 | return; |
649 | 0 | } |
650 | | |
651 | 0 | for (unsigned ic = 0; ic < 16; ic++) |
652 | 0 | if (ic != IMAGE_REL_BASED_DIR64 && counts[ic]) |
653 | 0 | infoWarning("skipping unsupported relocation type %d (%d)", ic, counts[ic]); |
654 | |
|
655 | 0 | LE32 *fix[16] = {}; |
656 | 0 | auto fix_deleter = upx::ArrayDeleter(fix, 0); // don't leak memory |
657 | 0 | for (unsigned ic = 0; ic < 16; ic++) { |
658 | 0 | fix[ic] = New(LE32, counts[ic]); |
659 | 0 | fix_deleter.count += 1; |
660 | 0 | } |
661 | |
|
662 | 0 | unsigned xcounts[16] = {}; |
663 | 0 | memset(xcounts, 0, sizeof(xcounts)); |
664 | | |
665 | | // prepare sorting |
666 | 0 | unsigned pos, reloc_type; |
667 | 0 | while (rel.next(pos, reloc_type)) { |
668 | | // FIXME add check for relocations which try to modify the |
669 | | // PE header or other relocation records |
670 | 0 | if (pos >= ih.imagesize) |
671 | 0 | continue; // skip out-of-bounds record |
672 | 0 | if (reloc_type < 16) |
673 | 0 | fix[reloc_type][xcounts[reloc_type]++] = pos - rvamin; |
674 | 0 | } |
675 | | |
676 | | // remove duplicated records |
677 | 0 | for (unsigned ic = 1; ic < 16; ic++) { |
678 | 0 | upx_qsort(fix[ic], xcounts[ic], 4, le32_compare); |
679 | 0 | unsigned prev = ~0u; |
680 | 0 | unsigned jc = 0; |
681 | 0 | for (unsigned kc = 0; kc < xcounts[ic]; kc++) |
682 | 0 | if (fix[ic][kc] != prev) |
683 | 0 | prev = fix[ic][jc++] = fix[ic][kc]; |
684 | |
|
685 | 0 | NO_printf("xcounts[%u] %u->%u\n", ic, xcounts[ic], jc); |
686 | 0 | xcounts[ic] = jc; |
687 | 0 | } |
688 | | |
689 | | // preprocess "type 10" relocation records |
690 | 0 | for (unsigned ic = 0; ic < xcounts[IMAGE_REL_BASED_DIR64]; ic++) { |
691 | 0 | pos = fix[IMAGE_REL_BASED_DIR64][ic] + rvamin; |
692 | 0 | upx_uint64_t w = get_le64(ibuf.subref("bad reloc 10 %#x", pos, sizeof(LE64))); |
693 | 0 | set_le64(ibuf + pos, w - ih.imagebase - rvamin); |
694 | 0 | } |
695 | |
|
696 | 0 | ibuf.fill(IDADDR(PEDIR_BASERELOC), IDSIZE(PEDIR_BASERELOC), FILLVAL); |
697 | 0 | mb_orelocs.alloc(mem_size(4, relocnum, 8192)); // 8192 - safety |
698 | 0 | orelocs = SPAN_S_MAKE(byte, mb_orelocs); // => orelocs now is a SPAN_S |
699 | 0 | sorelocs = optimizeReloc(xcounts[IMAGE_REL_BASED_DIR64], (byte *) fix[IMAGE_REL_BASED_DIR64], |
700 | 0 | orelocs, ibuf + rvamin, ibufgood - rvamin, 64, true, &big_relocs); |
701 | |
|
702 | 0 | info("Relocations: original size: %u bytes, preprocessed size: %u bytes", |
703 | 0 | (unsigned) IDSIZE(PEDIR_BASERELOC), sorelocs); |
704 | 0 | } |
705 | | |
706 | | /************************************************************************* |
707 | | // import handling |
708 | | **************************************************************************/ |
709 | | |
710 | 229 | LE32 &PeFile::IDSIZE(unsigned x) { return iddirs[x].size; } |
711 | 150 | LE32 &PeFile::IDADDR(unsigned x) { return iddirs[x].vaddr; } |
712 | 174 | LE32 &PeFile::ODSIZE(unsigned x) { return oddirs[x].size; } |
713 | 192 | LE32 &PeFile::ODADDR(unsigned x) { return oddirs[x].vaddr; } |
714 | 0 | const LE32 &PeFile::IDSIZE(unsigned x) const { return iddirs[x].size; } |
715 | 0 | const LE32 &PeFile::IDADDR(unsigned x) const { return iddirs[x].vaddr; } |
716 | | |
717 | | /* |
718 | | ImportLinker: 32 and 64 bit import table building. |
719 | | Import entries (dll name + proc name/ordinal pairs) can be |
720 | | added in arbitrary order. |
721 | | |
722 | | Internally it works by creating sections with special names, |
723 | | and adding relocation entries between those sections. The special |
724 | | names ensure that when the import table is built in the memory |
725 | | from those sections, a correct table can be generated simply by |
726 | | sorting the sections by name, and adding all of them to the output |
727 | | in the sorted order. |
728 | | */ |
729 | | |
730 | | class PeFile::ImportLinker final : public ElfLinkerAMD64 { |
731 | | // temporary string owner, deletes on destruction |
732 | | struct TStr final : private upx::noncopyable { |
733 | 0 | explicit TStr(char *str) noexcept : s(str) {} |
734 | 0 | ~TStr() noexcept { delete[] s; } // delete! |
735 | 0 | operator char *() noexcept { return s; } |
736 | 0 | operator const char *() const noexcept { return s; } |
737 | | private: |
738 | | char *s; |
739 | | }; |
740 | | |
741 | | // encoding of dll and proc names are required, so that our special |
742 | | // control characters in the name of sections can work as intended |
743 | 0 | static void encode_name(SPAN_P(const char) name, SPAN_S(char) buf) { |
744 | 0 | while (*name) { |
745 | 0 | *buf++ = 'a' + ((*name >> 4) & 0xf); |
746 | 0 | *buf++ = 'a' + (*name & 0xf); |
747 | 0 | name++; |
748 | 0 | } |
749 | 0 | *buf = 0; |
750 | 0 | } |
751 | | |
752 | 0 | static char *name_for_dll(const char *dll, char first_char) { |
753 | 0 | assert(dll != nullptr); |
754 | 0 | const unsigned l = strlen(dll); |
755 | 0 | assert(l > 0); |
756 | 0 | const unsigned new_size = 1 + 3 * l + 1; |
757 | 0 | char *const new_name = New(char, new_size); |
758 | 0 | SPAN_S_VAR(char, const name, new_name, new_size); |
759 | 0 | name[0] = first_char; |
760 | 0 | SPAN_S_VAR(char, n, name + (1 + 2 * l)); |
761 | 0 | do { |
762 | 0 | *n++ = tolower((uchar) *dll); |
763 | 0 | } while (*dll++); |
764 | 0 | encode_name(new_name + (1 + 2 * l), name + 1); |
765 | 0 | return new_name; |
766 | 0 | } |
767 | | |
768 | 0 | static char *name_for_proc(const char *dll, const char *proc, char first_char, char separator) { |
769 | 0 | const unsigned new_size = 1 + 2 * strlen(dll) + 1 + 2 * strlen(proc) + 1 + 1; |
770 | 0 | TStr dll_name(name_for_dll(dll, first_char)); |
771 | 0 | char *const new_name = New(char, new_size); |
772 | 0 | SPAN_S_VAR(char, const name, new_name, new_size); |
773 | 0 | upx_safe_snprintf(new_name, new_size, "%s%c", (const char *) dll_name, separator); |
774 | 0 | encode_name(proc, name + strlen(name)); |
775 | 0 | return new_name; |
776 | 0 | } |
777 | | |
778 | | static const char zeros[sizeof(import_desc)]; |
779 | | |
780 | | enum { |
781 | | // the order of identifiers is very important below!! |
782 | | descriptor_id = 'D', |
783 | | thunk_id, |
784 | | dll_name_id, |
785 | | proc_name_id, |
786 | | ordinal_id, |
787 | | |
788 | | thunk_separator_first, |
789 | | thunk_separator, |
790 | | thunk_separator_last, |
791 | | procname_separator, |
792 | | }; |
793 | | |
794 | | unsigned thunk_size; // 4 or 8 bytes |
795 | | |
796 | 0 | void add_import(const char *dll, const char *proc, unsigned ordinal) { |
797 | 0 | TStr sdll(name_for_dll(dll, dll_name_id)); |
798 | 0 | TStr desc_name(name_for_dll(dll, descriptor_id)); |
799 | |
|
800 | 0 | char tsep = thunk_separator; |
801 | 0 | if (findSection(sdll, false) == nullptr) { |
802 | 0 | tsep = thunk_separator_first; |
803 | 0 | addSection(sdll, dll, strlen(dll) + 1, 0); // name of the dll |
804 | 0 | addSymbol(sdll, sdll, 0); |
805 | |
|
806 | 0 | addSection(desc_name, zeros, sizeof(zeros), 0); // descriptor |
807 | 0 | addRelocation(desc_name, offsetof(import_desc, dllname), "R_X86_64_32", sdll, 0); |
808 | 0 | } |
809 | 0 | TStr thunk(proc == nullptr ? name_for_dll(dll, thunk_id) |
810 | 0 | : name_for_proc(dll, proc, thunk_id, tsep)); |
811 | |
|
812 | 0 | if (findSection(thunk, false) != nullptr) |
813 | 0 | return; // we already have this dll/proc |
814 | 0 | addSection(thunk, zeros, thunk_size, 0); |
815 | 0 | addSymbol(thunk, thunk, 0); |
816 | 0 | if (tsep == thunk_separator_first) { |
817 | 0 | addRelocation(desc_name, offsetof(import_desc, iat), "R_X86_64_32", thunk, 0); |
818 | |
|
819 | 0 | TStr last_thunk(name_for_proc(dll, "X", thunk_id, thunk_separator_last)); |
820 | 0 | addSection(last_thunk, zeros, thunk_size, 0); |
821 | 0 | } |
822 | |
|
823 | 0 | const char *reltype = thunk_size == 4 ? "R_X86_64_32" : "R_X86_64_64"; |
824 | 0 | if (ordinal != 0u) { |
825 | 0 | addRelocation(thunk, 0, reltype, "*UND*", ordinal | (1ull << (thunk_size * 8 - 1))); |
826 | 0 | } else if (proc != nullptr) { |
827 | 0 | TStr proc_name(name_for_proc(dll, proc, proc_name_id, procname_separator)); |
828 | 0 | addSection(proc_name, zeros, 2, 1); // 2 bytes of word aligned "hint" |
829 | 0 | addSymbol(proc_name, proc_name, 0); |
830 | 0 | addRelocation(thunk, 0, reltype, proc_name, 0); |
831 | |
|
832 | 0 | strcat(proc_name, "X"); |
833 | 0 | addSection(proc_name, proc, strlen(proc), 0); // the name of the symbol |
834 | 0 | } else |
835 | 0 | infoWarning("empty import: %s", dll); |
836 | 0 | } |
837 | | |
838 | 0 | static int __acc_cdecl_qsort compare(const void *aa, const void *bb) { |
839 | 0 | const Section *a = *(const Section *const *) aa; |
840 | 0 | const Section *b = *(const Section *const *) bb; |
841 | 0 | if (a->sort_id == b->sort_id) // identical object, poor qsort() implementation |
842 | 0 | return 0; |
843 | 0 | int rc = strcmp(a->name, b->name); |
844 | 0 | if (rc != 0) |
845 | 0 | return rc; |
846 | | // What could remain? |
847 | | // make sort order deterministic |
848 | 0 | return a->sort_id < b->sort_id ? -1 : 1; |
849 | 0 | } |
850 | | |
851 | 0 | virtual void alignCode(unsigned len) override { alignWithByte(len, 0); } |
852 | | |
853 | 0 | const Section *getThunk(const char *dll, const char *proc, char tsep) const { |
854 | 0 | assert(dll); |
855 | 0 | assert(proc); |
856 | 0 | TStr thunk(name_for_proc(dll, proc, thunk_id, tsep)); |
857 | 0 | return findSection(thunk, false); |
858 | 0 | } |
859 | | |
860 | | public: |
861 | 0 | explicit ImportLinker(unsigned thunk_size_) : thunk_size(thunk_size_) { |
862 | 0 | assert(thunk_size == 4 || thunk_size == 8); |
863 | 0 | addSection("*UND*", nullptr, 0, 0); |
864 | 0 | addSymbol("*UND*", "*UND*", 0); |
865 | 0 | addSection("*ZSTART", nullptr, 0, 0); |
866 | 0 | addSymbol("*ZSTART", "*ZSTART", 0); |
867 | 0 | Section *s = addSection("Dzero", zeros, sizeof(import_desc), 0); |
868 | 0 | assert(s->name[0] == descriptor_id); |
869 | | |
870 | | // one trailing 00 byte after the last proc name |
871 | 0 | addSection("Zzero", zeros, 1, 0); |
872 | 0 | } |
873 | | |
874 | | template <typename C> |
875 | 0 | void add_import(const C *dll, unsigned ordinal) { |
876 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" |
877 | 0 | assert(ordinal < 0x10000); |
878 | 0 | char ord[1 + 5 + 1]; |
879 | 0 | upx_safe_snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal); |
880 | 0 | add_import((const char *) dll, ordinal ? ord : nullptr, ordinal); |
881 | 0 | } Unexecuted instantiation: void PeFile::ImportLinker::add_import<char>(char const*, unsigned int) Unexecuted instantiation: void PeFile::ImportLinker::add_import<unsigned char>(unsigned char const*, unsigned int) |
882 | | |
883 | | template <typename C1, typename C2> |
884 | 0 | void add_import(const C1 *dll, const C2 *proc) { |
885 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C1) == 1) // "char" or "byte" |
886 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C2) == 1) // "char" or "byte" |
887 | 0 | assert(proc); |
888 | 0 | add_import((const char *) dll, (const char *) proc, 0); |
889 | 0 | } Unexecuted instantiation: void PeFile::ImportLinker::add_import<char, char>(char const*, char const*) Unexecuted instantiation: void PeFile::ImportLinker::add_import<unsigned char, unsigned char>(unsigned char const*, unsigned char const*) |
890 | | |
891 | 0 | unsigned build() { |
892 | 0 | assert(output == nullptr); |
893 | 0 | int osize = 4 + 2 * nsections; // upper limit for alignments |
894 | 0 | for (unsigned ic = 0; ic < nsections; ic++) |
895 | 0 | osize += sections[ic]->size; |
896 | 0 | output_capacity = osize; |
897 | 0 | output = New(byte, output_capacity); |
898 | 0 | outputlen = 0; |
899 | | |
900 | | // sort the sections by name before adding them all |
901 | | // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) |
902 | 0 | upx_qsort(sections, nsections, sizeof(sections[0]), ImportLinker::compare); |
903 | |
|
904 | 0 | for (unsigned ic = 0; ic < nsections; ic++) |
905 | 0 | addLoader(sections[ic]->name); |
906 | 0 | addLoader("+40D"); |
907 | 0 | assert(outputlen <= osize); |
908 | | |
909 | | // OutputFile::dump("il0.imp", output, outputlen); |
910 | 0 | return outputlen; |
911 | 0 | } |
912 | | |
913 | 0 | void relocate_import(unsigned myimport) { |
914 | 0 | assert(nsections > 0); |
915 | 0 | assert(output); |
916 | 0 | defineSymbol("*ZSTART", /*0xffffffffff1000ull + 0 * */ myimport); |
917 | 0 | ElfLinkerAMD64::relocate(); |
918 | | // OutputFile::dump("il1.imp", output, outputlen); |
919 | 0 | } |
920 | | |
921 | | template <typename C1, typename C2> |
922 | 0 | upx_uint64_t getAddress(const C1 *dll, const C2 *proc) const { |
923 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C1) == 1) // "char" or "byte" |
924 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C2) == 1) // "char" or "byte" |
925 | 0 | const Section *s = getThunk((const char *) dll, (const char *) proc, thunk_separator_first); |
926 | 0 | if (s == nullptr) |
927 | 0 | s = getThunk((const char *) dll, (const char *) proc, thunk_separator); |
928 | 0 | if (s == nullptr) |
929 | 0 | throwInternalError("entry not found"); |
930 | 0 | return s->offset; |
931 | 0 | } |
932 | | |
933 | | template <typename C> |
934 | 0 | upx_uint64_t getAddress(const C *dll, unsigned ordinal) const { |
935 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" |
936 | 0 | assert(ordinal > 0 && ordinal < 0x10000); |
937 | 0 | char ord[1 + 5 + 1]; |
938 | 0 | upx_safe_snprintf(ord, sizeof(ord), "%c%05u", ordinal_id, ordinal); |
939 | 0 | const Section *s = getThunk((const char *) dll, ord, thunk_separator_first); |
940 | 0 | if (s == nullptr) |
941 | 0 | s = getThunk((const char *) dll, ord, thunk_separator); |
942 | 0 | if (s == nullptr) |
943 | 0 | throwInternalError("entry not found"); |
944 | 0 | return s->offset; |
945 | 0 | } |
946 | | |
947 | | template <typename C> |
948 | 0 | upx_uint64_t getAddress(const C *dll) const { |
949 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" |
950 | 0 | TStr sdll(name_for_dll((const char *) dll, dll_name_id)); |
951 | 0 | return findSection(sdll, true)->offset; |
952 | 0 | } |
953 | | |
954 | | template <typename C> |
955 | 0 | bool hasDll(const C *dll) const { |
956 | 0 | ACC_COMPILE_TIME_ASSERT(sizeof(C) == 1) // "char" or "byte" |
957 | 0 | TStr sdll(name_for_dll((const char *) dll, dll_name_id)); |
958 | 0 | return findSection(sdll, false) != nullptr; |
959 | 0 | } |
960 | | }; // class PeFile::ImportLinker |
961 | | |
962 | | /*static*/ const char PeFile::ImportLinker::zeros[sizeof(import_desc)] = {0}; |
963 | | |
964 | 0 | void PeFile::addKernelImport(const char *name) { ilinker->add_import(kernelDll(), name); } |
965 | | |
966 | 0 | void PeFile::addStubImports() { |
967 | 0 | addKernelImport("LoadLibraryA"); |
968 | 0 | addKernelImport("GetProcAddress"); |
969 | 0 | if (!isdll) |
970 | 0 | addKernelImport("ExitProcess"); |
971 | 0 | addKernelImport("VirtualProtect"); |
972 | 0 | } |
973 | | |
974 | 0 | void PeFile::processImports2(unsigned myimport, unsigned) { // pass 2 |
975 | 0 | COMPILE_TIME_ASSERT(sizeof(import_desc) == 20) |
976 | 0 | if (ilinker == nullptr) |
977 | 0 | return; |
978 | 0 | ilinker->relocate_import(myimport); |
979 | 0 | int len; |
980 | 0 | oimpdlls = ilinker->getLoader(&len); |
981 | 0 | assert(len == (int) soimpdlls); |
982 | | // OutputFile::dump("x1.imp", oimpdlls, soimpdlls); |
983 | 0 | } |
984 | | |
985 | | template <typename LEXX, typename ord_mask_t> |
986 | 0 | unsigned PeFile::processImports0(ord_mask_t ord_mask) { // pass 1 |
987 | 0 | if (isefi) { |
988 | 0 | if (IDSIZE(PEDIR_IMPORT)) |
989 | 0 | throwCantPack("imports not supported on EFI"); |
990 | 0 | return 0; |
991 | 0 | } |
992 | | |
993 | 0 | unsigned dllnum = 0; |
994 | 0 | const unsigned skip = IDADDR(PEDIR_IMPORT); |
995 | 0 | const unsigned take = IDSIZE(PEDIR_IMPORT); |
996 | 0 | import_desc *const im_start = (import_desc *) ibuf.subref("bad import %#x", skip, take); |
997 | 0 | if (IDADDR(PEDIR_IMPORT) != 0) { |
998 | 0 | for (const import_desc *im = im_start;; ++dllnum, ++im) { |
999 | 0 | const unsigned skip2 = ptr_udiff_bytes(im, ibuf); |
1000 | 0 | (void) ibuf.subref("bad import %#x", skip2, sizeof(*im)); |
1001 | 0 | if (im->dllname == 0) |
1002 | 0 | break; |
1003 | 0 | } |
1004 | 0 | } |
1005 | 0 | if (dllnum > 4096) // just some arbitrary limit/sanity check |
1006 | 0 | throwCantPack("too many DLL imports %u", dllnum); |
1007 | | |
1008 | 0 | struct UDll final { |
1009 | 0 | const byte *name; |
1010 | 0 | const byte *shname; |
1011 | 0 | unsigned ordinal; |
1012 | 0 | unsigned iat; |
1013 | 0 | const LEXX *lookupt; |
1014 | 0 | unsigned original_position; |
1015 | 0 | bool isk32; |
1016 | |
|
1017 | 0 | static int __acc_cdecl_qsort compare(const void *aa, const void *bb) { |
1018 | 0 | const UDll *a = *(const UDll *const *) aa; |
1019 | 0 | const UDll *b = *(const UDll *const *) bb; |
1020 | 0 | if (a->original_position == b->original_position) // identical object, poor qsort() |
1021 | 0 | return 0; |
1022 | 0 | if (a->isk32 != b->isk32) |
1023 | 0 | return a->isk32 ? -1 : 1; |
1024 | 0 | if ((*a->lookupt != 0) != (*b->lookupt != 0)) |
1025 | 0 | return (*a->lookupt != 0) ? -1 : 1; |
1026 | 0 | int rc = strcasecmp(a->name, b->name); |
1027 | 0 | if (rc != 0) |
1028 | 0 | return rc; |
1029 | 0 | if ((a->ordinal != 0) != (b->ordinal != 0)) |
1030 | 0 | return (a->ordinal != 0) ? -1 : 1; |
1031 | 0 | if (a->shname && b->shname) { |
1032 | 0 | rc = (int) (upx_safe_strlen(a->shname) - upx_safe_strlen(b->shname)); |
1033 | 0 | if (rc != 0) |
1034 | 0 | return rc; |
1035 | 0 | rc = strcmp(a->shname, b->shname); |
1036 | 0 | if (rc != 0) |
1037 | 0 | return rc; |
1038 | 0 | } else if ((a->shname != nullptr) != (b->shname != nullptr)) |
1039 | 0 | return (a->shname != nullptr) ? -1 : 1; |
1040 | | // What could remain? |
1041 | | // make sort order deterministic |
1042 | 0 | return a->original_position < b->original_position ? -1 : 1; |
1043 | 0 | } Unexecuted instantiation: PeFile::processImports0<LE32, unsigned int>(unsigned int)::UDll::compare(void const*, void const*) Unexecuted instantiation: PeFile::processImports0<LE64, unsigned long long>(unsigned long long)::UDll::compare(void const*, void const*) |
1044 | 0 | }; |
1045 | | |
1046 | | // +1 for dllnum=0 |
1047 | 0 | Array(UDll, dlls, dllnum + 1); |
1048 | 0 | Array(UDll *, idlls, dllnum + 1); |
1049 | |
|
1050 | 0 | soimport = 1024; // safety |
1051 | |
|
1052 | 0 | for (unsigned ic = 0; ic < dllnum; ic++) { |
1053 | 0 | const import_desc *const im = im_start + ic; |
1054 | 0 | idlls[ic] = dlls + ic; |
1055 | 0 | dlls[ic].name = ibuf.subref("bad dllname %#x", im->dllname, 1); |
1056 | 0 | dlls[ic].shname = nullptr; |
1057 | 0 | dlls[ic].ordinal = 0; |
1058 | 0 | dlls[ic].iat = im->iat; |
1059 | 0 | const unsigned skip2 = (im->oft ? im->oft : im->iat); |
1060 | 0 | dlls[ic].lookupt = (LEXX *) ibuf.subref("bad dll lookupt %#x", skip2, sizeof(LEXX)); |
1061 | 0 | dlls[ic].original_position = ic; |
1062 | 0 | dlls[ic].isk32 = strcasecmp(kernelDll(), dlls[ic].name) == 0; |
1063 | |
|
1064 | 0 | soimport += strlen(dlls[ic].name) + 1 + 4; |
1065 | |
|
1066 | 0 | unsigned i_tarr = 0; |
1067 | 0 | for (IPTR_VAR(const LEXX, tarr, dlls[ic].lookupt); *tarr; tarr += 1, i_tarr += 1) { |
1068 | 0 | if (0xfffdu & (*tarr >> 30)) { // UPX_RSIZE_MAX_MEM but allowing (1<<31) |
1069 | 0 | throwCantPack("bad import %s[%#x]:%#llx", dlls[ic].name, i_tarr, |
1070 | 0 | (unsigned long long) *tarr); |
1071 | 0 | } |
1072 | 0 | if (*tarr & ord_mask) { |
1073 | 0 | importbyordinal = true; |
1074 | 0 | soimport += 2; // ordinal num: 2 bytes |
1075 | 0 | dlls[ic].ordinal = *tarr & 0xffff; |
1076 | 0 | } else { |
1077 | | // it's an import by name |
1078 | 0 | IPTR_VAR(const byte, const name, ibuf + (*tarr + 2)); |
1079 | 0 | unsigned len = strlen(name); |
1080 | 0 | soimport += len + 1; |
1081 | 0 | if (dlls[ic].shname == nullptr || len < strlen(dlls[ic].shname)) |
1082 | 0 | dlls[ic].shname = ibuf + (*tarr + 2); |
1083 | 0 | } |
1084 | 0 | soimport++; // separator |
1085 | 0 | } |
1086 | 0 | } |
1087 | 0 | mb_oimport.alloc(soimport); |
1088 | 0 | mb_oimport.clear(); |
1089 | 0 | oimport = SPAN_S_MAKE(byte, mb_oimport); // => now is a SPAN_S |
1090 | | |
1091 | | // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) |
1092 | 0 | upx_qsort(idlls, dllnum, sizeof(idlls[0]), UDll::compare); |
1093 | |
|
1094 | 0 | info("Processing imports: %d DLLs", dllnum); |
1095 | 0 | for (unsigned ic = 0; ic < dllnum; ic++) { |
1096 | 0 | info(" DLL %3d %s %s", ic, idlls[ic]->name, idlls[ic]->shname); |
1097 | 0 | } |
1098 | |
|
1099 | 0 | ilinker = new ImportLinker(sizeof(LEXX)); |
1100 | | // create the new import table |
1101 | 0 | addStubImports(); |
1102 | |
|
1103 | 0 | for (unsigned ic = 0; ic < dllnum; ic++) { |
1104 | 0 | if (idlls[ic]->isk32) { |
1105 | | // for kernel32.dll we need to put all the imported |
1106 | | // ordinals into the output import table, as on |
1107 | | // some versions of windows GetProcAddress does not resolve them |
1108 | 0 | if (strcasecmp(idlls[ic]->name, "kernel32.dll")) |
1109 | 0 | continue; |
1110 | 0 | if (idlls[ic]->ordinal) |
1111 | 0 | for (const LEXX *tarr = idlls[ic]->lookupt; *tarr; tarr++) { |
1112 | 0 | if (*tarr & ord_mask) { |
1113 | 0 | ilinker->add_import(kernelDll(), *tarr & 0xffff); |
1114 | 0 | kernel32ordinal = true; |
1115 | 0 | } |
1116 | 0 | } |
1117 | 0 | } else if (!ilinker->hasDll(idlls[ic]->name)) { |
1118 | 0 | if (idlls[ic]->shname && !idlls[ic]->ordinal) |
1119 | 0 | ilinker->add_import(idlls[ic]->name, idlls[ic]->shname); |
1120 | 0 | else |
1121 | 0 | ilinker->add_import(idlls[ic]->name, idlls[ic]->ordinal); |
1122 | 0 | } |
1123 | 0 | } |
1124 | |
|
1125 | 0 | soimpdlls = ilinker->build(); |
1126 | |
|
1127 | 0 | Interval names(ibuf), iats(ibuf), lookups(ibuf); |
1128 | | |
1129 | | // create the preprocessed data |
1130 | 0 | SPAN_S_VAR(byte, ppi, oimport); // preprocessed imports |
1131 | 0 | for (unsigned ic = 0; ic < dllnum; ic++) { |
1132 | 0 | const LEXX *tarr = idlls[ic]->lookupt; |
1133 | 0 | set_le32(ppi, ilinker->getAddress(idlls[ic]->name)); |
1134 | 0 | set_le32(ppi + 4, idlls[ic]->iat - rvamin); |
1135 | 0 | ppi += 8; |
1136 | 0 | for (; *tarr; tarr++) { |
1137 | 0 | if (*tarr & ord_mask) { |
1138 | 0 | const unsigned ord = *tarr & 0xffff; |
1139 | 0 | if (idlls[ic]->isk32 && kernel32ordinal) { |
1140 | 0 | *ppi++ = 0xfe; // signed + odd parity |
1141 | 0 | set_le32(ppi, ilinker->getAddress(idlls[ic]->name, ord)); |
1142 | 0 | ppi += 4; |
1143 | 0 | } else { |
1144 | 0 | *ppi++ = 0xff; |
1145 | 0 | set_le16(ppi, ord); |
1146 | 0 | ppi += 2; |
1147 | 0 | } |
1148 | 0 | } else { |
1149 | 0 | *ppi++ = 1; |
1150 | 0 | const unsigned skip2 = 2 + *tarr; |
1151 | 0 | const unsigned take2 = 1 + strlen(ibuf.subref("bad import name %#x", skip2, 1)); |
1152 | 0 | memcpy(ppi, ibuf.subref("bad import name %#x", skip2, take2), take2); |
1153 | 0 | ppi += take2; |
1154 | 0 | names.add_interval(*tarr, 2 + take2); |
1155 | 0 | } |
1156 | 0 | } |
1157 | 0 | ppi++; |
1158 | |
|
1159 | 0 | const unsigned esize = ptr_udiff_bytes(tarr, idlls[ic]->lookupt); |
1160 | 0 | lookups.add_interval(idlls[ic]->lookupt, esize); |
1161 | 0 | if (ptr_diff_bytes(ibuf.subref("bad import name %#x", idlls[ic]->iat, 1), |
1162 | 0 | idlls[ic]->lookupt) != 0) { |
1163 | 0 | byte *a = ibuf.subref("bad import name %#x %#x", idlls[ic]->iat, esize); |
1164 | | // ptr_check_no_overlap(a, esize, idlls[ic]->lookupt, esize); |
1165 | 0 | memmove(a, idlls[ic]->lookupt, esize); |
1166 | 0 | iats.add_interval(idlls[ic]->iat, esize); |
1167 | 0 | } |
1168 | 0 | names.add_interval(idlls[ic]->name, strlen(idlls[ic]->name) + 1 + 1); |
1169 | 0 | } |
1170 | 0 | ppi += 4; |
1171 | 0 | assert(ppi < oimport + soimport); |
1172 | 0 | soimport = ptr_udiff_bytes(ppi, oimport); |
1173 | |
|
1174 | 0 | if (soimport == 4) |
1175 | 0 | soimport = 0; |
1176 | | |
1177 | | // OutputFile::dump("x0.imp", oimport, soimport); |
1178 | |
|
1179 | 0 | unsigned ilen = 0; |
1180 | 0 | names.flatten(); |
1181 | 0 | if (names.ivnum > 1) { |
1182 | | // The area occupied by the dll and imported names is not continuous |
1183 | | // so to still support uncompression, I can't zero the iat area. |
1184 | | // This decreases compression ratio, so FIXME somehow. |
1185 | 0 | infoWarning("can't remove unneeded imports"); |
1186 | 0 | ilen += sizeof(import_desc) * dllnum; |
1187 | | #if TESTING |
1188 | | if (opt->verbose > 3) |
1189 | | names.dump(); |
1190 | | #endif |
1191 | | // do some work for the unpacker |
1192 | 0 | for (unsigned ic = 0; ic < dllnum; ic++) { |
1193 | 0 | import_desc *const im = im_start + ic; |
1194 | 0 | memset(im, FILLVAL, sizeof(*im)); |
1195 | 0 | im->dllname = ptr_udiff_bytes(dlls[idlls[ic]->original_position].name, ibuf); |
1196 | 0 | } |
1197 | 0 | } else { |
1198 | 0 | iats.add_interval(im_start, sizeof(import_desc) * dllnum); |
1199 | | // zero unneeded data |
1200 | 0 | iats.clear(); |
1201 | 0 | lookups.clear(); |
1202 | 0 | } |
1203 | 0 | names.clear(); |
1204 | |
|
1205 | 0 | iats.add_interval(&names); |
1206 | 0 | iats.add_interval(&lookups); |
1207 | 0 | iats.flatten(); |
1208 | 0 | for (unsigned ic = 0; ic < iats.ivnum; ic++) |
1209 | 0 | ilen += iats.ivarr[ic].len; |
1210 | |
|
1211 | 0 | info("Imports: original size: %u bytes, preprocessed size: %u bytes", ilen, soimport); |
1212 | 0 | return names.ivnum == 1 ? names.ivarr[0].start : 0; |
1213 | 0 | } Unexecuted instantiation: unsigned int PeFile::processImports0<LE32, unsigned int>(unsigned int) Unexecuted instantiation: unsigned int PeFile::processImports0<LE64, unsigned long long>(unsigned long long) |
1214 | | |
1215 | | /************************************************************************* |
1216 | | // export handling |
1217 | | **************************************************************************/ |
1218 | | |
1219 | 0 | PeFile::Export::Export(char *_base) : base(_base), iv((byte *) _base) { |
1220 | 0 | COMPILE_TIME_ASSERT(sizeof(export_dir_t) == 40) |
1221 | 0 | COMPILE_TIME_ASSERT_ALIGNED1(export_dir_t) |
1222 | 0 | ename = functionptrs = ordinals = nullptr; |
1223 | 0 | names = nullptr; |
1224 | 0 | mem_clear(&edir); |
1225 | 0 | size = 0; |
1226 | 0 | } |
1227 | | |
1228 | 0 | PeFile::Export::~Export() noexcept { |
1229 | 0 | ::free(ename); |
1230 | 0 | delete[] functionptrs; |
1231 | 0 | delete[] ordinals; |
1232 | 0 | if (names) { |
1233 | 0 | const unsigned limit = edir.names + edir.functions; |
1234 | 0 | for (unsigned ic = 0; ic < limit; ic++) |
1235 | 0 | if (names[ic]) |
1236 | 0 | ::free(names[ic]); // allocated by strdup() |
1237 | 0 | delete[] names; |
1238 | 0 | } |
1239 | 0 | } |
1240 | | |
1241 | 0 | void PeFile::Export::convert(unsigned eoffs, unsigned esize) { |
1242 | 0 | memcpy(&edir, base + eoffs, sizeof(export_dir_t)); |
1243 | 0 | size = sizeof(export_dir_t); |
1244 | 0 | iv.add_interval(eoffs, size); |
1245 | |
|
1246 | 0 | if (!edir.name || eoffs + esize <= (unsigned) edir.name) { |
1247 | 0 | char msg[50]; |
1248 | 0 | snprintf(msg, sizeof(msg), "bad export directory name RVA %#x", (unsigned) edir.name); |
1249 | 0 | throwInternalError(msg); |
1250 | 0 | } |
1251 | 0 | unsigned len = strlen(base + edir.name) + 1; |
1252 | 0 | ename = ::strdup(base + edir.name); |
1253 | 0 | assert_noexcept(ename != nullptr); |
1254 | 0 | size += len; |
1255 | 0 | iv.add_interval(edir.name, len); |
1256 | | |
1257 | | // This test is weak because it does not consider other necessary storage. |
1258 | | // But it does detect outrageous individual members. |
1259 | | // Note: sizeof(LE32) <= sizeof(char *) |
1260 | 0 | if (UPX_RSIZE_MAX_MEM / sizeof(char *) <= edir.functions || |
1261 | 0 | UPX_RSIZE_MAX_MEM / sizeof(char *) <= edir.names) { |
1262 | 0 | throwCantPack("export directory too big: functions=%#x names=%#x", |
1263 | 0 | (unsigned) edir.functions, (unsigned) edir.names); |
1264 | 0 | } |
1265 | | // edir.name is checked above; the address/name/ordinal tables and the |
1266 | | // individual name RVAs are read from base with the same trust but were |
1267 | | // never confined to the export directory, so a crafted table RVA reads |
1268 | | // out of bounds. Keep every access within [eoffs, eoffs + esize). |
1269 | 0 | const unsigned end = eoffs + esize; |
1270 | 0 | len = sizeof(LE32) * edir.functions; |
1271 | 0 | if (edir.addrtable >= end || len > end - edir.addrtable) |
1272 | 0 | throwCantPack("bad export address table RVA %#x", (unsigned) edir.addrtable); |
1273 | 0 | functionptrs = New(char, len + 1); |
1274 | 0 | memcpy(functionptrs, base + edir.addrtable, len); |
1275 | 0 | size += len; |
1276 | 0 | iv.add_interval(edir.addrtable, len); |
1277 | |
|
1278 | 0 | unsigned ic; |
1279 | 0 | names = New(char *, edir.names + edir.functions + 1); |
1280 | 0 | if (edir.nameptrtable >= end || sizeof(LE32) * edir.names > end - edir.nameptrtable) |
1281 | 0 | throwCantPack("bad export name pointer table RVA %#x", (unsigned) edir.nameptrtable); |
1282 | 0 | for (ic = 0; ic < edir.names; ic++) { |
1283 | 0 | const unsigned namerva = get_le32(base + edir.nameptrtable + ic * sizeof(LE32)); |
1284 | 0 | if (namerva >= end) |
1285 | 0 | throwCantPack("bad export name RVA %#x", namerva); |
1286 | 0 | char *n = base + namerva; |
1287 | 0 | len = strlen(n) + 1; |
1288 | 0 | names[ic] = ::strdup(n); |
1289 | 0 | assert_noexcept(names[ic] != nullptr); |
1290 | 0 | size += len; |
1291 | 0 | iv.add_interval(namerva, len); |
1292 | 0 | } |
1293 | 0 | iv.add_interval(edir.nameptrtable, sizeof(LE32) * edir.names); |
1294 | 0 | size += sizeof(LE32) * edir.names; |
1295 | |
|
1296 | 0 | LE32 *fp = (LE32 *) functionptrs; |
1297 | | // export forwarders |
1298 | 0 | for (ic = 0; ic < edir.functions; ic++) |
1299 | 0 | if (fp[ic] >= eoffs && fp[ic] < eoffs + esize) { |
1300 | 0 | char *forw = base + fp[ic]; |
1301 | 0 | len = strlen(forw) + 1; |
1302 | 0 | iv.add_interval(forw, len); |
1303 | 0 | size += len; |
1304 | 0 | names[ic + edir.names] = ::strdup(forw); |
1305 | 0 | assert_noexcept(names[ic + edir.names] != nullptr); |
1306 | 0 | } else |
1307 | 0 | names[ic + edir.names] = nullptr; |
1308 | |
|
1309 | 0 | len = 2 * edir.names; |
1310 | 0 | if (edir.ordinaltable >= end || len > end - edir.ordinaltable) |
1311 | 0 | throwCantPack("bad export ordinal table RVA %#x", (unsigned) edir.ordinaltable); |
1312 | 0 | ordinals = New(char, len + 1); |
1313 | 0 | memcpy(ordinals, base + edir.ordinaltable, len); |
1314 | 0 | size += len; |
1315 | 0 | iv.add_interval(edir.ordinaltable, len); |
1316 | 0 | iv.flatten(); |
1317 | 0 | if (iv.ivnum == 1) |
1318 | 0 | iv.clear(); |
1319 | | #if TESTING |
1320 | | else |
1321 | | iv.dump(); |
1322 | | #endif |
1323 | 0 | } |
1324 | | |
1325 | 0 | void PeFile::Export::build(char *newbase, unsigned newoffs) { |
1326 | 0 | char *const functionp = newbase + sizeof(edir); |
1327 | 0 | char *const namep = functionp + sizeof(LE32) * edir.functions; |
1328 | 0 | char *const ordinalp = namep + sizeof(LE32) * edir.names; |
1329 | 0 | char *const enamep = ordinalp + 2 * edir.names; |
1330 | 0 | char *exports = enamep + strlen(ename) + 1; |
1331 | |
|
1332 | 0 | edir.addrtable = newoffs + ptr_diff_bytes(functionp, newbase); |
1333 | 0 | edir.ordinaltable = newoffs + ptr_diff_bytes(ordinalp, newbase); |
1334 | 0 | assert(ordinals != nullptr); // pacify clang-tidy |
1335 | 0 | memcpy(ordinalp, ordinals, 2 * edir.names); |
1336 | |
|
1337 | 0 | edir.name = newoffs + ptr_diff_bytes(enamep, newbase); |
1338 | 0 | strcpy(enamep, ename); |
1339 | 0 | edir.nameptrtable = newoffs + ptr_diff_bytes(namep, newbase); |
1340 | 0 | unsigned ic; |
1341 | 0 | for (ic = 0; ic < edir.names; ic++) { |
1342 | 0 | strcpy(exports, names[ic]); |
1343 | 0 | set_le32(namep + sizeof(LE32) * ic, newoffs + ptr_diff_bytes(exports, newbase)); |
1344 | 0 | exports += strlen(exports) + 1; |
1345 | 0 | } |
1346 | |
|
1347 | 0 | memcpy(functionp, functionptrs, sizeof(LE32) * edir.functions); |
1348 | 0 | for (ic = 0; ic < edir.functions; ic++) |
1349 | 0 | if (names[edir.names + ic]) { |
1350 | 0 | strcpy(exports, names[edir.names + ic]); |
1351 | 0 | set_le32(functionp + sizeof(LE32) * ic, newoffs + ptr_diff_bytes(exports, newbase)); |
1352 | 0 | exports += strlen(exports) + 1; |
1353 | 0 | } |
1354 | |
|
1355 | 0 | memcpy(newbase, &edir, sizeof(edir)); |
1356 | 0 | assert(exports - newbase == (int) size); |
1357 | 0 | } |
1358 | | |
1359 | 0 | void PeFile::processExports(Export *xport) { // pass1 |
1360 | 0 | soexport = ALIGN_UP(IDSIZE(PEDIR_EXPORT), 4u); |
1361 | 0 | if (soexport == 0) |
1362 | 0 | return; |
1363 | 0 | if (!isdll && opt->win32_pe.compress_exports) { |
1364 | 0 | infoWarning("exports compressed, --compress-exports=0 might be needed"); |
1365 | 0 | soexport = 0; |
1366 | 0 | return; |
1367 | 0 | } |
1368 | 0 | xport->convert(IDADDR(PEDIR_EXPORT), IDSIZE(PEDIR_EXPORT)); |
1369 | 0 | soexport = ALIGN_UP(xport->getsize(), 4u); |
1370 | 0 | mb_oexport.alloc(soexport); |
1371 | 0 | mb_oexport.clear(); |
1372 | 0 | oexport = SPAN_S_MAKE(byte, mb_oexport); // => now is a SPAN_S |
1373 | 0 | } |
1374 | | |
1375 | 0 | void PeFile::processExports(Export *xport, unsigned newoffs) { // pass2 |
1376 | 0 | if (soexport) |
1377 | 0 | xport->build((char *) raw_bytes(oexport, 0), newoffs); |
1378 | 0 | } |
1379 | | |
1380 | | /************************************************************************* |
1381 | | // TLS handling |
1382 | | **************************************************************************/ |
1383 | | |
1384 | | // thanks for theowl for providing me some docs, so that now I understand |
1385 | | // what I'm doing here :) |
1386 | | |
1387 | | // 1999-10-17: this was tricky to find: |
1388 | | // when the fixup records and the tls area are on the same page, then |
1389 | | // the tls area is not relocated, because the relocation is done by |
1390 | | // the virtual memory manager only for pages which are not yet loaded. |
1391 | | // of course it was impossible to debug this ;-) |
1392 | | |
1393 | | template <> |
1394 | | struct PeFile::tls_traits<LE32> final { |
1395 | | struct alignas(1) tls { |
1396 | | LE32 datastart; // VA tls init data start |
1397 | | LE32 dataend; // VA tls init data end |
1398 | | LE32 tlsindex; // VA tls index |
1399 | | LE32 callbacks; // VA tls callbacks |
1400 | | byte _[8]; // zero init, characteristics |
1401 | | }; |
1402 | | |
1403 | | static constexpr unsigned sotls = 24; |
1404 | | static constexpr unsigned cb_size = 4; |
1405 | | typedef unsigned cb_value_t; |
1406 | | static constexpr unsigned reloc_type = IMAGE_REL_BASED_HIGHLOW; |
1407 | | static constexpr int tls_handler_offset_reloc = 4; |
1408 | | }; |
1409 | | |
1410 | | template <> |
1411 | | struct PeFile::tls_traits<LE64> final { |
1412 | | struct alignas(1) tls { |
1413 | | LE64 datastart; // VA tls init data start |
1414 | | LE64 dataend; // VA tls init data end |
1415 | | LE64 tlsindex; // VA tls index |
1416 | | LE64 callbacks; // VA tls callbacks |
1417 | | byte _[8]; // zero init, characteristics |
1418 | | }; |
1419 | | |
1420 | | static constexpr unsigned sotls = 40; |
1421 | | static constexpr unsigned cb_size = 8; |
1422 | | typedef upx_uint64_t cb_value_t; |
1423 | | static constexpr unsigned reloc_type = IMAGE_REL_BASED_DIR64; |
1424 | | static constexpr int tls_handler_offset_reloc = -1; // no need to relocate |
1425 | | }; |
1426 | | |
1427 | | template <typename LEXX> |
1428 | | void PeFile::processTls1(Interval *iv, typename tls_traits<LEXX>::cb_value_t imagebase, |
1429 | 0 | unsigned imagesize) { // pass 1 |
1430 | 0 | typedef typename tls_traits<LEXX>::tls tls; |
1431 | 0 | typedef typename tls_traits<LEXX>::cb_value_t cb_value_t; |
1432 | 0 | constexpr unsigned cb_size = tls_traits<LEXX>::cb_size; |
1433 | |
|
1434 | 0 | COMPILE_TIME_ASSERT(sizeof(tls) == tls_traits<LEXX>::sotls) |
1435 | 0 | COMPILE_TIME_ASSERT_ALIGNED1(tls) |
1436 | |
|
1437 | 0 | if (isefi && IDSIZE(PEDIR_TLS)) |
1438 | 0 | throwCantPack("TLS not supported on EFI"); |
1439 | | |
1440 | 0 | const unsigned take = ALIGN_UP(IDSIZE(PEDIR_TLS), 4u); |
1441 | 0 | sotls = take; |
1442 | 0 | if (!sotls) |
1443 | 0 | return; |
1444 | 0 | const unsigned skip = IDADDR(PEDIR_TLS); |
1445 | 0 | const tls *const tlsp = (const tls *) ibuf.subref("bad tls %#x", skip, sizeof(tls)); |
1446 | | |
1447 | | // note: TLS callbacks are not implemented in Windows 95/98/ME |
1448 | 0 | if (tlsp->callbacks) { |
1449 | 0 | if (tlsp->callbacks < imagebase) |
1450 | 0 | throwCantPack("invalid TLS callback"); |
1451 | 0 | else if (tlsp->callbacks - imagebase + 4 >= imagesize) |
1452 | 0 | throwCantPack("invalid TLS callback"); |
1453 | 0 | cb_value_t v = |
1454 | 0 | *(LEXX *) ibuf.subref("bad TLS %#x", (tlsp->callbacks - imagebase), sizeof(LEXX)); |
1455 | |
|
1456 | 0 | if (v != 0) { |
1457 | | // count number of callbacks, just for information string - Stefan Widmann |
1458 | 0 | unsigned num_callbacks = 0; |
1459 | 0 | unsigned callback_offset = 0; |
1460 | 0 | while (*(LEXX *) ibuf.subref( |
1461 | 0 | "bad TLS %#x", tlsp->callbacks - imagebase + callback_offset, sizeof(LEXX))) { |
1462 | | // increment number of callbacks |
1463 | 0 | num_callbacks++; |
1464 | 0 | callback_offset += cb_size; |
1465 | 0 | } |
1466 | 0 | info("TLS: %u callback(s) found, adding TLS callback handler", num_callbacks); |
1467 | | // set flag to include necessary sections in loader |
1468 | 0 | use_tls_callbacks = true; |
1469 | | // define linker symbols |
1470 | 0 | tlscb_ptr = tlsp->callbacks; |
1471 | 0 | } |
1472 | 0 | } |
1473 | | |
1474 | 0 | const unsigned tlsdatastart = tlsp->datastart - imagebase; |
1475 | 0 | const unsigned tlsdataend = tlsp->dataend - imagebase; |
1476 | | |
1477 | | // now some ugly stuff: find the relocation entries in the tls data area |
1478 | 0 | const unsigned skip2 = IDADDR(PEDIR_BASERELOC); |
1479 | 0 | const unsigned take2 = IDSIZE(PEDIR_BASERELOC); |
1480 | 0 | Reloc rel(ibuf.subref("bad tls reloc %#x", skip2, take2), take2); |
1481 | 0 | unsigned pos, type; |
1482 | 0 | while (rel.next(pos, type)) |
1483 | 0 | if (pos >= tlsdatastart && pos < tlsdataend) |
1484 | 0 | iv->add_interval(pos, type); |
1485 | |
|
1486 | 0 | sotls = sizeof(tls) + tlsdataend - tlsdatastart; |
1487 | | // if TLS callbacks are used, we need two more {D|Q}WORDS at the end of the TLS |
1488 | | // ... and those dwords should be correctly aligned |
1489 | 0 | if (use_tls_callbacks) |
1490 | 0 | sotls = ALIGN_UP(sotls, cb_size) + 2 * cb_size; |
1491 | 0 | const unsigned aligned_sotls = ALIGN_UP(sotls, usizeof(LEXX)); |
1492 | | |
1493 | | // the PE loader wants this stuff uncompressed |
1494 | 0 | mb_otls.alloc(aligned_sotls); |
1495 | 0 | mb_otls.clear(); |
1496 | 0 | otls = SPAN_S_MAKE(byte, mb_otls); // => otls now is a SPAN_S |
1497 | 0 | const unsigned skip1 = IDADDR(PEDIR_TLS); |
1498 | 0 | const unsigned take1 = sizeof(tls); |
1499 | 0 | memcpy(otls, ibuf.subref("bad tls %#x", skip1, take1), take1); |
1500 | | // WARNING: this can access data in BSS |
1501 | 0 | const unsigned take3 = sotls - sizeof(tls); |
1502 | 0 | memcpy(otls + sizeof(tls), ibuf.subref("bad tls %#x", tlsdatastart, take3), take3); |
1503 | 0 | tlsindex = tlsp->tlsindex - imagebase; |
1504 | | // NEW: subtract two dwords if TLS callbacks are used - Stefan Widmann |
1505 | 0 | info("TLS: %u bytes tls data and %u relocations added", |
1506 | 0 | sotls - (unsigned) sizeof(tls) - (use_tls_callbacks ? 2 * cb_size : 0), iv->ivnum); |
1507 | | |
1508 | | // makes sure tls index is zero after decompression |
1509 | 0 | if (tlsindex && tlsindex < imagesize) |
1510 | 0 | set_le32(ibuf.subref("bad tlsindex %#x", tlsindex, sizeof(unsigned)), 0); |
1511 | 0 | } Unexecuted instantiation: void PeFile::processTls1<LE32>(PeFile::Interval*, PeFile::tls_traits<LE32>::cb_value_t, unsigned int) Unexecuted instantiation: void PeFile::processTls1<LE64>(PeFile::Interval*, PeFile::tls_traits<LE64>::cb_value_t, unsigned int) |
1512 | | |
1513 | | template <typename LEXX> |
1514 | | void PeFile::processTls2(Reloc *const rel, const Interval *const iv, unsigned newaddr, |
1515 | 0 | typename tls_traits<LEXX>::cb_value_t imagebase) { // pass 2 |
1516 | 0 | typedef typename tls_traits<LEXX>::tls tls; |
1517 | 0 | typedef typename tls_traits<LEXX>::cb_value_t cb_value_t; |
1518 | 0 | constexpr unsigned cb_size = tls_traits<LEXX>::cb_size; |
1519 | 0 | constexpr unsigned reloc_type = tls_traits<LEXX>::reloc_type; |
1520 | 0 | static_assert(reloc_type > IMAGE_REL_BASED_IGNORE && reloc_type < 16); |
1521 | 0 | constexpr int tls_handler_offset_reloc = tls_traits<LEXX>::tls_handler_offset_reloc; |
1522 | |
|
1523 | 0 | if (sotls == 0) |
1524 | 0 | return; |
1525 | | |
1526 | | // add new relocation entries |
1527 | 0 | if (tls_handler_offset > 0 && tls_handler_offset_reloc > 0) |
1528 | 0 | rel->add_reloc(tls_handler_offset + tls_handler_offset_reloc, reloc_type); |
1529 | | |
1530 | | // NEW: if TLS callbacks are used, relocate the VA of the callback chain, too - Stefan Widmann |
1531 | 0 | for (unsigned ic = 0; ic < (unsigned) (use_tls_callbacks ? 4 : 3); ic++) |
1532 | 0 | rel->add_reloc(newaddr + ic * cb_size, reloc_type); |
1533 | |
|
1534 | 0 | SPAN_S_VAR(tls, const tlsp, mb_otls); |
1535 | | // now the relocation entries in the tls data area |
1536 | 0 | for (unsigned ic = 0; ic < iv->ivnum; ic++) { |
1537 | 0 | SPAN_S_VAR(byte, const pp, |
1538 | 0 | otls + (iv->ivarr[ic].start - (tlsp->datastart - imagebase) + sizeof(tls))); |
1539 | 0 | LEXX *const p = (LEXX *) raw_bytes(pp, sizeof(LEXX)); |
1540 | 0 | cb_value_t kc = *p; |
1541 | 0 | if (kc >= tlsp->datastart && kc < tlsp->dataend) { |
1542 | | // add a relocation entry referring to an address inside of the original tls data area |
1543 | | // - as the new tls area is moved, the referred address have to be also adjusted |
1544 | 0 | kc += newaddr + sizeof(tls) - tlsp->datastart; |
1545 | 0 | *p = kc + imagebase; |
1546 | 0 | rel->add_reloc(kc, iv->ivarr[ic].len); |
1547 | 0 | } else { |
1548 | | // add a relocation entry referring to an address outside of the original tls data area |
1549 | | // by adding the difference of the new tlsdatastart and the old tlsdatastart to |
1550 | | // the address of the original relocation record |
1551 | 0 | const unsigned a = |
1552 | 0 | iv->ivarr[ic].start + (newaddr + sizeof(tls)) - (tlsp->datastart - imagebase); |
1553 | | // Must not overwrite compressed data |
1554 | 0 | if (a < newaddr && !opt->win32_pe.strip_relocs) |
1555 | 0 | throwCantPack("relocation too low (%#x < %#x); try --strip-relocs", a, newaddr); |
1556 | 0 | rel->add_reloc(a, iv->ivarr[ic].len); |
1557 | 0 | } |
1558 | 0 | } |
1559 | | |
1560 | 0 | const unsigned tls_data_size = tlsp->dataend - tlsp->datastart; |
1561 | 0 | tlsp->datastart = newaddr + sizeof(tls) + imagebase; |
1562 | 0 | tlsp->dataend = tlsp->datastart + tls_data_size; |
1563 | | |
1564 | | // NEW: if we have TLS callbacks to handle, we create a pointer to the new callback chain - |
1565 | | // Stefan Widmann |
1566 | 0 | tlsp->callbacks = (use_tls_callbacks ? newaddr + sotls + imagebase - 2 * cb_size : 0); |
1567 | |
|
1568 | 0 | if (use_tls_callbacks) { |
1569 | | // set handler offset |
1570 | 0 | SPAN_S_VAR(byte, pp, otls); |
1571 | 0 | pp = otls + (sotls - 2 * cb_size); |
1572 | 0 | *(LEXX *) raw_bytes(pp, sizeof(LEXX)) = tls_handler_offset + imagebase; |
1573 | 0 | pp = otls + (sotls - 1 * cb_size); |
1574 | 0 | *(LEXX *) raw_bytes(pp, sizeof(LEXX)) = 0; // end of one-item list |
1575 | | // add relocation for TLS handler offset |
1576 | 0 | rel->add_reloc(newaddr + sotls - 2 * cb_size, reloc_type); |
1577 | 0 | } |
1578 | 0 | } Unexecuted instantiation: void PeFile::processTls2<LE32>(PeFile::Reloc*, PeFile::Interval const*, unsigned int, PeFile::tls_traits<LE32>::cb_value_t) Unexecuted instantiation: void PeFile::processTls2<LE64>(PeFile::Reloc*, PeFile::Interval const*, unsigned int, PeFile::tls_traits<LE64>::cb_value_t) |
1579 | | |
1580 | | /************************************************************************* |
1581 | | // Load Configuration handling |
1582 | | **************************************************************************/ |
1583 | | |
1584 | 0 | void PeFile::processLoadConf(Interval *iv) { // pass 1 |
1585 | 0 | if (IDSIZE(PEDIR_LOAD_CONFIG) == 0) |
1586 | 0 | return; |
1587 | | |
1588 | 0 | const unsigned lcaddr = IDADDR(PEDIR_LOAD_CONFIG); |
1589 | 0 | const byte *const loadconf = ibuf.subref("bad loadconf %#x", lcaddr, 4); |
1590 | 0 | soloadconf = get_le32(loadconf); |
1591 | 0 | if (soloadconf == 0) |
1592 | 0 | return; |
1593 | 0 | static constexpr unsigned MAX_SOLOADCONF = 256; // XXX FIXME: Why? |
1594 | 0 | if (soloadconf > MAX_SOLOADCONF) |
1595 | 0 | info("Load Configuration directory %u > %u", soloadconf, MAX_SOLOADCONF); |
1596 | 0 | if (lcaddr + soloadconf > ibuf.getSize()) { |
1597 | 0 | throwCantPack("load config size exceeds file bounds"); |
1598 | 0 | } |
1599 | | |
1600 | | // if there were relocation entries referring to the load config table |
1601 | | // then we need them for the copy of the table too |
1602 | 0 | const unsigned skip = IDADDR(PEDIR_BASERELOC); |
1603 | 0 | const unsigned take = IDSIZE(PEDIR_BASERELOC); |
1604 | 0 | Reloc rel(ibuf.subref("bad reloc %#x", skip, take), take); |
1605 | 0 | unsigned pos, type; |
1606 | 0 | while (rel.next(pos, type)) |
1607 | 0 | if (pos >= lcaddr && pos < lcaddr + soloadconf) { |
1608 | 0 | iv->add_interval(pos - lcaddr, type); |
1609 | 0 | NO_printf("loadconf reloc detected: %x\n", pos); |
1610 | 0 | } |
1611 | |
|
1612 | 0 | mb_oloadconf.alloc(soloadconf); |
1613 | 0 | oloadconf = (byte *) mb_oloadconf.getVoidPtr(); |
1614 | 0 | memcpy(oloadconf, loadconf, soloadconf); |
1615 | 0 | } |
1616 | | |
1617 | | void PeFile::processLoadConf(Reloc *rel, const Interval *iv, |
1618 | 0 | unsigned newaddr) { // pass2 |
1619 | | // now we have the address of the new load config table |
1620 | | // so we can create the new relocation entries |
1621 | 0 | for (unsigned ic = 0; ic < iv->ivnum; ic++) { |
1622 | 0 | rel->add_reloc(iv->ivarr[ic].start + newaddr, iv->ivarr[ic].len); |
1623 | 0 | NO_printf("loadconf reloc added: %x %d\n", iv->ivarr[ic].start + newaddr, |
1624 | 0 | iv->ivarr[ic].len); |
1625 | 0 | } |
1626 | 0 | } |
1627 | | |
1628 | | /************************************************************************* |
1629 | | // resource handling |
1630 | | **************************************************************************/ |
1631 | | |
1632 | | struct alignas(1) PeFile::Resource::res_dir_entry final { |
1633 | | LE32 tnl; // Type | Name | Language id - depending on level |
1634 | | LE32 child; |
1635 | | }; |
1636 | | |
1637 | | struct alignas(1) PeFile::Resource::res_dir final { |
1638 | | byte _[12]; // flags, timedate, version |
1639 | | LE16 namedentr; |
1640 | | LE16 identr; |
1641 | | // it's usually safe to assume that every res_dir contains |
1642 | | // at least one res_dir_entry - check() complains otherwise |
1643 | | res_dir_entry entries[1]; |
1644 | | |
1645 | 90 | unsigned Sizeof() const { return 16 + mem_size(sizeof(res_dir_entry), namedentr + identr); } |
1646 | | }; |
1647 | | |
1648 | | struct alignas(1) PeFile::Resource::res_data final { |
1649 | | LE32 offset; |
1650 | | LE32 size; |
1651 | | byte _[8]; // codepage, reserved |
1652 | | }; |
1653 | | |
1654 | | struct PeFile::Resource::upx_rnode /*not_final*/ { |
1655 | | unsigned id = 0; |
1656 | | byte *name = nullptr; |
1657 | | upx_rnode *parent = nullptr; |
1658 | | }; |
1659 | | |
1660 | | struct PeFile::Resource::upx_rbranch final : public PeFile::Resource::upx_rnode { |
1661 | | unsigned nc = 0; |
1662 | | upx_rnode **children = nullptr; |
1663 | | res_dir data; |
1664 | | }; |
1665 | | |
1666 | | struct PeFile::Resource::upx_rleaf final : public PeFile::Resource::upx_rnode { |
1667 | | upx_rleaf *next = nullptr; |
1668 | | unsigned newoffset = 0; |
1669 | | res_data data; |
1670 | | }; |
1671 | | |
1672 | 0 | PeFile::Resource::Resource(const byte *ibufstart_, const byte *ibufend_) : root(nullptr) { |
1673 | 0 | ibufstart = ibufstart_; |
1674 | 0 | ibufend = ibufend_; |
1675 | 0 | } |
1676 | | |
1677 | 30 | PeFile::Resource::Resource(const byte *p, const byte *ibufstart_, const byte *ibufend_) { |
1678 | 30 | ibufstart = ibufstart_; |
1679 | 30 | ibufend = ibufend_; |
1680 | 30 | newstart = nullptr; |
1681 | 30 | init(p); |
1682 | 30 | } |
1683 | | |
1684 | 14 | PeFile::Resource::~Resource() noexcept { |
1685 | 14 | if (root) { |
1686 | 13 | destroy(root, 0); |
1687 | 13 | root = nullptr; |
1688 | 13 | } |
1689 | 14 | } |
1690 | | |
1691 | 65 | unsigned PeFile::Resource::dirsize() const { return ALIGN_UP(dsize + ssize, 4u); } |
1692 | | |
1693 | 34 | bool PeFile::Resource::next() { |
1694 | | // wow, builtin autorewind... :-) |
1695 | 34 | current = current ? current->next : head; |
1696 | 34 | return current != nullptr; |
1697 | 34 | } |
1698 | | |
1699 | 0 | unsigned PeFile::Resource::itype() const { return current->parent->parent->id; } |
1700 | | |
1701 | 0 | const byte *PeFile::Resource::ntype() const { return current->parent->parent->name; } |
1702 | | |
1703 | 34 | unsigned PeFile::Resource::size() const { return ALIGN_UP(current->data.size, 4u); } |
1704 | | |
1705 | 91 | unsigned PeFile::Resource::offs() const { return current->data.offset; } |
1706 | | |
1707 | 23 | unsigned &PeFile::Resource::newoffs() { return current->newoffset; } |
1708 | | |
1709 | 0 | void PeFile::Resource::dump() const { dump(root, 0); } |
1710 | | |
1711 | 0 | unsigned PeFile::Resource::iname() const { return current->parent->id; } |
1712 | | |
1713 | 0 | const byte *PeFile::Resource::nname() const { return current->parent->name; } |
1714 | | |
1715 | | /* |
1716 | | unsigned ilang() const {return current->id;} |
1717 | | const byte *nlang() const {return current->name;} |
1718 | | */ |
1719 | | |
1720 | 30 | void PeFile::Resource::init(const byte *res) { |
1721 | 30 | COMPILE_TIME_ASSERT(sizeof(res_dir_entry) == 8) |
1722 | 30 | COMPILE_TIME_ASSERT(sizeof(res_dir) == 16 + 8) |
1723 | 30 | COMPILE_TIME_ASSERT(sizeof(res_data) == 16) |
1724 | 30 | COMPILE_TIME_ASSERT_ALIGNED1(res_dir_entry) |
1725 | 30 | COMPILE_TIME_ASSERT_ALIGNED1(res_dir) |
1726 | 30 | COMPILE_TIME_ASSERT_ALIGNED1(res_data) |
1727 | | |
1728 | 30 | start = res; |
1729 | 30 | root = head = current = nullptr; |
1730 | 30 | dsize = ssize = 0; |
1731 | 30 | check((const res_dir *) start, 0); |
1732 | 30 | root = convert(start, nullptr, 0); |
1733 | 30 | } |
1734 | | |
1735 | 121 | void PeFile::Resource::check(const res_dir *node, unsigned level) { |
1736 | 121 | ibufcheck(node, sizeof(*node)); |
1737 | 121 | int ic = node->identr + node->namedentr; |
1738 | 121 | if (ic == 0) |
1739 | 7 | return; |
1740 | 646 | for (const res_dir_entry *rde = node->entries; --ic >= 0; rde++) { |
1741 | 540 | ibufcheck(rde, sizeof(*rde)); |
1742 | 540 | if (((rde->child & 0x80000000) == 0) ^ (level == 2)) |
1743 | 8 | throwCantPack("unsupported resource structure"); |
1744 | 532 | else if (level != 2) |
1745 | 91 | check((const res_dir *) (start + (rde->child & 0x7fffffff)), level + 1); |
1746 | 540 | } |
1747 | 114 | } |
1748 | | |
1749 | 783 | void PeFile::Resource::ibufcheck(const void *m, unsigned siz) { |
1750 | 783 | if (m < ibufstart || m > ibufend - siz) |
1751 | 4 | throwCantUnpack("corrupted resources"); |
1752 | 783 | } |
1753 | | |
1754 | | PeFile::Resource::upx_rnode *PeFile::Resource::convert(const void *rnode, upx_rnode *parent, |
1755 | 121 | unsigned level) { |
1756 | 121 | if (level == 3) { |
1757 | 36 | const res_data *node = ACC_STATIC_CAST(const res_data *, rnode); |
1758 | 36 | ibufcheck(node, sizeof(*node)); |
1759 | 36 | upx_rleaf *leaf = new upx_rleaf; |
1760 | 36 | leaf->id = 0; |
1761 | 36 | leaf->name = nullptr; |
1762 | 36 | leaf->parent = parent; |
1763 | 36 | leaf->next = head; |
1764 | 36 | leaf->newoffset = 0; |
1765 | 36 | leaf->data = *node; |
1766 | | |
1767 | 36 | head = leaf; // append node to a linked list for traversal |
1768 | 36 | dsize += sizeof(res_data); |
1769 | 36 | return leaf; |
1770 | 36 | } |
1771 | | |
1772 | 85 | const res_dir *node = ACC_STATIC_CAST(const res_dir *, rnode); |
1773 | 85 | ibufcheck(node, sizeof(*node)); |
1774 | 85 | int ic = node->identr + node->namedentr; |
1775 | 85 | if (ic == 0) |
1776 | 5 | return nullptr; |
1777 | | |
1778 | 80 | upx_rbranch *branch = new upx_rbranch; |
1779 | 80 | branch->id = 0; |
1780 | 80 | branch->name = nullptr; |
1781 | 80 | branch->parent = parent; |
1782 | 80 | branch->children = New0(upx_rnode *, ic); |
1783 | 80 | branch->nc = ic; |
1784 | 80 | branch->data = *node; |
1785 | 80 | if (!root) // first one |
1786 | 19 | root = branch; // prevent leak if xcheck throws (hacked unpack or test) |
1787 | | |
1788 | 181 | for (const res_dir_entry *rde = node->entries + ic - 1; --ic >= 0; rde--) { |
1789 | 101 | upx_rnode *child = convert(start + (rde->child & 0x7fffffff), branch, level + 1); |
1790 | 101 | branch->children[ic] = child; |
1791 | 101 | xcheck(child); |
1792 | 101 | child->id = rde->tnl; |
1793 | 101 | if (child->id & 0x80000000) { |
1794 | 1 | const byte *p = start + (child->id & 0x7fffffff); |
1795 | 1 | ibufcheck(p, 2); |
1796 | 1 | const unsigned len = 2 + 2 * get_le16(p); |
1797 | 1 | ibufcheck(p, len); |
1798 | 1 | child->name = New(byte, len); |
1799 | 1 | memcpy(child->name, p, len); // copy unicode string |
1800 | 1 | ssize += len; // size of unicode strings |
1801 | 1 | } |
1802 | 101 | } |
1803 | 80 | dsize += node->Sizeof(); |
1804 | 80 | return branch; |
1805 | 85 | } |
1806 | | |
1807 | | void PeFile::Resource::build(const upx_rnode *node, unsigned &bpos, unsigned &spos, |
1808 | 34 | unsigned level) { |
1809 | 34 | if (level == 3) { |
1810 | 11 | if (bpos + sizeof(res_data) > dirsize()) |
1811 | 0 | throwCantUnpack("corrupted resources"); |
1812 | | |
1813 | 11 | res_data *l = (res_data *) (newstart + bpos); |
1814 | 11 | const upx_rleaf *leaf = (const upx_rleaf *) node; |
1815 | 11 | *l = leaf->data; |
1816 | 11 | if (leaf->newoffset) |
1817 | 9 | l->offset = leaf->newoffset; |
1818 | 11 | bpos += sizeof(*l); |
1819 | 11 | return; |
1820 | 11 | } |
1821 | 23 | if (bpos + sizeof(res_dir) > dirsize()) |
1822 | 0 | throwCantUnpack("corrupted resources"); |
1823 | | |
1824 | 23 | res_dir *const b = (res_dir *) (newstart + bpos); |
1825 | 23 | const upx_rbranch *branch = (const upx_rbranch *) node; |
1826 | 23 | *b = branch->data; |
1827 | 23 | bpos += b->Sizeof(); |
1828 | 23 | res_dir_entry *be = b->entries; |
1829 | 52 | for (unsigned ic = 0; ic < branch->nc; ic++, be++) { |
1830 | 29 | xcheck(branch->children[ic]); |
1831 | 29 | be->tnl = branch->children[ic]->id; |
1832 | 29 | be->child = bpos + ((level < 2) ? 0x80000000 : 0); |
1833 | | |
1834 | 29 | const byte *p; |
1835 | 29 | if ((p = branch->children[ic]->name) != nullptr) { |
1836 | 0 | be->tnl = spos + 0x80000000; |
1837 | 0 | if (spos + get_le16(p) * 2 + 2 > dirsize()) |
1838 | 0 | throwCantUnpack("corrupted resources"); |
1839 | 0 | memcpy(newstart + spos, p, get_le16(p) * 2 + 2); |
1840 | 0 | spos += get_le16(p) * 2 + 2; |
1841 | 0 | } |
1842 | | |
1843 | 29 | build(branch->children[ic], bpos, spos, level + 1); |
1844 | 29 | } |
1845 | 23 | } |
1846 | | |
1847 | 5 | byte *PeFile::Resource::build() { |
1848 | 5 | mb_start.dealloc(); |
1849 | 5 | newstart = nullptr; |
1850 | 5 | if (dirsize()) { |
1851 | 5 | mb_start.alloc(dirsize()); |
1852 | 5 | newstart = static_cast<byte *>(mb_start.getVoidPtr()); |
1853 | 5 | unsigned bpos = 0, spos = dsize; |
1854 | 5 | build(root, bpos, spos, 0); |
1855 | | |
1856 | | // dirsize() is 4 bytes aligned, so we may need to zero |
1857 | | // up to 2 bytes to make valgrind happy |
1858 | 5 | while (spos < dirsize()) |
1859 | 0 | newstart[spos++] = 0; |
1860 | 5 | } |
1861 | | |
1862 | 5 | return newstart; |
1863 | 5 | } |
1864 | | |
1865 | 92 | void PeFile::Resource::destroy(upx_rnode *node, unsigned level) noexcept { |
1866 | 92 | xcheck_noexcept(node); |
1867 | 92 | if (level == 3) { |
1868 | 29 | upx_rleaf *leaf = ACC_STATIC_CAST(upx_rleaf *, node); |
1869 | 29 | delete[] leaf->name; |
1870 | 29 | leaf->name = nullptr; |
1871 | 29 | delete leaf; |
1872 | 63 | } else { |
1873 | 63 | upx_rbranch *branch = ACC_STATIC_CAST(upx_rbranch *, node); |
1874 | 63 | delete[] branch->name; |
1875 | 63 | branch->name = nullptr; |
1876 | 142 | for (int ic = branch->nc; --ic >= 0;) |
1877 | 79 | if (branch->children[ic] != nullptr) |
1878 | 79 | destroy(branch->children[ic], level + 1); |
1879 | 63 | delete[] branch->children; |
1880 | 63 | branch->children = nullptr; |
1881 | 63 | delete branch; |
1882 | 63 | } |
1883 | 92 | } |
1884 | | |
1885 | 0 | static void lame_print_unicode(const byte *p) { |
1886 | 0 | for (unsigned ic = 0; ic < get_le16(p); ic++) |
1887 | 0 | printf("%c", (char) p[ic * 2 + 2]); |
1888 | 0 | } |
1889 | | |
1890 | 0 | void PeFile::Resource::dump(const upx_rnode *node, unsigned level) const { |
1891 | 0 | if (level) { |
1892 | 0 | for (unsigned ic = 1; ic < level; ic++) |
1893 | 0 | printf("\t\t"); |
1894 | 0 | if (node->name) |
1895 | 0 | lame_print_unicode(node->name); |
1896 | 0 | else |
1897 | 0 | printf("0x%x", node->id); |
1898 | 0 | printf("\n"); |
1899 | 0 | } |
1900 | 0 | if (level == 3) |
1901 | 0 | return; |
1902 | 0 | const upx_rbranch *const branch = (const upx_rbranch *) node; |
1903 | 0 | for (unsigned ic = 0; ic < branch->nc; ic++) |
1904 | 0 | dump(branch->children[ic], level + 1); |
1905 | 0 | } |
1906 | | |
1907 | 0 | void PeFile::Resource::clear(byte *node, unsigned level, Interval *iv) { |
1908 | 0 | if (level == 3) |
1909 | 0 | iv->add_interval(node, sizeof(res_data)); |
1910 | 0 | else { |
1911 | 0 | const res_dir *const rd = (res_dir *) node; |
1912 | 0 | const unsigned n = rd->identr + rd->namedentr; |
1913 | 0 | const res_dir_entry *rde = rd->entries; |
1914 | 0 | for (unsigned ic = 0; ic < n; ic++, rde++) |
1915 | 0 | clear(newstart + (rde->child & 0x7fffffff), level + 1, iv); |
1916 | 0 | iv->add_interval(rd, rd->Sizeof()); |
1917 | 0 | } |
1918 | 0 | } |
1919 | | |
1920 | 0 | bool PeFile::Resource::clear() { |
1921 | 0 | newstart = const_cast<byte *>(start); |
1922 | 0 | Interval iv(newstart); |
1923 | 0 | clear(newstart, 0, &iv); |
1924 | 0 | iv.flatten(); |
1925 | 0 | if (iv.ivnum == 1) |
1926 | 0 | iv.clear(); |
1927 | | #if TESTING |
1928 | | if (opt->verbose > 3) |
1929 | | iv.dump(); |
1930 | | #endif |
1931 | 0 | return iv.ivnum == 1; |
1932 | 0 | } |
1933 | | |
1934 | 0 | void PeFile::processResources(Resource *res, unsigned newaddr) { |
1935 | 0 | if (IDSIZE(PEDIR_RESOURCE) == 0) |
1936 | 0 | return; |
1937 | 0 | while (res->next()) |
1938 | 0 | if (res->newoffs()) |
1939 | 0 | res->newoffs() += newaddr; |
1940 | 0 | if (res->dirsize()) { |
1941 | 0 | byte *p = res->build(); |
1942 | 0 | memcpy(oresources, p, res->dirsize()); |
1943 | 0 | } |
1944 | 0 | } |
1945 | | |
1946 | | static bool match(unsigned itype, const byte *ntype, unsigned iname, const byte *nname, |
1947 | 0 | const char *keep) { |
1948 | | // format of string keep: type1[/name1],type2[/name2], .... |
1949 | | // typex and namex can be string or number |
1950 | | // hopefully resource names do not have '/' or ',' characters inside |
1951 | |
|
1952 | 0 | struct Helper final { |
1953 | 0 | static bool match(unsigned num, const byte *unistr, const char *mkeep) { |
1954 | 0 | if (!unistr) |
1955 | 0 | return (unsigned) atoi(mkeep) == num; |
1956 | 0 | unsigned ic; |
1957 | 0 | for (ic = 0; ic < get_le16(unistr); ic++) |
1958 | 0 | if (unistr[2 + ic * 2] != (byte) mkeep[ic]) |
1959 | 0 | return false; |
1960 | 0 | return mkeep[ic] == 0 || mkeep[ic] == ',' || mkeep[ic] == '/'; |
1961 | 0 | } |
1962 | 0 | }; |
1963 | | |
1964 | | // FIXME this comparison is not too exact |
1965 | 0 | for (;;) { |
1966 | 0 | const char *delim1 = strchr(keep, '/'); |
1967 | 0 | const char *delim2 = strchr(keep, ','); |
1968 | 0 | if (Helper::match(itype, ntype, keep)) { |
1969 | 0 | if (!delim1) |
1970 | 0 | return true; |
1971 | 0 | if (delim2 && delim2 < delim1) |
1972 | 0 | return true; |
1973 | 0 | if (Helper::match(iname, nname, delim1 + 1)) |
1974 | 0 | return true; |
1975 | 0 | } |
1976 | 0 | if (delim2 == nullptr) |
1977 | 0 | break; |
1978 | 0 | keep = delim2 + 1; |
1979 | 0 | } |
1980 | 0 | return false; |
1981 | 0 | } |
1982 | | |
1983 | 0 | void PeFile::processResources(Resource *res) { |
1984 | 0 | const unsigned vaddr = IDADDR(PEDIR_RESOURCE); |
1985 | 0 | if ((soresources = IDSIZE(PEDIR_RESOURCE)) == 0) |
1986 | 0 | return; |
1987 | | |
1988 | | // setup default options for resource compression |
1989 | 0 | if (opt->win32_pe.compress_resources.isThird()) |
1990 | 0 | opt->win32_pe.compress_resources = !isefi; |
1991 | 0 | if (!opt->win32_pe.compress_resources) { |
1992 | 0 | opt->win32_pe.compress_icons = false; |
1993 | 0 | for (int i = 0; i < RT_LAST; i++) |
1994 | 0 | opt->win32_pe.compress_rt[i] = false; |
1995 | 0 | } |
1996 | 0 | if (opt->win32_pe.compress_rt[RT_STRING].isThird()) { |
1997 | | // by default, don't compress RT_STRINGs of screensavers (".scr") |
1998 | 0 | opt->win32_pe.compress_rt[RT_STRING] = true; |
1999 | 0 | if (fn_has_ext(fi->getName(), "scr")) |
2000 | 0 | opt->win32_pe.compress_rt[RT_STRING] = false; |
2001 | 0 | } |
2002 | |
|
2003 | 0 | res->init(ibuf.subref("bad res %#x", vaddr, 1)); |
2004 | |
|
2005 | 0 | for (soresources = res->dirsize(); res->next(); soresources += 4 + res->size()) |
2006 | 0 | ; |
2007 | 0 | if (!soresources) |
2008 | 0 | return; // empty .rsrc Section |
2009 | 0 | mb_oresources.alloc(soresources); |
2010 | 0 | mb_oresources.clear(); |
2011 | 0 | oresources = SPAN_S_MAKE(byte, mb_oresources); // => now is a SPAN_S |
2012 | 0 | SPAN_S_VAR(byte, ores, oresources + res->dirsize()); |
2013 | |
|
2014 | 0 | char *keep_icons = nullptr; // icon ids in the first icon group |
2015 | 0 | const auto keep_icons_deleter = upx::ArrayDeleter(&keep_icons, 1); // don't leak memory |
2016 | 0 | unsigned iconsin1stdir = 0; |
2017 | 0 | if (opt->win32_pe.compress_icons == 2) |
2018 | 0 | while (res->next()) // there is no rewind() in Resource |
2019 | 0 | if (res->itype() == RT_GROUP_ICON && iconsin1stdir == 0) { |
2020 | 0 | iconsin1stdir = get_le16(ibuf.subref("bad resoff %#x", res->offs() + 4, 2)); |
2021 | 0 | delete[] keep_icons; |
2022 | 0 | keep_icons = nullptr; |
2023 | 0 | keep_icons = New(char, 1 + iconsin1stdir * 9); |
2024 | 0 | *keep_icons = 0; |
2025 | 0 | for (unsigned ic = 0; ic < iconsin1stdir; ic++) |
2026 | 0 | upx_safe_snprintf( |
2027 | 0 | keep_icons + strlen(keep_icons), 9, "3/%u,", |
2028 | 0 | get_le16(ibuf.subref("bad resoff %#x", res->offs() + 6 + ic * 14 + 12, 2))); |
2029 | 0 | if (*keep_icons) |
2030 | 0 | keep_icons[strlen(keep_icons) - 1] = 0; |
2031 | 0 | } |
2032 | | |
2033 | | // the icon id which should not be compressed when compress_icons == 1 |
2034 | 0 | unsigned first_icon_id = (unsigned) -1; |
2035 | 0 | if (opt->win32_pe.compress_icons == 1) |
2036 | 0 | while (res->next()) |
2037 | 0 | if (res->itype() == RT_GROUP_ICON && first_icon_id == (unsigned) -1) |
2038 | 0 | first_icon_id = get_le16(ibuf.subref("bad resoff %#x", res->offs() + 6 + 12, 2)); |
2039 | |
|
2040 | 0 | bool compress_icon = opt->win32_pe.compress_icons > 1; |
2041 | 0 | bool compress_idir = opt->win32_pe.compress_icons == 3; |
2042 | | |
2043 | | // some statistics |
2044 | 0 | unsigned usize = 0; |
2045 | 0 | unsigned csize = 0; |
2046 | 0 | unsigned unum = 0; |
2047 | 0 | unsigned cnum = 0; |
2048 | |
|
2049 | 0 | while (res->next()) { |
2050 | 0 | const unsigned rtype = res->itype(); |
2051 | 0 | bool do_compress = true; |
2052 | 0 | if (!opt->win32_pe.compress_resources) |
2053 | 0 | do_compress = false; |
2054 | 0 | else if (rtype == RT_ICON) // icon |
2055 | 0 | { |
2056 | 0 | if (opt->win32_pe.compress_icons == 0) |
2057 | 0 | do_compress = false; |
2058 | 0 | else if (opt->win32_pe.compress_icons == 1) |
2059 | 0 | if ((first_icon_id == (unsigned) -1 || first_icon_id == res->iname())) |
2060 | 0 | do_compress = compress_icon; |
2061 | 0 | } else if (rtype == RT_GROUP_ICON) // icon directory |
2062 | 0 | do_compress = compress_idir && opt->win32_pe.compress_icons; |
2063 | 0 | else if (rtype > 0 && rtype < RT_LAST) |
2064 | 0 | do_compress = opt->win32_pe.compress_rt[rtype] ? true : false; |
2065 | |
|
2066 | 0 | if (do_compress && keep_icons) |
2067 | 0 | do_compress &= |
2068 | 0 | !match(res->itype(), res->ntype(), res->iname(), res->nname(), keep_icons); |
2069 | 0 | if (do_compress) |
2070 | 0 | do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(), |
2071 | 0 | "TYPELIB,REGISTRY,16"); |
2072 | 0 | if (do_compress) |
2073 | 0 | do_compress &= !match(res->itype(), res->ntype(), res->iname(), res->nname(), |
2074 | 0 | opt->win32_pe.keep_resource); |
2075 | |
|
2076 | 0 | if (do_compress) { |
2077 | 0 | csize += res->size(); |
2078 | 0 | cnum++; |
2079 | 0 | continue; |
2080 | 0 | } |
2081 | | |
2082 | 0 | usize += res->size(); |
2083 | 0 | unum++; |
2084 | |
|
2085 | 0 | set_le32(ores, res->offs()); // save original offset |
2086 | 0 | ores += 4; |
2087 | 0 | const unsigned take = res->size(); |
2088 | 0 | ICHECK(ibuf + res->offs(), take); |
2089 | 0 | memcpy(ores, ibuf.subref("bad resoff %#x", res->offs(), take), take); |
2090 | 0 | ibuf.fill(res->offs(), take, FILLVAL); |
2091 | 0 | res->newoffs() = ptr_diff_bytes(ores, oresources); |
2092 | 0 | if (rtype == RT_ICON && opt->win32_pe.compress_icons == 1) |
2093 | 0 | compress_icon = true; |
2094 | 0 | else if (rtype == RT_GROUP_ICON) { |
2095 | 0 | if (opt->win32_pe.compress_icons == 1) { |
2096 | 0 | icondir_offset = 4 + ptr_diff_bytes(ores, oresources); |
2097 | 0 | icondir_count = get_le16(oresources + icondir_offset); |
2098 | 0 | set_le16(oresources + icondir_offset, 1); |
2099 | 0 | } |
2100 | 0 | compress_idir = true; |
2101 | 0 | } |
2102 | 0 | ores += res->size(); |
2103 | 0 | } |
2104 | 0 | soresources = ptr_diff_bytes(ores, oresources); |
2105 | |
|
2106 | 0 | if (!res->clear()) { |
2107 | | // The area occupied by the resource directory is not continuous |
2108 | | // so to still support uncompression, I can't zero this area. |
2109 | | // This decreases compression ratio, so FIXME somehow. |
2110 | 0 | infoWarning("can't remove unneeded resource directory"); |
2111 | 0 | } |
2112 | 0 | info("Resources: compressed %u (%u bytes), not compressed %u (%u bytes)", cnum, csize, unum, |
2113 | 0 | usize); |
2114 | 0 | } |
2115 | | |
2116 | | /*static*/ |
2117 | 0 | unsigned PeFile::virta2objnum(unsigned addr, SPAN_0(const pe_section_t) sect, unsigned objs) { |
2118 | 0 | unsigned ic; |
2119 | 0 | for (ic = 0; ic < objs; ic++) { |
2120 | | // if (sect->vaddr >= addr && sect->vaddr + sect->vsize < addr) // ??? |
2121 | 0 | if (sect->vaddr <= addr && sect->vaddr + sect->vsize > addr) |
2122 | 0 | return ic; |
2123 | 0 | sect++; |
2124 | 0 | } |
2125 | | // throwCantPack("virta2objnum() failed"); |
2126 | 0 | return ic; |
2127 | 0 | } |
2128 | | |
2129 | 0 | unsigned PeFile::tryremove(unsigned vaddr, unsigned objs) { |
2130 | 0 | unsigned ic = virta2objnum(vaddr, isection, objs); |
2131 | 0 | if (ic && ic == objs - 1) { |
2132 | 0 | NO_fprintf(stderr, "removed section: %d size: 0x%x\n", ic, (int) isection[ic].size); |
2133 | 0 | info("removed section: %d size: 0x%x", ic, (int) isection[ic].size); |
2134 | 0 | objs--; |
2135 | 0 | } |
2136 | 0 | return objs; |
2137 | 0 | } |
2138 | | |
2139 | 0 | unsigned PeFile::stripDebug(unsigned overlaystart) { |
2140 | 0 | if (IDADDR(PEDIR_DEBUG) == 0) |
2141 | 0 | return overlaystart; |
2142 | | |
2143 | 0 | COMPILE_TIME_ASSERT(sizeof(DebugDir) == 28) |
2144 | 0 | COMPILE_TIME_ASSERT_ALIGNED1(DebugDir) |
2145 | |
|
2146 | 0 | const unsigned skip = IDADDR(PEDIR_DEBUG); |
2147 | 0 | const unsigned take = IDSIZE(PEDIR_DEBUG); |
2148 | 0 | DebugDir *const dd0 = (DebugDir *) ibuf.subref("bad debug %#x", skip, take); |
2149 | 0 | DebugDir *dd = dd0; |
2150 | 0 | for (unsigned ic = 0; ic < IDSIZE(PEDIR_DEBUG) / sizeof(DebugDir); ic++, dd++) { |
2151 | 0 | if (IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS == dd->type && dd->size == sizeof(LE32) && |
2152 | 0 | dd->fpos <= (file_size_u - sizeof(LE32))) { |
2153 | | // fpos need not belong to any PEDIR_* section. |
2154 | | // Read directly from input file, but keep position (paranoia). |
2155 | 0 | LE32 word; |
2156 | 0 | upx_off_t const now_pos = fi->tell(); |
2157 | 0 | fi->seek(dd->fpos, SEEK_SET); |
2158 | 0 | fi->read(&word, sizeof(word)); |
2159 | 0 | fi->seek(now_pos, SEEK_SET); |
2160 | 0 | if (IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT & word) { |
2161 | 0 | *(dbgCET = dd0) = *dd; // remember presence; copy to front |
2162 | 0 | } |
2163 | 0 | } |
2164 | 0 | if (overlaystart == dd->fpos) |
2165 | 0 | overlaystart += dd->size; |
2166 | 0 | } |
2167 | 0 | ibuf.fill((!dbgCET ? 0 : sizeof(DebugDir)) + IDADDR(PEDIR_DEBUG), |
2168 | 0 | (!dbgCET ? 0 : -(int) sizeof(DebugDir)) + IDSIZE(PEDIR_DEBUG), FILLVAL); |
2169 | 0 | return overlaystart; |
2170 | 0 | } |
2171 | | |
2172 | | /************************************************************************* |
2173 | | // pack |
2174 | | **************************************************************************/ |
2175 | | |
2176 | 0 | void PeFile::readSectionHeaders(unsigned objs, unsigned sizeof_ih) { |
2177 | 0 | if (objs == 0) |
2178 | 0 | return; |
2179 | 0 | mb_isection.alloc(mem_size(sizeof(pe_section_t), objs)); |
2180 | 0 | isection = SPAN_S_MAKE(pe_section_t, mb_isection); // => isection now is a SPAN_S |
2181 | 0 | if (file_size_u < pe_offset + sizeof_ih + sizeof(pe_section_t) * objs) { |
2182 | 0 | char buf[32]; |
2183 | 0 | snprintf(buf, sizeof(buf), "too many sections %d", objs); |
2184 | 0 | throwCantPack(buf); |
2185 | 0 | } |
2186 | 0 | fi->seek(pe_offset + sizeof_ih, SEEK_SET); |
2187 | 0 | fi->readx(isection, sizeof(pe_section_t) * objs); |
2188 | 0 | rvamin = isection[0].vaddr; |
2189 | 0 | const unsigned rvalast = isection[-1 + objs].vsize + isection[-1 + objs].vaddr; |
2190 | 0 | for (unsigned j = 0; j < objs; ++j) { // expect: first is min, last is max |
2191 | 0 | unsigned lo = isection[j].vaddr, hi = isection[j].vsize + lo; |
2192 | 0 | if (hi < lo) { // this checks first and last sections, too! |
2193 | 0 | char buf[64]; |
2194 | 0 | snprintf(buf, sizeof(buf), "bad section[%d] wrap-around %#x %#x", j, lo, hi - lo); |
2195 | 0 | throwCantPack(buf); |
2196 | 0 | } |
2197 | 0 | if (lo < rvamin) { |
2198 | 0 | char buf[64]; |
2199 | 0 | snprintf(buf, sizeof(buf), "bad section .rva [%d] %#x < [0] %#x", j, lo, rvamin); |
2200 | 0 | throwCantPack(buf); |
2201 | 0 | } |
2202 | 0 | if (rvalast < hi) { |
2203 | 0 | char buf[80]; |
2204 | 0 | snprintf(buf, sizeof(buf), "bad section .rva+.vsize [%d] %#x > [%d] %#x", j, hi, |
2205 | 0 | (-1 + objs), rvalast); |
2206 | 0 | throwCantPack(buf); |
2207 | 0 | } |
2208 | 0 | } |
2209 | | |
2210 | 0 | infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), |
2211 | 0 | objs); |
2212 | 0 | } |
2213 | | |
2214 | | void PeFile::checkHeaderValues(unsigned subsystem, unsigned mask, unsigned ih_entry, |
2215 | 0 | unsigned ih_filealign) { |
2216 | 0 | if ((1u << subsystem) & ~mask) { |
2217 | 0 | char buf[100]; |
2218 | 0 | upx_safe_snprintf(buf, sizeof(buf), "PE: subsystem %u is not supported", subsystem); |
2219 | 0 | throwCantPack(buf); |
2220 | 0 | } |
2221 | | // check CLR Runtime Header directory entry |
2222 | 0 | if (IDSIZE(PEDIR_COM_DESCRIPTOR)) |
2223 | 0 | throwCantPack(".NET files are not yet supported"); |
2224 | | |
2225 | 0 | if (isection == nullptr) |
2226 | 0 | throwCantPack("No section was found"); |
2227 | | |
2228 | 0 | if (memcmp(isection[0].name, "UPX", 3) == 0) |
2229 | 0 | throwAlreadyPackedByUPX(); |
2230 | | |
2231 | 0 | if (!opt->force && IDSIZE(15)) |
2232 | 0 | throwCantPack("file is possibly packed/protected (try --force)"); |
2233 | | |
2234 | 0 | if (ih_entry && ih_entry < rvamin) |
2235 | 0 | throwCantPack("run a virus scanner on this file!"); |
2236 | | |
2237 | 0 | const unsigned fam1 = ih_filealign - 1; |
2238 | 0 | if (!upx::has_single_bit(ih_filealign)) { // ih_filealign is not a power of 2 |
2239 | 0 | char buf[32]; |
2240 | 0 | snprintf(buf, sizeof(buf), "bad file alignment %#x", 1 + fam1); |
2241 | 0 | throwCantPack(buf); |
2242 | 0 | } |
2243 | 0 | } |
2244 | | |
2245 | | unsigned PeFile::handleStripRelocs(upx_uint64_t ih_imagebase, upx_uint64_t default_imagebase, |
2246 | 0 | LE16 &dllflags) { |
2247 | 0 | if (opt->win32_pe.strip_relocs < 0) { |
2248 | 0 | if (isdll || isefi || dllflags & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) |
2249 | 0 | opt->win32_pe.strip_relocs = false; |
2250 | 0 | else |
2251 | 0 | opt->win32_pe.strip_relocs = ih_imagebase >= default_imagebase; |
2252 | 0 | } |
2253 | 0 | if (opt->win32_pe.strip_relocs) { |
2254 | 0 | if (isdll || isefi) |
2255 | 0 | throwCantPack("--strip-relocs is not allowed with DLL and EFI images"); |
2256 | 0 | if (dllflags & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) { |
2257 | 0 | if (opt->force) // Disable ASLR |
2258 | 0 | { |
2259 | | // The bit is set, so clear it with XOR |
2260 | 0 | dllflags ^= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; |
2261 | | // HIGH_ENTROPY_VA has no effect without DYNAMIC_BASE, so clear |
2262 | | // it also if set |
2263 | 0 | dllflags &= ~(unsigned) IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA; |
2264 | 0 | } else |
2265 | 0 | throwCantPack("--strip-relocs is not allowed with ASLR (use " |
2266 | 0 | "with --force to remove)"); |
2267 | 0 | } |
2268 | 0 | if (!opt->force && ih_imagebase < default_imagebase) |
2269 | 0 | throwCantPack("--strip-relocs may not support this imagebase (try " |
2270 | 0 | "with --force)"); |
2271 | 0 | return IMAGE_FILE_RELOCS_STRIPPED; |
2272 | 0 | } else |
2273 | 0 | info("Base relocations stripping is disabled for this image"); |
2274 | 0 | return 0; |
2275 | 0 | } |
2276 | | |
2277 | | unsigned PeFile::readSections(unsigned objs, unsigned usize, unsigned ih_filealign, |
2278 | 0 | unsigned ih_datasize) { |
2279 | 0 | const unsigned xtrasize = UPX_MAX(ih_datasize, 65536u) + IDSIZE(PEDIR_IMPORT) + |
2280 | 0 | IDSIZE(PEDIR_BOUND_IMPORT) + IDSIZE(PEDIR_IAT) + |
2281 | 0 | IDSIZE(PEDIR_DELAY_IMPORT) + IDSIZE(PEDIR_BASERELOC); |
2282 | 0 | ibuf.alloc(usize + xtrasize); |
2283 | | |
2284 | | // BOUND IMPORT support. FIXME: is this ok? |
2285 | 0 | ibufgood = isection[0].rawdataptr; |
2286 | 0 | fi->seek(0, SEEK_SET); |
2287 | 0 | fi->readx(ibuf, ibufgood); |
2288 | | |
2289 | | // Interval holes(ibuf); |
2290 | |
|
2291 | 0 | unsigned ic, jc, overlaystart = 0; |
2292 | 0 | ibuf.clear(0, usize); |
2293 | 0 | for (ic = jc = 0; ic < objs; ic++) { |
2294 | 0 | if (isection[ic].rawdataptr && overlaystart < isection[ic].rawdataptr + isection[ic].size) |
2295 | 0 | overlaystart = ALIGN_UP(isection[ic].rawdataptr + isection[ic].size, ih_filealign); |
2296 | 0 | if (isection[ic].vsize == 0) |
2297 | 0 | isection[ic].vsize = isection[ic].size; |
2298 | 0 | if ((isection[ic].flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA) || |
2299 | 0 | isection[ic].rawdataptr == 0 || (isection[ic].flags & IMAGE_SCN_LNK_INFO)) { |
2300 | | // holes.add_interval(isection[ic].vaddr, isection[ic].vsize); |
2301 | 0 | continue; |
2302 | 0 | } |
2303 | 0 | if (isection[ic].vaddr + isection[ic].size > usize) |
2304 | 0 | throwCantPack("section size problem"); |
2305 | 0 | if (!isrtm && ((isection[ic].flags & (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED)) == |
2306 | 0 | (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED))) |
2307 | 0 | if (!opt->force) |
2308 | 0 | throwCantPack("writable shared sections not supported (try --force)"); |
2309 | 0 | if (jc && isection[ic].rawdataptr - jc > ih_filealign && !opt->force) |
2310 | 0 | throwCantPack("superfluous data between sections (try --force)"); |
2311 | 0 | fi->seek(isection[ic].rawdataptr, SEEK_SET); |
2312 | 0 | jc = isection[ic].size; |
2313 | 0 | if (jc > isection[ic].vsize) |
2314 | 0 | jc = isection[ic].vsize; |
2315 | 0 | if (isection[ic].vsize == 0) // hack for some tricky programs - may this break other progs? |
2316 | 0 | jc = isection[ic].vsize = isection[ic].size; |
2317 | 0 | if (isection[ic].vaddr + jc > ibuf.getSize()) |
2318 | 0 | throwInternalError("buffer too small 1"); |
2319 | 0 | fi->readx(ibuf.subref("bad section %#x", isection[ic].vaddr, jc), jc); |
2320 | 0 | ibufgood = upx::umax(ibufgood, jc + isection[ic].vaddr); // FIXME: simplistic |
2321 | 0 | jc += isection[ic].rawdataptr; |
2322 | 0 | } |
2323 | 0 | return overlaystart; |
2324 | 0 | } |
2325 | | |
2326 | 0 | void PeFile::callCompressWithFilters(Filter &ft, int filter_strategy, unsigned ih_codebase) { |
2327 | 0 | compressWithFilters(&ft, 2048, NULL_cconf, filter_strategy, ih_codebase, rvamin, 0, nullptr, 0); |
2328 | 0 | } |
2329 | | |
2330 | 0 | void PeFile::callProcessStubRelocs(Reloc &rel, unsigned &ic) { |
2331 | | // WinCE wants relocation data at the beginning of a section |
2332 | 0 | rel.finish(oxrelocs, soxrelocs); |
2333 | 0 | if (opt->win32_pe.strip_relocs) |
2334 | 0 | soxrelocs = 0; |
2335 | 0 | ODADDR(PEDIR_BASERELOC) = soxrelocs ? ic : 0; |
2336 | 0 | ODSIZE(PEDIR_BASERELOC) = soxrelocs; |
2337 | 0 | ic += soxrelocs; |
2338 | 0 | } |
2339 | | |
2340 | 0 | void PeFile::callProcessResources(Resource &res, unsigned &ic) { |
2341 | 0 | if (soresources) |
2342 | 0 | processResources(&res, ic); |
2343 | 0 | ODADDR(PEDIR_RESOURCE) = soresources ? ic : 0; |
2344 | 0 | ODSIZE(PEDIR_RESOURCE) = soresources; |
2345 | 0 | ic += soresources; |
2346 | 0 | } |
2347 | | |
2348 | | template <typename LEXX, typename ht> |
2349 | | void PeFile::pack0(OutputFile *fo, ht &ih, ht &oh, unsigned subsystem_mask, |
2350 | 0 | upx_uint64_t default_imagebase, bool last_section_rsrc_only) { |
2351 | | // FIXME: we need to think about better support for --exact |
2352 | 0 | if (opt->exact) |
2353 | 0 | throwCantPackExact(); |
2354 | | |
2355 | 0 | const unsigned objs = ih.objects; |
2356 | 0 | readSectionHeaders(objs, sizeof(ih)); |
2357 | 0 | if (!opt->force && needForceOption()) |
2358 | 0 | throwCantPack("unexpected value in PE header (try --force)"); |
2359 | | |
2360 | 0 | if (ih.dllflags & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY) { |
2361 | 0 | if (opt->force) |
2362 | 0 | ih.dllflags &= ~(unsigned) IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY; |
2363 | 0 | else |
2364 | 0 | throwCantPack("image forces integrity check (use --force to remove)"); |
2365 | 0 | } |
2366 | 0 | checkHeaderValues(ih.subsystem, subsystem_mask, ih.entry, ih.filealign); |
2367 | | |
2368 | | // remove certificate directory entry |
2369 | 0 | if (IDSIZE(PEDIR_SECURITY)) |
2370 | 0 | IDSIZE(PEDIR_SECURITY) = IDADDR(PEDIR_SECURITY) = 0; |
2371 | |
|
2372 | 0 | if (ih.flags & IMAGE_FILE_RELOCS_STRIPPED) |
2373 | 0 | opt->win32_pe.strip_relocs = true; |
2374 | 0 | else |
2375 | 0 | ih.flags |= handleStripRelocs(ih.imagebase, default_imagebase, ih.dllflags); |
2376 | |
|
2377 | 0 | if (isefi) { |
2378 | | // PIC for EFI only to avoid false positive detections of Win32 images |
2379 | | // without relocations fixed address is smaller |
2380 | 0 | if (!opt->win32_pe.strip_relocs) |
2381 | 0 | use_stub_relocs = false; |
2382 | | |
2383 | | // EFI build tools already clear DOS stub |
2384 | | // and small file alignment benefits from extra space |
2385 | 0 | byte stub[0x40]; |
2386 | 0 | memset(stub, 0, sizeof(stub)); |
2387 | 0 | set_le16(stub, 'M' + 'Z' * 256); |
2388 | 0 | set_le32(stub + sizeof(stub) - sizeof(LE32), sizeof(stub)); |
2389 | 0 | fo->write(stub, sizeof(stub)); |
2390 | 0 | pe_offset = sizeof(stub); |
2391 | 0 | } else |
2392 | 0 | handleStub(fi, fo, pe_offset); |
2393 | 0 | unsigned overlaystart = readSections(objs, ih.imagesize, ih.filealign, ih.datasize); |
2394 | 0 | unsigned overlay = file_size_u - stripDebug(overlaystart); |
2395 | 0 | if (overlay >= file_size_u) |
2396 | 0 | overlay = 0; |
2397 | 0 | checkOverlay(overlay); |
2398 | |
|
2399 | 0 | if (ih.dllflags & IMAGE_DLLCHARACTERISTICS_GUARD_CF) { |
2400 | 0 | if (opt->force) { |
2401 | 0 | const unsigned lcsize = IDSIZE(PEDIR_LOAD_CONFIG); |
2402 | 0 | const unsigned lcaddr = IDADDR(PEDIR_LOAD_CONFIG); |
2403 | 0 | const unsigned gfpos = 14 * sizeof(ih.imagebase) + 6 * sizeof(LE32) + 4 * sizeof(LE16); |
2404 | 0 | if (lcaddr && lcsize >= gfpos + sizeof(LE32)) |
2405 | | // GuardFlags: Set IMAGE_GUARD_SECURITY_COOKIE_UNUSED |
2406 | | // and clear the rest |
2407 | 0 | set_le32(ibuf.subref("bad guard flags at %#x", lcaddr + gfpos, sizeof(LE32)), |
2408 | 0 | 0x00000800); |
2409 | 0 | ih.dllflags ^= IMAGE_DLLCHARACTERISTICS_GUARD_CF; |
2410 | 0 | } else |
2411 | 0 | throwCantPack("GUARD_CF enabled PE files are not supported (use --force to disable)"); |
2412 | 0 | } |
2413 | | |
2414 | 0 | Resource res(ibuf, ibuf + ibuf.getSize()); |
2415 | 0 | Interval tlsiv(ibuf); |
2416 | 0 | Interval loadconfiv(ibuf); |
2417 | 0 | Export xport((char *) (byte *) ibuf); |
2418 | |
|
2419 | 0 | const unsigned dllstrings = processImports(); |
2420 | 0 | processTls(&tlsiv); // call before processRelocs!! |
2421 | 0 | processLoadConf(&loadconfiv); |
2422 | 0 | processResources(&res); |
2423 | 0 | processExports(&xport); |
2424 | 0 | processRelocs(); |
2425 | | |
2426 | | // OutputFile::dump("x1", ibuf, usize); |
2427 | | |
2428 | | // some checks for broken linkers - disable filter if necessary |
2429 | 0 | bool allow_filter = true; |
2430 | 0 | if (/*FIXME ih.codebase == ih.database |
2431 | 0 | ||*/ ih.codebase + ih.codesize > ih.imagesize || |
2432 | 0 | (isection[virta2objnum(ih.codebase, isection, objs)].flags & IMAGE_SCN_CNT_CODE) == 0) |
2433 | 0 | allow_filter = false; |
2434 | |
|
2435 | 0 | const unsigned oam1 = ih.objectalign - 1; |
2436 | 0 | if (!upx::has_single_bit(ih.objectalign)) { // ih.objectalign is not a power of 2 |
2437 | 0 | char buf[32]; |
2438 | 0 | snprintf(buf, sizeof(buf), "bad object alignment %#x", 1 + oam1); |
2439 | 0 | throwCantPack(buf); |
2440 | 0 | } |
2441 | | |
2442 | | // FIXME: if the last object has a bss then this won't work |
2443 | | // newvsize = (isection[objs-1].vaddr + isection[objs-1].size + oam1) &~ oam1; |
2444 | | // temporary solution: |
2445 | 0 | unsigned newvsize = (isection[objs - 1].vaddr + isection[objs - 1].vsize + oam1) & ~oam1; |
2446 | |
|
2447 | 0 | NO_fprintf(stderr, "newvsize=%x objs=%d\n", newvsize, objs); |
2448 | 0 | if ((upx_uint64_t) newvsize + soimport + sorelocs > ibuf.getSize()) |
2449 | 0 | throwInternalError("buffer too small 2"); |
2450 | 0 | memcpy(ibuf + newvsize, oimport, soimport); |
2451 | 0 | memcpy(ibuf + newvsize + soimport, orelocs, sorelocs); |
2452 | |
|
2453 | 0 | cimports = newvsize - rvamin; // rva of preprocessed imports |
2454 | 0 | crelocs = cimports + soimport; // rva of preprocessed fixups |
2455 | |
|
2456 | 0 | ph.u_len = newvsize + soimport + sorelocs; |
2457 | | |
2458 | | // some extra_info data for uncompression support |
2459 | 0 | unsigned s = 0; |
2460 | 0 | byte *const p1 = ibuf.subref("bad ph.u_len %#x", ph.u_len, sizeof(ih)); |
2461 | 0 | memcpy(p1 + s, &ih, sizeof(ih)); |
2462 | 0 | s += sizeof(ih); |
2463 | 0 | memcpy(p1 + s, isection, ih.objects * sizeof(*isection)); |
2464 | 0 | s += ih.objects * sizeof(*isection); |
2465 | 0 | if (soimport) { |
2466 | 0 | set_le32(p1 + s, cimports); |
2467 | 0 | set_le32(p1 + s + 4, dllstrings); |
2468 | 0 | s += 8; |
2469 | 0 | } |
2470 | 0 | if (sorelocs) { |
2471 | 0 | set_le32(p1 + s, crelocs); |
2472 | 0 | p1[s + 4] = (byte) (big_relocs & 6); |
2473 | 0 | s += 5; |
2474 | 0 | } |
2475 | 0 | if (soresources) { |
2476 | 0 | set_le16(p1 + s, icondir_count); |
2477 | 0 | s += 2; |
2478 | 0 | } |
2479 | | // end of extra_info data |
2480 | |
|
2481 | 0 | set_le32(p1 + s, ptr_diff_bytes(p1, ibuf) - rvamin); |
2482 | 0 | s += 4; |
2483 | 0 | ph.u_len += s; |
2484 | 0 | obuf.allocForCompression(ph.u_len); |
2485 | | |
2486 | | // prepare packheader |
2487 | 0 | if (ph.u_len < rvamin) { // readSectionHeaders() should have caught this |
2488 | 0 | char buf[64]; |
2489 | 0 | snprintf(buf, sizeof(buf), "bad PE header ph.u_len=%#x rvamin=%#x", ph.u_len, rvamin); |
2490 | 0 | throwInternalError(buf); |
2491 | 0 | } |
2492 | 0 | ph.u_len -= rvamin; |
2493 | | // prepare filter |
2494 | 0 | Filter ft(ph.level); |
2495 | 0 | ft.buf_len = ih.codesize; |
2496 | 0 | ft.addvalue = ih.codebase - rvamin; |
2497 | | // compress |
2498 | 0 | int filter_strategy = allow_filter ? 0 : -3; |
2499 | | |
2500 | | // disable filters for files with broken headers |
2501 | 0 | if (ih.codebase + ih.codesize > ph.u_len) { |
2502 | 0 | ft.buf_len = 1; |
2503 | 0 | filter_strategy = -3; |
2504 | 0 | } |
2505 | |
|
2506 | 0 | callCompressWithFilters(ft, filter_strategy, ih.codebase); |
2507 | | // info: see buildLoader() |
2508 | 0 | newvsize = (ph.u_len + rvamin + ph.overlap_overhead + oam1) & ~oam1; |
2509 | | // but keep PETLSHAK for DLLs: the loader sets the tls index after |
2510 | | // LoadLibrary, so it must survive decompression |
2511 | 0 | if (tlsindex && !isdll && ((newvsize - ph.c_len - 1024 + oam1) & ~oam1) > tlsindex + 4) |
2512 | 0 | tlsindex = 0; |
2513 | |
|
2514 | 0 | const int oh_filealign = UPX_MIN(ih.filealign, 0x200u); |
2515 | 0 | const unsigned fam1 = oh_filealign - 1; |
2516 | |
|
2517 | 0 | int identsize = 0; |
2518 | 0 | const unsigned codesize = getLoaderSection("IDENTSTR", &identsize); |
2519 | 0 | assert(identsize > 0); |
2520 | 0 | unsigned ic; |
2521 | 0 | getLoaderSection("UPX1HEAD", (int *) &ic); |
2522 | 0 | identsize += ic; |
2523 | |
|
2524 | 0 | const bool has_oxrelocs = |
2525 | 0 | !opt->win32_pe.strip_relocs && (use_stub_relocs || sotls || loadconfiv.ivnum); |
2526 | 0 | const bool has_ncsection = has_oxrelocs || soimpdlls || soexport || soresources; |
2527 | 0 | const unsigned oobjs = last_section_rsrc_only ? 4 : has_ncsection ? 3 : 2; |
2528 | | ////pe_section_t osection[oobjs]; |
2529 | 0 | pe_section_t osection[4]; |
2530 | 0 | memset(osection, 0, sizeof(osection)); |
2531 | | // section 0 : bss |
2532 | | // 1 : [ident + header] + packed_data + unpacker + tls + loadconf |
2533 | | // 2 : not compressed data |
2534 | | // 3 : resource data -- wince/arm 5 needs a new section for this |
2535 | | |
2536 | | // the last section should start with the resource data, because lots of lame |
2537 | | // windoze codes assume that resources starts on the beginning of a section |
2538 | | |
2539 | | // note: there should be no data in the last section which needs fixup |
2540 | | |
2541 | | // identsplit - number of ident + (upx header) bytes to put into the PE header |
2542 | 0 | const unsigned sizeof_osection = sizeof(osection[0]) * oobjs; |
2543 | 0 | int identsplit = pe_offset + sizeof_osection + sizeof(ht); |
2544 | 0 | if ((identsplit & fam1) == 0) |
2545 | 0 | identsplit = 0; |
2546 | 0 | else if (((identsplit + identsize) ^ identsplit) < oh_filealign) |
2547 | 0 | identsplit = identsize; |
2548 | 0 | else |
2549 | 0 | identsplit = ALIGN_UP_GAP(identsplit, oh_filealign); |
2550 | 0 | ic = identsize - identsplit; |
2551 | |
|
2552 | 0 | const unsigned c_len = |
2553 | 0 | ((ph.c_len + ic) & 15) == 0 ? ph.c_len : ph.c_len + 16 - ((ph.c_len + ic) & 15); |
2554 | 0 | obuf.clear(ph.c_len, c_len - ph.c_len); |
2555 | |
|
2556 | 0 | const unsigned aligned_sotls = ALIGN_UP(sotls, usizeof(LEXX)); |
2557 | 0 | const unsigned s1size = ALIGN_UP(ic + c_len + codesize, usizeof(LEXX)) + aligned_sotls + |
2558 | 0 | soloadconf + (dbgCET ? (sizeof(LE32) + sizeof(*dbgCET)) : 0); |
2559 | 0 | const unsigned s1addr = (newvsize - (ic + c_len) + oam1) & ~oam1; |
2560 | |
|
2561 | 0 | const unsigned ncsection = (s1addr + s1size + oam1) & ~oam1; |
2562 | 0 | const unsigned upxsection = s1addr + ic + c_len; |
2563 | |
|
2564 | 0 | Reloc rel(1024); // new stub relocations are put here |
2565 | 0 | addNewRelocations(rel, upxsection); |
2566 | | |
2567 | | // new PE header |
2568 | 0 | memcpy(&oh, &ih, sizeof(oh)); |
2569 | 0 | oh.filealign = oh_filealign; // identsplit depends on this |
2570 | |
|
2571 | 0 | oh.entry = upxsection; |
2572 | 0 | oh.objects = oobjs; |
2573 | 0 | oh.chksum = 0; |
2574 | | |
2575 | | // fill the data directory |
2576 | 0 | ODADDR(PEDIR_DEBUG) = 0; // dbgCET later |
2577 | 0 | ODSIZE(PEDIR_DEBUG) = 0; |
2578 | 0 | ODADDR(PEDIR_IAT) = 0; |
2579 | 0 | ODSIZE(PEDIR_IAT) = 0; |
2580 | 0 | ODADDR(PEDIR_BOUND_IMPORT) = 0; |
2581 | 0 | ODSIZE(PEDIR_BOUND_IMPORT) = 0; |
2582 | | |
2583 | | // tls & loadconf are put into section 1 |
2584 | 0 | ic = s1addr + s1size - aligned_sotls - soloadconf - |
2585 | 0 | (dbgCET ? (sizeof(LE32) + sizeof(*dbgCET)) : 0); |
2586 | |
|
2587 | 0 | if (use_tls_callbacks) |
2588 | 0 | tls_handler_offset = linker->getSymbolOffset("PETLSC2") + upxsection; |
2589 | |
|
2590 | 0 | processTls(&rel, &tlsiv, ic); |
2591 | 0 | ODADDR(PEDIR_TLS) = aligned_sotls ? ic : 0; |
2592 | 0 | ODSIZE(PEDIR_TLS) = aligned_sotls ? (sizeof(LEXX) == 4 ? 0x18 : 0x28) : 0; |
2593 | 0 | ic += aligned_sotls; |
2594 | |
|
2595 | 0 | processLoadConf(&rel, &loadconfiv, ic); |
2596 | 0 | ODADDR(PEDIR_LOAD_CONFIG) = soloadconf ? ic : 0; |
2597 | 0 | ODSIZE(PEDIR_LOAD_CONFIG) = soloadconf; |
2598 | 0 | ic += soloadconf; |
2599 | |
|
2600 | 0 | if (dbgCET) { |
2601 | 0 | int delta = ic - dbgCET->rva; |
2602 | 0 | dbgCET->rva = ic; |
2603 | 0 | dbgCET->fpos += delta; |
2604 | 0 | ODADDR(PEDIR_DEBUG) = ic; |
2605 | 0 | ODSIZE(PEDIR_DEBUG) = sizeof(*dbgCET); |
2606 | 0 | ic += sizeof(LE32) + ODSIZE(PEDIR_DEBUG); |
2607 | 0 | } |
2608 | 0 | const bool rel_at_sections_start = last_section_rsrc_only; |
2609 | |
|
2610 | 0 | ic = ncsection; |
2611 | 0 | if (!last_section_rsrc_only) |
2612 | 0 | callProcessResources(res, ic); |
2613 | 0 | if (rel_at_sections_start) |
2614 | 0 | callProcessStubRelocs(rel, ic); |
2615 | |
|
2616 | 0 | processImports2(ic, getProcessImportParam(upxsection)); |
2617 | 0 | ODADDR(PEDIR_IMPORT) = soimpdlls ? ic : 0; |
2618 | 0 | ODSIZE(PEDIR_IMPORT) = soimpdlls; |
2619 | 0 | ic += soimpdlls; |
2620 | |
|
2621 | 0 | processExports(&xport, ic); |
2622 | 0 | ODADDR(PEDIR_EXPORT) = soexport ? ic : 0; |
2623 | 0 | ODSIZE(PEDIR_EXPORT) = soexport; |
2624 | 0 | if (!isdll && opt->win32_pe.compress_exports) { |
2625 | 0 | ODADDR(PEDIR_EXPORT) = IDADDR(PEDIR_EXPORT); |
2626 | 0 | ODSIZE(PEDIR_EXPORT) = IDSIZE(PEDIR_EXPORT); |
2627 | 0 | } |
2628 | 0 | ic += soexport; |
2629 | |
|
2630 | 0 | if (!rel_at_sections_start) |
2631 | 0 | callProcessStubRelocs(rel, ic); |
2632 | | |
2633 | | // when the resource is put alone into section 3 |
2634 | 0 | const unsigned res_start = (ic + oam1) & ~oam1; |
2635 | 0 | if (last_section_rsrc_only) |
2636 | 0 | callProcessResources(res, ic = res_start); |
2637 | |
|
2638 | 0 | defineSymbols(ncsection, upxsection, sizeof(oh), identsize - identsplit, s1addr); |
2639 | 0 | defineFilterSymbols(&ft); |
2640 | 0 | relocateLoader(); |
2641 | 0 | const unsigned lsize = getLoaderSize(); |
2642 | 0 | MemBuffer loader(lsize); |
2643 | 0 | memcpy(loader, getLoader(), lsize); |
2644 | 0 | patchPackHeader(loader, lsize); |
2645 | |
|
2646 | 0 | const unsigned ncsize = |
2647 | 0 | soxrelocs + soimpdlls + soexport + (!last_section_rsrc_only ? soresources : 0); |
2648 | 0 | assert((soxrelocs == 0) == !has_oxrelocs); |
2649 | 0 | assert((ncsize == 0) == !has_ncsection); |
2650 | | |
2651 | | // this one is tricky: it seems windoze touches 4 bytes after |
2652 | | // the end of the relocation data - so we have to increase |
2653 | | // the virtual size of this section |
2654 | 0 | const unsigned ncsize_virt_increase = soxrelocs && (ncsize & oam1) == 0 ? 8 : 0; |
2655 | | |
2656 | | // fill the sections |
2657 | 0 | strcpy(osection[0].name, "UPX0"); |
2658 | 0 | strcpy(osection[1].name, "UPX1"); |
2659 | | // after some windoze debugging I found that the name of the sections |
2660 | | // DOES matter :( .rsrc is used by oleaut32.dll (TYPELIBS) |
2661 | | // and because of this lame dll, the resource stuff must be the |
2662 | | // first in the 3rd section - the author of this dll seems to be |
2663 | | // too idiot to use the data directories... M$ suxx 4 ever! |
2664 | | // ... even worse: exploder.exe in NiceTry also depends on this to |
2665 | | // locate version info |
2666 | 0 | strcpy(osection[2].name, !last_section_rsrc_only && soresources ? ".rsrc" : "UPX2"); |
2667 | |
|
2668 | 0 | osection[0].vaddr = rvamin; |
2669 | 0 | osection[1].vaddr = s1addr; |
2670 | 0 | osection[2].vaddr = ncsection; |
2671 | |
|
2672 | 0 | osection[0].size = 0; |
2673 | 0 | osection[1].size = (s1size + fam1) & ~fam1; |
2674 | 0 | osection[2].size = (ncsize + fam1) & ~fam1; |
2675 | |
|
2676 | 0 | osection[0].vsize = osection[1].vaddr - osection[0].vaddr; |
2677 | 0 | if (!last_section_rsrc_only) { |
2678 | 0 | osection[1].vsize = (osection[1].size + oam1) & ~oam1; |
2679 | 0 | osection[2].vsize = (osection[2].size + ncsize_virt_increase + oam1) & ~oam1; |
2680 | 0 | oh.imagesize = osection[2].vaddr + osection[2].vsize; |
2681 | 0 | osection[0].rawdataptr = (pe_offset + sizeof(ht) + sizeof_osection + fam1) & ~(size_t) fam1; |
2682 | 0 | osection[1].rawdataptr = osection[0].rawdataptr; |
2683 | 0 | } else { |
2684 | 0 | osection[1].vsize = osection[1].size; |
2685 | 0 | osection[2].vsize = osection[2].size; |
2686 | 0 | osection[0].rawdataptr = 0; |
2687 | 0 | osection[1].rawdataptr = (pe_offset + sizeof(ht) + sizeof_osection + fam1) & ~(size_t) fam1; |
2688 | 0 | } |
2689 | 0 | osection[2].rawdataptr = osection[1].rawdataptr + osection[1].size; |
2690 | |
|
2691 | 0 | osection[0].flags = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | |
2692 | 0 | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE; |
2693 | 0 | osection[1].flags = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | |
2694 | 0 | IMAGE_SCN_MEM_EXECUTE; |
2695 | 0 | osection[2].flags = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; |
2696 | |
|
2697 | 0 | if (last_section_rsrc_only) { |
2698 | 0 | strcpy(osection[3].name, ".rsrc"); |
2699 | 0 | osection[3].vaddr = res_start; |
2700 | 0 | osection[3].size = (soresources + fam1) & ~fam1; |
2701 | 0 | osection[3].vsize = osection[3].size; |
2702 | 0 | osection[3].rawdataptr = osection[2].rawdataptr + osection[2].size; |
2703 | 0 | osection[2].flags = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; |
2704 | 0 | osection[3].flags = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; |
2705 | 0 | oh.imagesize = (osection[3].vaddr + osection[3].vsize + oam1) & ~oam1; |
2706 | 0 | if (soresources == 0) { |
2707 | 0 | oh.objects = 3; |
2708 | 0 | mem_clear(&osection[3]); |
2709 | 0 | } |
2710 | 0 | } |
2711 | |
|
2712 | 0 | oh.bsssize = osection[0].vsize; |
2713 | 0 | oh.datasize = osection[2].vsize + (oobjs > 3 ? osection[3].vsize : 0); |
2714 | 0 | setOhDataBase(osection); |
2715 | 0 | oh.codesize = osection[1].vsize; |
2716 | 0 | oh.codebase = osection[1].vaddr; |
2717 | 0 | setOhHeaderSize(osection); |
2718 | 0 | if (rvamin < osection[0].rawdataptr) { |
2719 | 0 | throwCantPack("object alignment too small rvamin=%#x oraw=%#x", rvamin, |
2720 | 0 | unsigned(osection[0].rawdataptr)); |
2721 | 0 | } |
2722 | | |
2723 | 0 | if (opt->win32_pe.strip_relocs) |
2724 | 0 | oh.flags |= IMAGE_FILE_RELOCS_STRIPPED; |
2725 | |
|
2726 | 0 | ibuf.clear(0, oh.filealign); |
2727 | |
|
2728 | 0 | info("Image size change: %u -> %u KiB", ih.imagesize / 1024, oh.imagesize / 1024); |
2729 | |
|
2730 | 0 | infoHeader("[Writing compressed file]"); |
2731 | | |
2732 | | // write loader + compressed file |
2733 | 0 | fo->write(&oh, sizeof(oh)); |
2734 | 0 | fo->write(osection, sizeof(osection[0]) * oobjs); |
2735 | | // some alignment |
2736 | 0 | if (identsplit == identsize) { |
2737 | 0 | unsigned n = osection[!last_section_rsrc_only ? 0 : 1].rawdataptr - fo->getBytesWritten() - |
2738 | 0 | identsize; |
2739 | 0 | assert(n <= oh.filealign); |
2740 | 0 | fo->write(ibuf, n); |
2741 | 0 | } |
2742 | 0 | fo->write(loader + codesize, identsize); |
2743 | 0 | infoWriting("loader", fo->getBytesWritten()); |
2744 | 0 | fo->write(obuf, c_len); |
2745 | 0 | infoWriting("compressed data", c_len); |
2746 | 0 | fo->write(loader, codesize); |
2747 | 0 | if (opt->debug.dump_stub_loader) |
2748 | 0 | OutputFile::dump(opt->debug.dump_stub_loader, loader, codesize); |
2749 | 0 | if ((ic = fo->getBytesWritten() & (sizeof(LEXX) - 1)) != 0) |
2750 | 0 | fo->write(ibuf, sizeof(LEXX) - ic); |
2751 | 0 | fo->write(otls, aligned_sotls); |
2752 | 0 | fo->write(oloadconf, soloadconf); |
2753 | 0 | if (dbgCET) { |
2754 | 0 | ic = fo->getBytesWritten(); |
2755 | 0 | dbgCET->fpos = ic + sizeof(*dbgCET); |
2756 | 0 | dbgCET->rva = osection[1].vaddr + dbgCET->fpos - osection[1].rawdataptr; |
2757 | 0 | LE32 word; |
2758 | 0 | set_le32(&word, IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT); |
2759 | 0 | if (0) { // set all bytes t0 zero |
2760 | 0 | memset(dbgCET, 0, sizeof(*dbgCET)); |
2761 | 0 | set_le32(&word, 0); |
2762 | 0 | } |
2763 | 0 | fo->write(dbgCET, sizeof(*dbgCET)); |
2764 | 0 | fo->write(&word, sizeof(word)); |
2765 | 0 | } |
2766 | 0 | if ((ic = fo->getBytesWritten() & fam1) != 0) |
2767 | 0 | fo->write(ibuf, oh.filealign - ic); |
2768 | 0 | if (!last_section_rsrc_only) |
2769 | 0 | fo->write(oresources, soresources); |
2770 | 0 | else |
2771 | 0 | fo->write(oxrelocs, soxrelocs); |
2772 | 0 | fo->write(oimpdlls, soimpdlls); |
2773 | 0 | fo->write(oexport, soexport); |
2774 | 0 | if (!last_section_rsrc_only) |
2775 | 0 | fo->write(oxrelocs, soxrelocs); |
2776 | |
|
2777 | 0 | if ((ic = fo->getBytesWritten() & fam1) != 0) |
2778 | 0 | fo->write(ibuf, oh.filealign - ic); |
2779 | |
|
2780 | 0 | if (last_section_rsrc_only) { |
2781 | 0 | fo->write(oresources, soresources); |
2782 | 0 | if ((ic = fo->getBytesWritten() & fam1) != 0) |
2783 | 0 | fo->write(ibuf, oh.filealign - ic); |
2784 | 0 | } |
2785 | |
|
2786 | | #if 0 // (debug) print section sizes |
2787 | | printf("%-13s: program hdr : %8d bytes\n", getName(), (int) sizeof(oh)); |
2788 | | printf("%-13s: sections : %8d bytes\n", getName(), (int) sizeof(osection[0]) * oobjs); |
2789 | | printf("%-13s: ident : %8d bytes\n", getName(), (int) identsize); |
2790 | | printf("%-13s: compressed : %8d bytes\n", getName(), (int) c_len); |
2791 | | printf("%-13s: decompressor : %8d bytes\n", getName(), (int) codesize); |
2792 | | printf("%-13s: tls : %8d bytes\n", getName(), (int) sotls); |
2793 | | printf("%-13s: aligned_tls : %8d bytes\n", getName(), (int) aligned_sotls); |
2794 | | printf("%-13s: resources : %8d bytes\n", getName(), (int) soresources); |
2795 | | printf("%-13s: imports : %8d bytes\n", getName(), (int) soimpdlls); |
2796 | | printf("%-13s: exports : %8d bytes\n", getName(), (int) soexport); |
2797 | | printf("%-13s: relocs : %8d bytes\n", getName(), (int) soxrelocs); |
2798 | | printf("%-13s: loadconf : %8d bytes\n", getName(), (int) soloadconf); |
2799 | | // linker->dumpSymbols(); |
2800 | | #endif |
2801 | | |
2802 | | // verify |
2803 | 0 | verifyOverlappingDecompression(); |
2804 | | |
2805 | | // copy the overlay |
2806 | 0 | copyOverlay(fo, overlay, obuf); |
2807 | | |
2808 | | // finally check the compression ratio |
2809 | 0 | if (!checkFinalCompressionRatio(fo)) |
2810 | 0 | throwNotCompressible(); |
2811 | 0 | } Unexecuted instantiation: void PeFile::pack0<LE32, PeFile32::pe_header_t>(OutputFile*, PeFile32::pe_header_t&, PeFile32::pe_header_t&, unsigned int, unsigned long long, bool) Unexecuted instantiation: void PeFile::pack0<LE64, PeFile64::pe_header_t>(OutputFile*, PeFile64::pe_header_t&, PeFile64::pe_header_t&, unsigned int, unsigned long long, bool) |
2812 | | |
2813 | | /************************************************************************* |
2814 | | // unpack |
2815 | | **************************************************************************/ |
2816 | | |
2817 | | void PeFile::rebuildRelocs(SPAN_S(byte) & extra_info, unsigned bits, unsigned flags, |
2818 | 36 | upx_uint64_t imagebase) { |
2819 | 36 | assert(bits == 32 || bits == 64); |
2820 | 36 | if (!ODADDR(PEDIR_BASERELOC) || !ODSIZE(PEDIR_BASERELOC) || |
2821 | 2 | (flags & IMAGE_FILE_RELOCS_STRIPPED)) |
2822 | 34 | return; |
2823 | | |
2824 | 2 | if (ODSIZE(PEDIR_BASERELOC) == 8) { // some tricky dlls use this |
2825 | 0 | omemcpy(obuf + (ODADDR(PEDIR_BASERELOC) - rvamin), "\x0\x0\x0\x0\x8\x0\x0\x0", 8); |
2826 | 0 | return; |
2827 | 0 | } |
2828 | | |
2829 | | // Comments at end of this file say that compressed relocs are optional. |
2830 | | // Try to detect their presence. There might be no compressed relocs. |
2831 | 2 | #if WITH_XSPAN >= 2 && 1 |
2832 | 2 | const size_t headway = extra_info.size_bytes(); |
2833 | | #else |
2834 | | // FIXME: last 4 bytes of extra_info are the file offset of extra_info, |
2835 | | // so they should be excluded from the count of data bytes. |
2836 | | // Something is peculiar unless WITH_XSPAN >= 2. |
2837 | | // Also, optional icondir_count is strange following compressed relocs. |
2838 | | const size_t headway = 9; |
2839 | | #endif |
2840 | 2 | unsigned orig_crelocs = 0; |
2841 | 2 | byte big = 0; |
2842 | 2 | if (headway >= 8) { |
2843 | 2 | orig_crelocs = mem_size(1, get_le32(extra_info)); |
2844 | 2 | extra_info += 4; |
2845 | 2 | if (headway >= 9) { |
2846 | 2 | big = extra_info[0]; |
2847 | 2 | extra_info += 1; |
2848 | 2 | } |
2849 | 2 | } |
2850 | | |
2851 | 2 | SPAN_S_VAR(const byte, rdata, obuf + orig_crelocs, obuf); |
2852 | 2 | MemBuffer mb_wrkmem; |
2853 | 2 | unsigned relocnum = unoptimizeReloc(rdata, mb_wrkmem, obuf, orig_crelocs, bits, true); |
2854 | | |
2855 | | // 16-bit relocations |
2856 | 2 | unsigned r16 = 0; |
2857 | 2 | if (big & 6) { // count 16-bit relocations |
2858 | 0 | SPAN_S_VAR(const LE32, q, SPAN_TYPE_CAST(const LE32, rdata)); |
2859 | 0 | while (*q++) |
2860 | 0 | r16++; |
2861 | 0 | if ((big & 6) == 6) |
2862 | 0 | while (*++q) |
2863 | 0 | r16++; |
2864 | 0 | } |
2865 | 2 | Reloc rel(relocnum + r16); |
2866 | 2 | if (big & 6) { // add 16-bit relocations |
2867 | 0 | SPAN_S_VAR(const LE32, q, SPAN_TYPE_CAST(const LE32, rdata)); |
2868 | 0 | while (*q) |
2869 | 0 | rel.add_reloc(*q++ + rvamin, (big & 4) ? IMAGE_REL_BASED_LOW : IMAGE_REL_BASED_HIGH); |
2870 | 0 | if ((big & 6) == 6) |
2871 | 0 | while (*++q) |
2872 | 0 | rel.add_reloc(*q + rvamin, IMAGE_REL_BASED_HIGH); |
2873 | | // rdata = (const byte *) raw_bytes(q, 0); // advance rdata |
2874 | 0 | } |
2875 | | |
2876 | 2 | SPAN_S_VAR(byte, const wrkmem, mb_wrkmem); |
2877 | 18.7k | for (unsigned ic = 0; ic < relocnum; ic++) { |
2878 | 18.7k | OPTR_VAR(byte, const p, obuf + get_le32(wrkmem + sizeof(LE32) * ic)); |
2879 | 18.7k | if (bits == 32) |
2880 | 13.6k | set_le32(p, get_le32(p) + imagebase + rvamin); |
2881 | 5.07k | else |
2882 | 5.07k | set_le64(p, get_le64(p) + imagebase + rvamin); |
2883 | 18.7k | rel.add_reloc(rvamin + get_le32(wrkmem + sizeof(LE32) * ic), |
2884 | 18.7k | bits == 32 ? IMAGE_REL_BASED_HIGHLOW : IMAGE_REL_BASED_DIR64); |
2885 | 18.7k | } |
2886 | 2 | rel.finish(oxrelocs, soxrelocs); |
2887 | | |
2888 | 2 | omemcpy(obuf + (ODADDR(PEDIR_BASERELOC) - rvamin), oxrelocs, soxrelocs); |
2889 | 2 | delete[] oxrelocs; |
2890 | 2 | oxrelocs = nullptr; |
2891 | 2 | mb_wrkmem.dealloc(); |
2892 | | |
2893 | 2 | ODSIZE(PEDIR_BASERELOC) = soxrelocs; |
2894 | | // FIXME?: ODADDR(PEDIR_BASERELOC) for compressed relocs? |
2895 | 2 | } |
2896 | | |
2897 | 36 | void PeFile::rebuildExports() { |
2898 | 36 | if (ODSIZE(PEDIR_EXPORT) == 0 || ODADDR(PEDIR_EXPORT) == IDADDR(PEDIR_EXPORT)) |
2899 | 36 | return; // nothing to do |
2900 | | |
2901 | 0 | opt->win32_pe.compress_exports = 0; |
2902 | 0 | Export xport((char *) (byte *) ibuf - isection[2].vaddr); |
2903 | 0 | processExports(&xport); |
2904 | 0 | processExports(&xport, ODADDR(PEDIR_EXPORT)); |
2905 | 0 | omemcpy(obuf + (ODADDR(PEDIR_EXPORT) - rvamin), oexport, soexport); |
2906 | 0 | } |
2907 | | |
2908 | 36 | void PeFile::rebuildTls() { |
2909 | | // this is an easy one : just do nothing ;-) |
2910 | 36 | } |
2911 | | |
2912 | | namespace { |
2913 | | template <class T> |
2914 | | struct VPtr final { // "virtual pointer" pointing before a buffer |
2915 | | static_assert(sizeof(T) == 1); |
2916 | | SPAN_S(T) base; |
2917 | | size_t x; |
2918 | | // return base + (n - x) |
2919 | 7.77k | SPAN_S(T) operator+(size_t n) const { return base + mem_size_get_n(sizeof(T), n - x); }pefile.cpp:(anonymous namespace)::VPtr<unsigned char const>::operator+(unsigned long) const Line | Count | Source | 2919 | 93 | SPAN_S(T) operator+(size_t n) const { return base + mem_size_get_n(sizeof(T), n - x); } |
pefile.cpp:(anonymous namespace)::VPtr<unsigned char>::operator+(unsigned long) const Line | Count | Source | 2919 | 7.68k | SPAN_S(T) operator+(size_t n) const { return base + mem_size_get_n(sizeof(T), n - x); } |
|
2920 | | }; |
2921 | | } // namespace |
2922 | | |
2923 | 36 | void PeFile::rebuildResources(SPAN_S(byte) & extra_info, unsigned lastvaddr) { |
2924 | 36 | if (ODSIZE(PEDIR_RESOURCE) == 0 || IDSIZE(PEDIR_RESOURCE) == 0) |
2925 | 2 | return; |
2926 | | |
2927 | 34 | icondir_count = get_le16(extra_info); |
2928 | 34 | extra_info += 2; |
2929 | | |
2930 | 34 | const unsigned vaddr = IDADDR(PEDIR_RESOURCE); |
2931 | | |
2932 | 34 | if (vaddr < lastvaddr || (vaddr - lastvaddr) > ibuf.getSize()) |
2933 | 4 | throwCantUnpack("corrupted PE header"); |
2934 | | |
2935 | | // INFO: use VPtr for "virtual pointer" pointing before a buffer |
2936 | | //// const byte *const r = ibuf.raw_bytes(0) - lastvaddr; |
2937 | 30 | VPtr<const byte> const r{ibuf, lastvaddr}; |
2938 | 30 | Resource res(raw_bytes(r + vaddr, 0), ibuf, ibuf + ibuf.getSize()); |
2939 | 58 | while (res.next()) |
2940 | 28 | if (res.offs() > vaddr) { |
2941 | 24 | ICHECK(r + (res.offs() - 4), 4); |
2942 | 24 | unsigned origoffs = get_le32(r + (res.offs() - 4)); |
2943 | 24 | res.newoffs() = origoffs; |
2944 | 24 | omemcpy(obuf + (origoffs - rvamin), r + res.offs(), res.size()); |
2945 | 24 | if (icondir_count && res.itype() == RT_GROUP_ICON) { |
2946 | 0 | set_le16(obuf + (origoffs - rvamin + 4), icondir_count); |
2947 | 0 | icondir_count = 0; |
2948 | 0 | } |
2949 | 24 | } |
2950 | 30 | if (res.dirsize()) { |
2951 | 5 | byte *p = res.build(); |
2952 | 5 | OCHECK(obuf + (ODADDR(PEDIR_RESOURCE) - rvamin), 16); |
2953 | | // write back when the original is zeroed |
2954 | 5 | if (get_le32(obuf + (ODADDR(PEDIR_RESOURCE) - rvamin + 12)) == 0) |
2955 | 5 | omemcpy(obuf + (ODADDR(PEDIR_RESOURCE) - rvamin), p, res.dirsize()); |
2956 | 5 | } |
2957 | 30 | } |
2958 | | |
2959 | | template <typename LEXX, typename ord_mask_t> |
2960 | 42 | void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool set_oft) { |
2961 | 42 | if (ODADDR(PEDIR_IMPORT) == 0 || ODSIZE(PEDIR_IMPORT) <= sizeof(import_desc)) |
2962 | 0 | return; |
2963 | | |
2964 | 42 | OPTR_VAR(const byte, const imdata, obuf + mem_size(1, get_le32(extra_info))); |
2965 | 42 | const unsigned inamespos = mem_size(1, get_le32(extra_info + 4)); |
2966 | 42 | extra_info += 8; |
2967 | | |
2968 | 42 | unsigned sdllnames = 0; |
2969 | | |
2970 | 42 | IPTR_VAR_OFFSET(const byte, const import, IDADDR(PEDIR_IMPORT) - isection[2].vaddr); |
2971 | 42 | OPTR_VAR(const byte, p, raw_bytes(imdata, 4)); |
2972 | | |
2973 | 144 | for (; get_le32(p) != 0; ++p) { |
2974 | 102 | const byte *dname = raw_bytes(import + mem_size(1, get_le32(p)), 1); |
2975 | 102 | const unsigned dlen = strlen(dname); |
2976 | 102 | ICHECK(dname, dlen + 1); |
2977 | | |
2978 | 102 | sdllnames += dlen + 1; |
2979 | 3.77k | for (p += 8; *p;) |
2980 | 3.67k | if (*p == 1) |
2981 | 3.67k | p += 1 + strlen(p + 1) + 1; |
2982 | 0 | else if (*p == 0xff) |
2983 | 0 | p += 3; // ordinal |
2984 | 0 | else |
2985 | 0 | p += 5; |
2986 | 102 | } |
2987 | 42 | sdllnames = ALIGN_UP(sdllnames, 2u); |
2988 | | |
2989 | | // INFO: use VPtr for "virtual pointer" pointing before a buffer |
2990 | | //// byte *const Obuf = obuf.raw_bytes(0) - rvamin; |
2991 | 42 | VPtr<byte> const Obuf{obuf, rvamin}; |
2992 | 42 | SPAN_S_VAR(import_desc, im, (import_desc *) raw_bytes(Obuf + ODADDR(PEDIR_IMPORT), 0), obuf); |
2993 | 42 | SPAN_0_VAR(byte, dllnames, inamespos ? raw_bytes(Obuf + inamespos, 0) : nullptr, obuf); |
2994 | 42 | SPAN_0_VAR(byte, const importednames_start, inamespos ? dllnames + sdllnames : nullptr); |
2995 | 42 | SPAN_0_VAR(byte, importednames, importednames_start); |
2996 | | |
2997 | 143 | for (p = imdata; get_le32(p) != 0; ++p) { |
2998 | | // restore the name of the dll |
2999 | 101 | const byte *dname = raw_bytes(import + get_le32(p), 1); |
3000 | 101 | const unsigned dlen = strlen(dname); |
3001 | 101 | ICHECK(dname, dlen + 1); |
3002 | | |
3003 | 101 | const unsigned iatoffs = get_le32(p + 4) + rvamin; |
3004 | 101 | if (inamespos) { |
3005 | | // now I rebuild the dll names |
3006 | 0 | omemcpy(dllnames, dname, dlen + 1); |
3007 | 0 | im->dllname = ptr_udiff_bytes(dllnames, obuf) + rvamin; |
3008 | | //;;;printf("\ndll: %s:",dllnames); |
3009 | 0 | dllnames += dlen + 1; |
3010 | 101 | } else { |
3011 | 101 | omemcpy(Obuf + im->dllname, dname, dlen + 1); |
3012 | 101 | } |
3013 | 101 | im->iat = iatoffs; |
3014 | 101 | if (set_oft) |
3015 | 6 | im->oft = iatoffs; |
3016 | | |
3017 | 101 | OPTR_VAR(LEXX, newiat, (LEXX *) raw_bytes(Obuf + iatoffs, 0)); |
3018 | | |
3019 | | // restore the imported names+ordinals |
3020 | 3.77k | for (p += 8; *p; ++newiat) |
3021 | 3.67k | if (*p == 1) { |
3022 | 3.67k | const unsigned ilen = strlen(++p) + 1; |
3023 | 3.67k | if (inamespos) { |
3024 | 0 | if (ptr_udiff_bytes(importednames, importednames_start) & 1) |
3025 | 0 | importednames -= 1; |
3026 | 0 | omemcpy(importednames + 2, p, ilen); |
3027 | | //;;;printf(" %s",importednames+2); |
3028 | 0 | *newiat = ptr_udiff_bytes(importednames, obuf) + rvamin; |
3029 | 0 | importednames += 2 + ilen; |
3030 | 3.67k | } else { |
3031 | | // Beware overlap! |
3032 | 3.67k | omemmove(Obuf + (*newiat + 2), p, ilen); |
3033 | 3.67k | } |
3034 | 3.67k | p += ilen; |
3035 | 3.67k | } else if (*p == 0xff) { |
3036 | 0 | *newiat = get_le16(p + 1) + ord_mask; |
3037 | | //;;;printf(" %x",(unsigned)*newiat); |
3038 | 0 | p += 3; |
3039 | 0 | } else { |
3040 | 0 | *newiat = *(const LEXX *) raw_bytes(import + get_le32(p + 1), sizeof(LEXX)); |
3041 | 0 | assert(*newiat & ord_mask); |
3042 | 0 | p += 5; |
3043 | 0 | } |
3044 | 101 | *newiat = 0; |
3045 | 101 | im++; |
3046 | 101 | } |
3047 | | // memset(imdata, 0, ptr_udiff_bytes(p, imdata)); |
3048 | 42 | } void PeFile::rebuildImports<LE32, unsigned int>(XSpan::Span<unsigned char>&, unsigned int, bool) Line | Count | Source | 2960 | 37 | void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool set_oft) { | 2961 | 37 | if (ODADDR(PEDIR_IMPORT) == 0 || ODSIZE(PEDIR_IMPORT) <= sizeof(import_desc)) | 2962 | 0 | return; | 2963 | | | 2964 | 37 | OPTR_VAR(const byte, const imdata, obuf + mem_size(1, get_le32(extra_info))); | 2965 | 37 | const unsigned inamespos = mem_size(1, get_le32(extra_info + 4)); | 2966 | 37 | extra_info += 8; | 2967 | | | 2968 | 37 | unsigned sdllnames = 0; | 2969 | | | 2970 | 37 | IPTR_VAR_OFFSET(const byte, const import, IDADDR(PEDIR_IMPORT) - isection[2].vaddr); | 2971 | 37 | OPTR_VAR(const byte, p, raw_bytes(imdata, 4)); | 2972 | | | 2973 | 135 | for (; get_le32(p) != 0; ++p) { | 2974 | 98 | const byte *dname = raw_bytes(import + mem_size(1, get_le32(p)), 1); | 2975 | 98 | const unsigned dlen = strlen(dname); | 2976 | 98 | ICHECK(dname, dlen + 1); | 2977 | | | 2978 | 98 | sdllnames += dlen + 1; | 2979 | 3.58k | for (p += 8; *p;) | 2980 | 3.48k | if (*p == 1) | 2981 | 3.48k | p += 1 + strlen(p + 1) + 1; | 2982 | 0 | else if (*p == 0xff) | 2983 | 0 | p += 3; // ordinal | 2984 | 0 | else | 2985 | 0 | p += 5; | 2986 | 98 | } | 2987 | 37 | sdllnames = ALIGN_UP(sdllnames, 2u); | 2988 | | | 2989 | | // INFO: use VPtr for "virtual pointer" pointing before a buffer | 2990 | | //// byte *const Obuf = obuf.raw_bytes(0) - rvamin; | 2991 | 37 | VPtr<byte> const Obuf{obuf, rvamin}; | 2992 | 37 | SPAN_S_VAR(import_desc, im, (import_desc *) raw_bytes(Obuf + ODADDR(PEDIR_IMPORT), 0), obuf); | 2993 | 37 | SPAN_0_VAR(byte, dllnames, inamespos ? raw_bytes(Obuf + inamespos, 0) : nullptr, obuf); | 2994 | 37 | SPAN_0_VAR(byte, const importednames_start, inamespos ? dllnames + sdllnames : nullptr); | 2995 | 37 | SPAN_0_VAR(byte, importednames, importednames_start); | 2996 | | | 2997 | 134 | for (p = imdata; get_le32(p) != 0; ++p) { | 2998 | | // restore the name of the dll | 2999 | 97 | const byte *dname = raw_bytes(import + get_le32(p), 1); | 3000 | 97 | const unsigned dlen = strlen(dname); | 3001 | 97 | ICHECK(dname, dlen + 1); | 3002 | | | 3003 | 97 | const unsigned iatoffs = get_le32(p + 4) + rvamin; | 3004 | 97 | if (inamespos) { | 3005 | | // now I rebuild the dll names | 3006 | 0 | omemcpy(dllnames, dname, dlen + 1); | 3007 | 0 | im->dllname = ptr_udiff_bytes(dllnames, obuf) + rvamin; | 3008 | | //;;;printf("\ndll: %s:",dllnames); | 3009 | 0 | dllnames += dlen + 1; | 3010 | 97 | } else { | 3011 | 97 | omemcpy(Obuf + im->dllname, dname, dlen + 1); | 3012 | 97 | } | 3013 | 97 | im->iat = iatoffs; | 3014 | 97 | if (set_oft) | 3015 | 6 | im->oft = iatoffs; | 3016 | | | 3017 | 97 | OPTR_VAR(LEXX, newiat, (LEXX *) raw_bytes(Obuf + iatoffs, 0)); | 3018 | | | 3019 | | // restore the imported names+ordinals | 3020 | 3.58k | for (p += 8; *p; ++newiat) | 3021 | 3.48k | if (*p == 1) { | 3022 | 3.48k | const unsigned ilen = strlen(++p) + 1; | 3023 | 3.48k | if (inamespos) { | 3024 | 0 | if (ptr_udiff_bytes(importednames, importednames_start) & 1) | 3025 | 0 | importednames -= 1; | 3026 | 0 | omemcpy(importednames + 2, p, ilen); | 3027 | | //;;;printf(" %s",importednames+2); | 3028 | 0 | *newiat = ptr_udiff_bytes(importednames, obuf) + rvamin; | 3029 | 0 | importednames += 2 + ilen; | 3030 | 3.48k | } else { | 3031 | | // Beware overlap! | 3032 | 3.48k | omemmove(Obuf + (*newiat + 2), p, ilen); | 3033 | 3.48k | } | 3034 | 3.48k | p += ilen; | 3035 | 3.48k | } else if (*p == 0xff) { | 3036 | 0 | *newiat = get_le16(p + 1) + ord_mask; | 3037 | | //;;;printf(" %x",(unsigned)*newiat); | 3038 | 0 | p += 3; | 3039 | 0 | } else { | 3040 | 0 | *newiat = *(const LEXX *) raw_bytes(import + get_le32(p + 1), sizeof(LEXX)); | 3041 | 0 | assert(*newiat & ord_mask); | 3042 | 0 | p += 5; | 3043 | 0 | } | 3044 | 97 | *newiat = 0; | 3045 | 97 | im++; | 3046 | 97 | } | 3047 | | // memset(imdata, 0, ptr_udiff_bytes(p, imdata)); | 3048 | 37 | } |
void PeFile::rebuildImports<LE64, unsigned long long>(XSpan::Span<unsigned char>&, unsigned long long, bool) Line | Count | Source | 2960 | 5 | void PeFile::rebuildImports(SPAN_S(byte) & extra_info, ord_mask_t ord_mask, bool set_oft) { | 2961 | 5 | if (ODADDR(PEDIR_IMPORT) == 0 || ODSIZE(PEDIR_IMPORT) <= sizeof(import_desc)) | 2962 | 0 | return; | 2963 | | | 2964 | 5 | OPTR_VAR(const byte, const imdata, obuf + mem_size(1, get_le32(extra_info))); | 2965 | 5 | const unsigned inamespos = mem_size(1, get_le32(extra_info + 4)); | 2966 | 5 | extra_info += 8; | 2967 | | | 2968 | 5 | unsigned sdllnames = 0; | 2969 | | | 2970 | 5 | IPTR_VAR_OFFSET(const byte, const import, IDADDR(PEDIR_IMPORT) - isection[2].vaddr); | 2971 | 5 | OPTR_VAR(const byte, p, raw_bytes(imdata, 4)); | 2972 | | | 2973 | 9 | for (; get_le32(p) != 0; ++p) { | 2974 | 4 | const byte *dname = raw_bytes(import + mem_size(1, get_le32(p)), 1); | 2975 | 4 | const unsigned dlen = strlen(dname); | 2976 | 4 | ICHECK(dname, dlen + 1); | 2977 | | | 2978 | 4 | sdllnames += dlen + 1; | 2979 | 189 | for (p += 8; *p;) | 2980 | 185 | if (*p == 1) | 2981 | 185 | p += 1 + strlen(p + 1) + 1; | 2982 | 0 | else if (*p == 0xff) | 2983 | 0 | p += 3; // ordinal | 2984 | 0 | else | 2985 | 0 | p += 5; | 2986 | 4 | } | 2987 | 5 | sdllnames = ALIGN_UP(sdllnames, 2u); | 2988 | | | 2989 | | // INFO: use VPtr for "virtual pointer" pointing before a buffer | 2990 | | //// byte *const Obuf = obuf.raw_bytes(0) - rvamin; | 2991 | 5 | VPtr<byte> const Obuf{obuf, rvamin}; | 2992 | 5 | SPAN_S_VAR(import_desc, im, (import_desc *) raw_bytes(Obuf + ODADDR(PEDIR_IMPORT), 0), obuf); | 2993 | 5 | SPAN_0_VAR(byte, dllnames, inamespos ? raw_bytes(Obuf + inamespos, 0) : nullptr, obuf); | 2994 | 5 | SPAN_0_VAR(byte, const importednames_start, inamespos ? dllnames + sdllnames : nullptr); | 2995 | 5 | SPAN_0_VAR(byte, importednames, importednames_start); | 2996 | | | 2997 | 9 | for (p = imdata; get_le32(p) != 0; ++p) { | 2998 | | // restore the name of the dll | 2999 | 4 | const byte *dname = raw_bytes(import + get_le32(p), 1); | 3000 | 4 | const unsigned dlen = strlen(dname); | 3001 | 4 | ICHECK(dname, dlen + 1); | 3002 | | | 3003 | 4 | const unsigned iatoffs = get_le32(p + 4) + rvamin; | 3004 | 4 | if (inamespos) { | 3005 | | // now I rebuild the dll names | 3006 | 0 | omemcpy(dllnames, dname, dlen + 1); | 3007 | 0 | im->dllname = ptr_udiff_bytes(dllnames, obuf) + rvamin; | 3008 | | //;;;printf("\ndll: %s:",dllnames); | 3009 | 0 | dllnames += dlen + 1; | 3010 | 4 | } else { | 3011 | 4 | omemcpy(Obuf + im->dllname, dname, dlen + 1); | 3012 | 4 | } | 3013 | 4 | im->iat = iatoffs; | 3014 | 4 | if (set_oft) | 3015 | 0 | im->oft = iatoffs; | 3016 | | | 3017 | 4 | OPTR_VAR(LEXX, newiat, (LEXX *) raw_bytes(Obuf + iatoffs, 0)); | 3018 | | | 3019 | | // restore the imported names+ordinals | 3020 | 189 | for (p += 8; *p; ++newiat) | 3021 | 185 | if (*p == 1) { | 3022 | 185 | const unsigned ilen = strlen(++p) + 1; | 3023 | 185 | if (inamespos) { | 3024 | 0 | if (ptr_udiff_bytes(importednames, importednames_start) & 1) | 3025 | 0 | importednames -= 1; | 3026 | 0 | omemcpy(importednames + 2, p, ilen); | 3027 | | //;;;printf(" %s",importednames+2); | 3028 | 0 | *newiat = ptr_udiff_bytes(importednames, obuf) + rvamin; | 3029 | 0 | importednames += 2 + ilen; | 3030 | 185 | } else { | 3031 | | // Beware overlap! | 3032 | 185 | omemmove(Obuf + (*newiat + 2), p, ilen); | 3033 | 185 | } | 3034 | 185 | p += ilen; | 3035 | 185 | } else if (*p == 0xff) { | 3036 | 0 | *newiat = get_le16(p + 1) + ord_mask; | 3037 | | //;;;printf(" %x",(unsigned)*newiat); | 3038 | 0 | p += 3; | 3039 | 0 | } else { | 3040 | 0 | *newiat = *(const LEXX *) raw_bytes(import + get_le32(p + 1), sizeof(LEXX)); | 3041 | 0 | assert(*newiat & ord_mask); | 3042 | 0 | p += 5; | 3043 | 0 | } | 3044 | 4 | *newiat = 0; | 3045 | 4 | im++; | 3046 | 4 | } | 3047 | | // memset(imdata, 0, ptr_udiff_bytes(p, imdata)); | 3048 | 5 | } |
|
3049 | | |
3050 | | template <typename ht, typename LEXX, typename ord_mask_t> |
3051 | 90 | void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, bool set_oft) { |
3052 | | // infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), |
3053 | | // objs); |
3054 | | |
3055 | 90 | handleStub(fi, fo, pe_offset); |
3056 | 90 | if (ih.filealign == 0) |
3057 | 2 | throwCantUnpack("unexpected value in the PE header"); |
3058 | | |
3059 | 88 | const unsigned iobjs = ih.objects; |
3060 | 88 | const unsigned overlay = |
3061 | 88 | file_size_u - |
3062 | 88 | ALIGN_UP(isection[iobjs - 1].rawdataptr + isection[iobjs - 1].size, ih.filealign); |
3063 | 88 | checkOverlay(overlay); |
3064 | | |
3065 | 88 | ibuf.alloc(ph.c_len); |
3066 | 88 | obuf.allocForDecompression(ph.u_len); |
3067 | 88 | fi->seek(isection[1].rawdataptr - 64 + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET); |
3068 | 88 | fi->readx(ibuf, ibufgood = ph.c_len); |
3069 | | |
3070 | | // decompress |
3071 | 88 | decompress(ibuf, obuf); |
3072 | 88 | unsigned skip = get_le32(obuf + (ph.u_len - 4)); |
3073 | 88 | unsigned take = sizeof(oh); |
3074 | 88 | SPAN_S_VAR(byte, extra_info, obuf); |
3075 | 88 | extra_info = obuf.subref("bad extra_info offset %#x", skip, take); |
3076 | | // byte *const eistart = raw_bytes(extra_info, 0); |
3077 | | |
3078 | 88 | memcpy(&oh, extra_info, take); |
3079 | 88 | extra_info += take; |
3080 | 88 | skip += take; |
3081 | 88 | unsigned objs = oh.objects; |
3082 | | |
3083 | 88 | if ((int) objs <= 0 || (iobjs > 2 && isection[2].size == 0)) |
3084 | 2 | throwCantUnpack("unexpected value in the PE header"); |
3085 | 86 | Array(pe_section_t, osection, objs); |
3086 | 86 | take = sizeof(pe_section_t) * objs; |
3087 | 86 | extra_info = obuf.subref("bad extra section size at %#x", skip, take); |
3088 | 86 | memcpy(osection, extra_info, take); |
3089 | 86 | extra_info += take; |
3090 | 86 | skip += take; |
3091 | 86 | rvamin = osection[0].vaddr; |
3092 | | |
3093 | 86 | if (iobjs > 2) { |
3094 | | // read the noncompressed section |
3095 | 45 | const unsigned size = isection[2].size; |
3096 | 45 | ibuf.dealloc(); |
3097 | 45 | ibuf.alloc(size + 1); |
3098 | 45 | fi->seek(isection[2].rawdataptr, SEEK_SET); |
3099 | 45 | fi->readx(ibuf, ibufgood = size); |
3100 | 45 | ibuf[size] = 0; // allow strlen() up to 'size' |
3101 | 45 | } |
3102 | | |
3103 | | // unfilter |
3104 | 86 | if (ph.filter) { |
3105 | 42 | Filter ft(ph.level); |
3106 | 42 | ft.init(ph.filter, oh.codebase - rvamin); |
3107 | 42 | ft.cto = (byte) ph.filter_cto; |
3108 | 42 | OCHECK(obuf + (oh.codebase - rvamin), oh.codesize); |
3109 | 42 | ft.unfilter(obuf + (oh.codebase - rvamin), oh.codesize); |
3110 | 42 | } |
3111 | | |
3112 | | // FIXME: ih.flags is checked here because of a bug in UPX 0.92 |
3113 | 86 | if (ih.flags & IMAGE_FILE_RELOCS_STRIPPED) { |
3114 | 38 | oh.flags |= IMAGE_FILE_RELOCS_STRIPPED; |
3115 | 38 | ODADDR(PEDIR_BASERELOC) = 0; |
3116 | 38 | ODSIZE(PEDIR_BASERELOC) = 0; |
3117 | 38 | } |
3118 | | |
3119 | 86 | rebuildImports<LEXX>(extra_info, ord_mask, set_oft); |
3120 | 86 | rebuildRelocs(extra_info, sizeof(ih.imagebase) * 8, oh.flags, oh.imagebase); |
3121 | 86 | rebuildTls(); |
3122 | 86 | rebuildExports(); |
3123 | | |
3124 | 86 | if (iobjs > 3) { |
3125 | | // read the resource section if present |
3126 | 2 | ibuf.dealloc(); |
3127 | 2 | ibuf.alloc(isection[3].size); |
3128 | 2 | fi->seek(isection[3].rawdataptr, SEEK_SET); |
3129 | 2 | fi->readx(ibuf, ibufgood = isection[3].size); |
3130 | 2 | } |
3131 | | |
3132 | 86 | rebuildResources(extra_info, isection[ih.objects - 1].vaddr); |
3133 | | |
3134 | | // FIXME: this does bad things if the relocation section got removed |
3135 | | // during compression ... |
3136 | | // memset(eistart, 0, ptr_udiff_bytes(extra_info, eistart) + 4); |
3137 | | |
3138 | | // fill the data directory |
3139 | 86 | ODADDR(PEDIR_IAT) = 0; |
3140 | 86 | ODSIZE(PEDIR_IAT) = 0; |
3141 | 86 | ODADDR(PEDIR_BOUND_IMPORT) = 0; |
3142 | 86 | ODSIZE(PEDIR_BOUND_IMPORT) = 0; |
3143 | | |
3144 | 86 | setOhHeaderSize(osection); |
3145 | 86 | oh.chksum = 0; |
3146 | | |
3147 | | // write decompressed file |
3148 | 86 | if (fo) { |
3149 | 0 | unsigned ic = 0; |
3150 | 0 | while (ic < objs && osection[ic].rawdataptr == 0) |
3151 | 0 | ic++; |
3152 | |
|
3153 | 0 | ibuf.dealloc(); |
3154 | 0 | ibuf.alloc(osection[ic].rawdataptr); |
3155 | 0 | ibuf.clear(); |
3156 | 0 | infoHeader("[Writing uncompressed file]"); |
3157 | | |
3158 | | // write header + decompressed file |
3159 | 0 | fo->write(&oh, sizeof(oh)); |
3160 | 0 | fo->write(osection, objs * sizeof(pe_section_t)); |
3161 | 0 | fo->write(ibuf, osection[ic].rawdataptr - fo->getBytesWritten()); |
3162 | 0 | for (ic = 0; ic < objs; ic++) |
3163 | 0 | if (osection[ic].rawdataptr) |
3164 | 0 | fo->write(obuf + (osection[ic].vaddr - rvamin), |
3165 | 0 | ALIGN_UP(osection[ic].size, oh.filealign)); |
3166 | 0 | copyOverlay(fo, overlay, obuf); |
3167 | 0 | } |
3168 | 86 | ibuf.dealloc(); |
3169 | 86 | } void PeFile::unpack0<PeFile32::pe_header_t, LE32, unsigned int>(OutputFile*, PeFile32::pe_header_t const&, PeFile32::pe_header_t&, unsigned int, bool) Line | Count | Source | 3051 | 80 | void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, bool set_oft) { | 3052 | | // infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), | 3053 | | // objs); | 3054 | | | 3055 | 80 | handleStub(fi, fo, pe_offset); | 3056 | 80 | if (ih.filealign == 0) | 3057 | 1 | throwCantUnpack("unexpected value in the PE header"); | 3058 | | | 3059 | 79 | const unsigned iobjs = ih.objects; | 3060 | 79 | const unsigned overlay = | 3061 | 79 | file_size_u - | 3062 | 79 | ALIGN_UP(isection[iobjs - 1].rawdataptr + isection[iobjs - 1].size, ih.filealign); | 3063 | 79 | checkOverlay(overlay); | 3064 | | | 3065 | 79 | ibuf.alloc(ph.c_len); | 3066 | 79 | obuf.allocForDecompression(ph.u_len); | 3067 | 79 | fi->seek(isection[1].rawdataptr - 64 + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET); | 3068 | 79 | fi->readx(ibuf, ibufgood = ph.c_len); | 3069 | | | 3070 | | // decompress | 3071 | 79 | decompress(ibuf, obuf); | 3072 | 79 | unsigned skip = get_le32(obuf + (ph.u_len - 4)); | 3073 | 79 | unsigned take = sizeof(oh); | 3074 | 79 | SPAN_S_VAR(byte, extra_info, obuf); | 3075 | 79 | extra_info = obuf.subref("bad extra_info offset %#x", skip, take); | 3076 | | // byte *const eistart = raw_bytes(extra_info, 0); | 3077 | | | 3078 | 79 | memcpy(&oh, extra_info, take); | 3079 | 79 | extra_info += take; | 3080 | 79 | skip += take; | 3081 | 79 | unsigned objs = oh.objects; | 3082 | | | 3083 | 79 | if ((int) objs <= 0 || (iobjs > 2 && isection[2].size == 0)) | 3084 | 1 | throwCantUnpack("unexpected value in the PE header"); | 3085 | 78 | Array(pe_section_t, osection, objs); | 3086 | 78 | take = sizeof(pe_section_t) * objs; | 3087 | 78 | extra_info = obuf.subref("bad extra section size at %#x", skip, take); | 3088 | 78 | memcpy(osection, extra_info, take); | 3089 | 78 | extra_info += take; | 3090 | 78 | skip += take; | 3091 | 78 | rvamin = osection[0].vaddr; | 3092 | | | 3093 | 78 | if (iobjs > 2) { | 3094 | | // read the noncompressed section | 3095 | 39 | const unsigned size = isection[2].size; | 3096 | 39 | ibuf.dealloc(); | 3097 | 39 | ibuf.alloc(size + 1); | 3098 | 39 | fi->seek(isection[2].rawdataptr, SEEK_SET); | 3099 | 39 | fi->readx(ibuf, ibufgood = size); | 3100 | 39 | ibuf[size] = 0; // allow strlen() up to 'size' | 3101 | 39 | } | 3102 | | | 3103 | | // unfilter | 3104 | 78 | if (ph.filter) { | 3105 | 37 | Filter ft(ph.level); | 3106 | 37 | ft.init(ph.filter, oh.codebase - rvamin); | 3107 | 37 | ft.cto = (byte) ph.filter_cto; | 3108 | 37 | OCHECK(obuf + (oh.codebase - rvamin), oh.codesize); | 3109 | 37 | ft.unfilter(obuf + (oh.codebase - rvamin), oh.codesize); | 3110 | 37 | } | 3111 | | | 3112 | | // FIXME: ih.flags is checked here because of a bug in UPX 0.92 | 3113 | 78 | if (ih.flags & IMAGE_FILE_RELOCS_STRIPPED) { | 3114 | 36 | oh.flags |= IMAGE_FILE_RELOCS_STRIPPED; | 3115 | 36 | ODADDR(PEDIR_BASERELOC) = 0; | 3116 | 36 | ODSIZE(PEDIR_BASERELOC) = 0; | 3117 | 36 | } | 3118 | | | 3119 | 78 | rebuildImports<LEXX>(extra_info, ord_mask, set_oft); | 3120 | 78 | rebuildRelocs(extra_info, sizeof(ih.imagebase) * 8, oh.flags, oh.imagebase); | 3121 | 78 | rebuildTls(); | 3122 | 78 | rebuildExports(); | 3123 | | | 3124 | 78 | if (iobjs > 3) { | 3125 | | // read the resource section if present | 3126 | 2 | ibuf.dealloc(); | 3127 | 2 | ibuf.alloc(isection[3].size); | 3128 | 2 | fi->seek(isection[3].rawdataptr, SEEK_SET); | 3129 | 2 | fi->readx(ibuf, ibufgood = isection[3].size); | 3130 | 2 | } | 3131 | | | 3132 | 78 | rebuildResources(extra_info, isection[ih.objects - 1].vaddr); | 3133 | | | 3134 | | // FIXME: this does bad things if the relocation section got removed | 3135 | | // during compression ... | 3136 | | // memset(eistart, 0, ptr_udiff_bytes(extra_info, eistart) + 4); | 3137 | | | 3138 | | // fill the data directory | 3139 | 78 | ODADDR(PEDIR_IAT) = 0; | 3140 | 78 | ODSIZE(PEDIR_IAT) = 0; | 3141 | 78 | ODADDR(PEDIR_BOUND_IMPORT) = 0; | 3142 | 78 | ODSIZE(PEDIR_BOUND_IMPORT) = 0; | 3143 | | | 3144 | 78 | setOhHeaderSize(osection); | 3145 | 78 | oh.chksum = 0; | 3146 | | | 3147 | | // write decompressed file | 3148 | 78 | if (fo) { | 3149 | 0 | unsigned ic = 0; | 3150 | 0 | while (ic < objs && osection[ic].rawdataptr == 0) | 3151 | 0 | ic++; | 3152 | |
| 3153 | 0 | ibuf.dealloc(); | 3154 | 0 | ibuf.alloc(osection[ic].rawdataptr); | 3155 | 0 | ibuf.clear(); | 3156 | 0 | infoHeader("[Writing uncompressed file]"); | 3157 | | | 3158 | | // write header + decompressed file | 3159 | 0 | fo->write(&oh, sizeof(oh)); | 3160 | 0 | fo->write(osection, objs * sizeof(pe_section_t)); | 3161 | 0 | fo->write(ibuf, osection[ic].rawdataptr - fo->getBytesWritten()); | 3162 | 0 | for (ic = 0; ic < objs; ic++) | 3163 | 0 | if (osection[ic].rawdataptr) | 3164 | 0 | fo->write(obuf + (osection[ic].vaddr - rvamin), | 3165 | 0 | ALIGN_UP(osection[ic].size, oh.filealign)); | 3166 | 0 | copyOverlay(fo, overlay, obuf); | 3167 | 0 | } | 3168 | 78 | ibuf.dealloc(); | 3169 | 78 | } |
void PeFile::unpack0<PeFile64::pe_header_t, LE64, unsigned long long>(OutputFile*, PeFile64::pe_header_t const&, PeFile64::pe_header_t&, unsigned long long, bool) Line | Count | Source | 3051 | 10 | void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh, ord_mask_t ord_mask, bool set_oft) { | 3052 | | // infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), | 3053 | | // objs); | 3054 | | | 3055 | 10 | handleStub(fi, fo, pe_offset); | 3056 | 10 | if (ih.filealign == 0) | 3057 | 1 | throwCantUnpack("unexpected value in the PE header"); | 3058 | | | 3059 | 9 | const unsigned iobjs = ih.objects; | 3060 | 9 | const unsigned overlay = | 3061 | 9 | file_size_u - | 3062 | 9 | ALIGN_UP(isection[iobjs - 1].rawdataptr + isection[iobjs - 1].size, ih.filealign); | 3063 | 9 | checkOverlay(overlay); | 3064 | | | 3065 | 9 | ibuf.alloc(ph.c_len); | 3066 | 9 | obuf.allocForDecompression(ph.u_len); | 3067 | 9 | fi->seek(isection[1].rawdataptr - 64 + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET); | 3068 | 9 | fi->readx(ibuf, ibufgood = ph.c_len); | 3069 | | | 3070 | | // decompress | 3071 | 9 | decompress(ibuf, obuf); | 3072 | 9 | unsigned skip = get_le32(obuf + (ph.u_len - 4)); | 3073 | 9 | unsigned take = sizeof(oh); | 3074 | 9 | SPAN_S_VAR(byte, extra_info, obuf); | 3075 | 9 | extra_info = obuf.subref("bad extra_info offset %#x", skip, take); | 3076 | | // byte *const eistart = raw_bytes(extra_info, 0); | 3077 | | | 3078 | 9 | memcpy(&oh, extra_info, take); | 3079 | 9 | extra_info += take; | 3080 | 9 | skip += take; | 3081 | 9 | unsigned objs = oh.objects; | 3082 | | | 3083 | 9 | if ((int) objs <= 0 || (iobjs > 2 && isection[2].size == 0)) | 3084 | 1 | throwCantUnpack("unexpected value in the PE header"); | 3085 | 8 | Array(pe_section_t, osection, objs); | 3086 | 8 | take = sizeof(pe_section_t) * objs; | 3087 | 8 | extra_info = obuf.subref("bad extra section size at %#x", skip, take); | 3088 | 8 | memcpy(osection, extra_info, take); | 3089 | 8 | extra_info += take; | 3090 | 8 | skip += take; | 3091 | 8 | rvamin = osection[0].vaddr; | 3092 | | | 3093 | 8 | if (iobjs > 2) { | 3094 | | // read the noncompressed section | 3095 | 6 | const unsigned size = isection[2].size; | 3096 | 6 | ibuf.dealloc(); | 3097 | 6 | ibuf.alloc(size + 1); | 3098 | 6 | fi->seek(isection[2].rawdataptr, SEEK_SET); | 3099 | 6 | fi->readx(ibuf, ibufgood = size); | 3100 | 6 | ibuf[size] = 0; // allow strlen() up to 'size' | 3101 | 6 | } | 3102 | | | 3103 | | // unfilter | 3104 | 8 | if (ph.filter) { | 3105 | 5 | Filter ft(ph.level); | 3106 | 5 | ft.init(ph.filter, oh.codebase - rvamin); | 3107 | 5 | ft.cto = (byte) ph.filter_cto; | 3108 | 5 | OCHECK(obuf + (oh.codebase - rvamin), oh.codesize); | 3109 | 5 | ft.unfilter(obuf + (oh.codebase - rvamin), oh.codesize); | 3110 | 5 | } | 3111 | | | 3112 | | // FIXME: ih.flags is checked here because of a bug in UPX 0.92 | 3113 | 8 | if (ih.flags & IMAGE_FILE_RELOCS_STRIPPED) { | 3114 | 2 | oh.flags |= IMAGE_FILE_RELOCS_STRIPPED; | 3115 | 2 | ODADDR(PEDIR_BASERELOC) = 0; | 3116 | 2 | ODSIZE(PEDIR_BASERELOC) = 0; | 3117 | 2 | } | 3118 | | | 3119 | 8 | rebuildImports<LEXX>(extra_info, ord_mask, set_oft); | 3120 | 8 | rebuildRelocs(extra_info, sizeof(ih.imagebase) * 8, oh.flags, oh.imagebase); | 3121 | 8 | rebuildTls(); | 3122 | 8 | rebuildExports(); | 3123 | | | 3124 | 8 | if (iobjs > 3) { | 3125 | | // read the resource section if present | 3126 | 0 | ibuf.dealloc(); | 3127 | 0 | ibuf.alloc(isection[3].size); | 3128 | 0 | fi->seek(isection[3].rawdataptr, SEEK_SET); | 3129 | 0 | fi->readx(ibuf, ibufgood = isection[3].size); | 3130 | 0 | } | 3131 | | | 3132 | 8 | rebuildResources(extra_info, isection[ih.objects - 1].vaddr); | 3133 | | | 3134 | | // FIXME: this does bad things if the relocation section got removed | 3135 | | // during compression ... | 3136 | | // memset(eistart, 0, ptr_udiff_bytes(extra_info, eistart) + 4); | 3137 | | | 3138 | | // fill the data directory | 3139 | 8 | ODADDR(PEDIR_IAT) = 0; | 3140 | 8 | ODSIZE(PEDIR_IAT) = 0; | 3141 | 8 | ODADDR(PEDIR_BOUND_IMPORT) = 0; | 3142 | 8 | ODSIZE(PEDIR_BOUND_IMPORT) = 0; | 3143 | | | 3144 | 8 | setOhHeaderSize(osection); | 3145 | 8 | oh.chksum = 0; | 3146 | | | 3147 | | // write decompressed file | 3148 | 8 | if (fo) { | 3149 | 0 | unsigned ic = 0; | 3150 | 0 | while (ic < objs && osection[ic].rawdataptr == 0) | 3151 | 0 | ic++; | 3152 | |
| 3153 | 0 | ibuf.dealloc(); | 3154 | 0 | ibuf.alloc(osection[ic].rawdataptr); | 3155 | 0 | ibuf.clear(); | 3156 | 0 | infoHeader("[Writing uncompressed file]"); | 3157 | | | 3158 | | // write header + decompressed file | 3159 | 0 | fo->write(&oh, sizeof(oh)); | 3160 | 0 | fo->write(osection, objs * sizeof(pe_section_t)); | 3161 | 0 | fo->write(ibuf, osection[ic].rawdataptr - fo->getBytesWritten()); | 3162 | 0 | for (ic = 0; ic < objs; ic++) | 3163 | 0 | if (osection[ic].rawdataptr) | 3164 | 0 | fo->write(obuf + (osection[ic].vaddr - rvamin), | 3165 | 0 | ALIGN_UP(osection[ic].size, oh.filealign)); | 3166 | 0 | copyOverlay(fo, overlay, obuf); | 3167 | 0 | } | 3168 | 8 | ibuf.dealloc(); | 3169 | 8 | } |
|
3170 | | |
3171 | 231 | int PeFile::canUnpack0(unsigned max_sections, unsigned objs, unsigned ih_entry, unsigned ih_size) { |
3172 | 231 | const unsigned min_sections = isefi ? 2 : 3; |
3173 | 231 | if (objs < min_sections) |
3174 | 2 | return -1; |
3175 | 229 | mb_isection.alloc(mem_size(sizeof(pe_section_t), objs)); |
3176 | 229 | isection = SPAN_S_MAKE(pe_section_t, mb_isection); // => isection now is a SPAN_S |
3177 | 229 | fi->seek(pe_offset + ih_size, SEEK_SET); |
3178 | 229 | fi->readx(isection, sizeof(pe_section_t) * objs); |
3179 | 229 | bool is_packed = (objs <= max_sections && (IDSIZE(15) || ih_entry > isection[1].vaddr)); |
3180 | 229 | bool found_ph = false; |
3181 | 229 | if (memcmp(isection[0].name, "UPX", 3) == 0) { |
3182 | | // current version |
3183 | 115 | fi->seek(isection[1].rawdataptr - 64, SEEK_SET); |
3184 | 115 | found_ph = readPackHeader(1024); |
3185 | 115 | if (!found_ph) { |
3186 | | // old versions |
3187 | 19 | fi->seek(isection[2].rawdataptr, SEEK_SET); |
3188 | 19 | found_ph = readPackHeader(1024); |
3189 | 19 | } |
3190 | 115 | } |
3191 | 229 | if (is_packed && found_ph) |
3192 | 90 | return true; |
3193 | 139 | if (!is_packed && !found_ph) |
3194 | 17 | return -1; |
3195 | 122 | if (is_packed && ih_entry < isection[2].vaddr) { |
3196 | 45 | byte buf[256]; |
3197 | 45 | bool x = false; |
3198 | | |
3199 | 45 | memset(buf, 0, sizeof(buf)); |
3200 | 45 | try { |
3201 | 45 | fi->seek(ih_entry - isection[1].vaddr + isection[1].rawdataptr, SEEK_SET); |
3202 | 45 | fi->read(buf, sizeof(buf)); |
3203 | | |
3204 | | // FIXME this is for x86 |
3205 | 45 | static const byte magic[] = "\x8b\x1e\x83\xee\xfc\x11\xdb"; |
3206 | | // mov ebx, [esi]; sub esi, -4; adc ebx,ebx |
3207 | | |
3208 | 45 | int offset = find(buf, sizeof(buf), magic, 7); |
3209 | 45 | if (offset >= 0 && find(buf + offset + 1, sizeof(buf) - offset - 1, magic, 7) >= 0) |
3210 | 9 | x = true; |
3211 | 45 | } catch (...) { |
3212 | | // x = true; |
3213 | 18 | } |
3214 | 45 | if (x) |
3215 | 9 | throwCantUnpack("file is modified/hacked/protected; take care!!!"); |
3216 | 36 | else |
3217 | 36 | throwCantUnpack("file is possibly modified/hacked/protected; take care!"); |
3218 | 0 | return false; // not reached |
3219 | 45 | } |
3220 | | |
3221 | | // FIXME: what should we say here ? |
3222 | | // throwCantUnpack("file is possibly modified/hacked/protected; take care!"); |
3223 | 77 | return false; |
3224 | 122 | } |
3225 | | |
3226 | 0 | upx_uint64_t PeFile::ilinkerGetAddress(const char *d, const char *n) const { |
3227 | 0 | return ilinker->getAddress(d, n); |
3228 | 0 | } |
3229 | | |
3230 | 40.0k | PeFile::~PeFile() noexcept { |
3231 | 40.0k | oimpdlls = nullptr; |
3232 | 40.0k | delete[] oxrelocs; |
3233 | 40.0k | delete ilinker; |
3234 | | // delete res; |
3235 | 40.0k | } |
3236 | | |
3237 | | /************************************************************************* |
3238 | | // PeFile32 |
3239 | | **************************************************************************/ |
3240 | | |
3241 | 26.5k | PeFile32::PeFile32(InputFile *f) : super(f) { |
3242 | 26.5k | COMPILE_TIME_ASSERT(sizeof(pe_header_t) == 248) |
3243 | 26.5k | COMPILE_TIME_ASSERT_ALIGNED1(pe_header_t) |
3244 | | |
3245 | 26.5k | iddirs = ih.ddirs; |
3246 | 26.5k | oddirs = oh.ddirs; |
3247 | 26.5k | } |
3248 | | |
3249 | | PeFile32::~PeFile32() noexcept {} |
3250 | | |
3251 | 318 | void PeFile32::readPeHeader() { |
3252 | 318 | fi->readx(&ih, sizeof(ih)); |
3253 | 318 | if (31 < (unsigned) ih.subsystem) { |
3254 | 1 | throwCantPack("bad ih.subsystem 0x%x", (unsigned) ih.subsystem); |
3255 | 1 | } |
3256 | 317 | isefi = ((1u << ih.subsystem) & |
3257 | 317 | ((1u << IMAGE_SUBSYSTEM_EFI_APPLICATION) | |
3258 | 317 | (1u << IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) | |
3259 | 317 | (1u << IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) | (1u << IMAGE_SUBSYSTEM_EFI_ROM))) != 0; |
3260 | 317 | isdll = !isefi && (ih.flags & IMAGE_FILE_DLL) != 0; |
3261 | 317 | use_dep_hack &= !isefi; |
3262 | 317 | use_clear_dirty_stack &= !isefi; |
3263 | 317 | } |
3264 | | |
3265 | | void PeFile32::pack0(OutputFile *fo, unsigned subsystem_mask, upx_uint64_t default_imagebase, |
3266 | 0 | bool last_section_rsrc_only) { |
3267 | 0 | super::pack0<LE32>(fo, ih, oh, subsystem_mask, default_imagebase, last_section_rsrc_only); |
3268 | | // infoWarning("End of PeFile32::pack0"); |
3269 | 0 | } |
3270 | | |
3271 | 80 | void PeFile32::unpack(OutputFile *fo) { |
3272 | 80 | bool set_oft = getFormat() == UPX_F_WINCE_ARM; |
3273 | 80 | unpack0<pe_header_t, LE32>(fo, ih, oh, 1U << 31, set_oft); |
3274 | 80 | } |
3275 | | |
3276 | 26.5k | tribool PeFile32::canUnpack() { |
3277 | 26.5k | if (!canPack()) // this calls readFileHeader() and readPeHeader() |
3278 | 26.2k | return false; |
3279 | 300 | return canUnpack0(getFormat() == UPX_F_WINCE_ARM ? 4 : 3, ih.objects, ih.entry, sizeof(ih)); |
3280 | 26.5k | } |
3281 | | |
3282 | 0 | unsigned PeFile32::processImports() { // pass 1 |
3283 | 0 | return processImports0<LE32>(1u << 31); |
3284 | 0 | } |
3285 | | |
3286 | 0 | void PeFile32::processTls(Interval *iv) { processTls1<LE32>(iv, ih.imagebase, ih.imagesize); } |
3287 | | |
3288 | 0 | void PeFile32::processTls(Reloc *r, const Interval *iv, unsigned a) { |
3289 | 0 | processTls2<LE32>(r, iv, a, ih.imagebase); |
3290 | 0 | } |
3291 | | |
3292 | | /************************************************************************* |
3293 | | // PeFile64 |
3294 | | **************************************************************************/ |
3295 | | |
3296 | 13.4k | PeFile64::PeFile64(InputFile *f) : super(f) { |
3297 | 13.4k | COMPILE_TIME_ASSERT(sizeof(pe_header_t) == 264) |
3298 | 13.4k | COMPILE_TIME_ASSERT_ALIGNED1(pe_header_t) |
3299 | | |
3300 | 13.4k | iddirs = ih.ddirs; |
3301 | 13.4k | oddirs = oh.ddirs; |
3302 | 13.4k | } |
3303 | | |
3304 | | PeFile64::~PeFile64() noexcept {} |
3305 | | |
3306 | 285 | void PeFile64::readPeHeader() { |
3307 | 285 | fi->readx(&ih, sizeof(ih)); |
3308 | 285 | if (31 < (unsigned) ih.subsystem) { |
3309 | 6 | throwCantPack("bad ih.subsystem 0x%x", (unsigned) ih.subsystem); |
3310 | 6 | } |
3311 | 279 | isefi = ((1u << ih.subsystem) & |
3312 | 279 | ((1u << IMAGE_SUBSYSTEM_EFI_APPLICATION) | |
3313 | 279 | (1u << IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) | |
3314 | 279 | (1u << IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) | (1u << IMAGE_SUBSYSTEM_EFI_ROM))) != 0; |
3315 | 279 | isdll = !isefi && (ih.flags & IMAGE_FILE_DLL) != 0; |
3316 | 279 | use_dep_hack &= !isefi; |
3317 | 279 | use_clear_dirty_stack &= !isefi; |
3318 | 279 | } |
3319 | | |
3320 | 0 | void PeFile64::pack0(OutputFile *fo, unsigned subsystem_mask, upx_uint64_t default_imagebase) { |
3321 | 0 | super::pack0<LE64>(fo, ih, oh, subsystem_mask, default_imagebase, false); |
3322 | 0 | } |
3323 | | |
3324 | 10 | void PeFile64::unpack(OutputFile *fo) { unpack0<pe_header_t, LE64>(fo, ih, oh, 1ULL << 63, false); } |
3325 | | |
3326 | 13.4k | tribool PeFile64::canUnpack() { |
3327 | 13.4k | if (!canPack()) // this calls readFileHeader() and readPeHeader() |
3328 | 13.2k | return false; |
3329 | 213 | return canUnpack0(3, ih.objects, ih.entry, sizeof(ih)); |
3330 | 13.4k | } |
3331 | | |
3332 | 0 | unsigned PeFile64::processImports() { // pass 1 |
3333 | 0 | return processImports0<LE64>(1ULL << 63); |
3334 | 0 | } |
3335 | | |
3336 | 0 | void PeFile64::processTls(Interval *iv) { processTls1<LE64>(iv, ih.imagebase, ih.imagesize); } |
3337 | | |
3338 | 0 | void PeFile64::processTls(Reloc *r, const Interval *iv, unsigned a) { |
3339 | 0 | processTls2<LE64>(r, iv, a, ih.imagebase); |
3340 | 0 | } |
3341 | | |
3342 | | /* |
3343 | | extra_info added to help uncompression: |
3344 | | |
3345 | | <ih sizeof(pe_head)> |
3346 | | <pe_section_t objs*sizeof(pe_section_t)> |
3347 | | <start of compressed imports 4> - optional \ |
3348 | | <start of the names from uncompressed imports> - opt / |
3349 | | <start of compressed relocs 4> - optional \ |
3350 | | <relocation type indicator 1> - optional / |
3351 | | <icondir_count 2> - optional |
3352 | | <offset of extra info 4> |
3353 | | */ |
3354 | | |
3355 | | /* vim:set ts=4 sw=4 et: */ |