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