Coverage Report

Created: 2026-08-13 06:59

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