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