Skip to content

Commit 586bdfa

Browse files
committed
Add an initial cut of Windows PE DLL support
The initial cut is likely unstable and has not been comprehensively tested.
1 parent e655cdc commit 586bdfa

9 files changed

Lines changed: 98 additions & 61 deletions

File tree

src/e9patch/e9api.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ static void queuePatch(Binary *B, Instr *I, const Trampoline *T)
178178
static Binary *parseBinary(const Message &msg)
179179
{
180180
const char *filename = nullptr;
181-
Mode mode = MODE_ELF_EXECUTABLE;
181+
Mode mode = MODE_ELF_EXE;
182182
bool have_mode = false, dup = false;
183183
for (unsigned i = 0; i < msg.num_params; i++)
184184
{
@@ -247,10 +247,10 @@ static Binary *parseBinary(const Message &msg)
247247
B->config = 0x0;
248248
switch (B->mode)
249249
{
250-
case MODE_ELF_EXECUTABLE: case MODE_ELF_SHARED_OBJECT:
250+
case MODE_ELF_EXE: case MODE_ELF_DSO:
251251
B->pic = parseElf(B);
252252
break;
253-
case MODE_PE_EXECUTABLE:
253+
case MODE_PE_EXE: case MODE_PE_DLL:
254254
parsePE(B);
255255
B->pic = true;
256256
break;
@@ -450,8 +450,10 @@ static void parseEmit(Binary *B, const Message &msg)
450450

451451
// Create and optimize the mappings:
452452
MappingSet mappings;
453-
size_t granularity = (B->mode != MODE_PE_EXECUTABLE? PAGE_SIZE:
454-
WINDOWS_VIRTUAL_ALLOC_SIZE);
453+
size_t granularity = PAGE_SIZE;
454+
granularity = (B->mode == MODE_PE_EXE ||
455+
B->mode == MODE_PE_DLL? WINDOWS_VIRTUAL_ALLOC_SIZE:
456+
granularity);
455457
size_t mapping_size = std::max(granularity, option_mem_mapping_size);
456458
buildMappings(B->allocator, mapping_size, mappings);
457459
switch (option_mem_granularity)
@@ -472,10 +474,10 @@ static void parseEmit(Binary *B, const Message &msg)
472474
// Create the patched binary:
473475
switch (B->mode)
474476
{
475-
case MODE_ELF_EXECUTABLE: case MODE_ELF_SHARED_OBJECT:
477+
case MODE_ELF_EXE: case MODE_ELF_DSO:
476478
B->patched.size = emitElf(B, mappings, mapping_size);
477479
break;
478-
case MODE_PE_EXECUTABLE:
480+
case MODE_PE_EXE: case MODE_PE_DLL:
479481
B->patched.size = emitPE(B, mappings, mapping_size);
480482
break;
481483
}

src/e9patch/e9elf.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ bool parseElf(Binary *B)
114114
{
115115
case ET_EXEC:
116116
{
117-
if (mode == MODE_ELF_SHARED_OBJECT)
117+
if (mode == MODE_ELF_DSO)
118118
error("failed to parse ELF file \"%s\": file is an "
119119
"executable and not a shared object", filename);
120120
if (!reserve(B, 0x0, 0x10000))
@@ -123,7 +123,7 @@ bool parseElf(Binary *B)
123123
}
124124
case ET_DYN:
125125
pic = true;
126-
pie = (mode == MODE_ELF_EXECUTABLE);
126+
pie = (mode == MODE_ELF_EXE);
127127
break;
128128
default:
129129
error("failed to parse ELF file \"%s\"; file is not executable",
@@ -398,15 +398,15 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
398398
data[size++] = /*int3=*/0xCC;
399399
switch (B->mode)
400400
{
401-
case MODE_ELF_EXECUTABLE:
401+
case MODE_ELF_EXE:
402402
// mov (%rsp),%rdi
403403
// lea 0x8(%rsp),%rsi
404404
data[size++] = 0x48; data[size++] = 0x8B; data[size++] = 0x3C;
405405
data[size++] = 0x24;
406406
data[size++] = 0x48; data[size++] = 0x8D; data[size++] = 0x74;
407407
data[size++] = 0x24; data[size++] = 0x08;
408408
break;
409-
case MODE_ELF_SHARED_OBJECT:
409+
case MODE_ELF_DSO:
410410
// xorq %edi, %edi
411411
// xorq %esi, %esi
412412
data[size++] = 0x31; data[size++] = 0xFF;
@@ -434,15 +434,15 @@ size_t emitElf(Binary *B, const MappingSet &mappings, size_t mapping_size)
434434
config_elf->dynamic = dynamic = (intptr_t)phdr->p_vaddr;
435435
switch (B->mode)
436436
{
437-
case MODE_ELF_EXECUTABLE:
437+
case MODE_ELF_EXE:
438438
{
439439
Elf64_Ehdr *ehdr = B->elf.ehdr;
440440
config->entry = (intptr_t)B->elf.ehdr->e_entry;
441441
ehdr->e_entry = (Elf64_Addr)entry;
442442
config->flags |= E9_FLAG_EXE;
443443
break;
444444
}
445-
case MODE_ELF_SHARED_OBJECT:
445+
case MODE_ELF_DSO:
446446
{
447447
if (phdr == nullptr)
448448
error("failed to replace DT_INIT entry; missing PT_DYNAMIC "

src/e9patch/e9json.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,19 +1143,17 @@ static void parseParams(Parser &parser, Message &msg)
11431143
case PARAM_MODE:
11441144
expectToken(parser, TOKEN_STRING);
11451145
if (strcmp(parser.s, "elf.exe") == 0)
1146-
value.integer = (intptr_t)MODE_ELF_EXECUTABLE;
1146+
value.integer = (intptr_t)MODE_ELF_EXE;
11471147
else if (strcmp(parser.s, "elf.dso") == 0)
1148-
value.integer = (intptr_t)MODE_ELF_SHARED_OBJECT;
1148+
value.integer = (intptr_t)MODE_ELF_DSO;
11491149
else if (strcmp(parser.s, "pe.exe") == 0)
1150-
value.integer = (intptr_t)MODE_PE_EXECUTABLE;
1150+
value.integer = (intptr_t)MODE_PE_EXE;
11511151
else if (strcmp(parser.s, "pe.dll") == 0)
1152-
parse_error(parser, "failed to parse mode string "
1153-
"\"%s\"; Windows PE DLLs are not-yet-implemented",
1154-
parser.s);
1152+
value.integer = (intptr_t)MODE_PE_DLL;
11551153
else
11561154
parse_error(parser, "failed to parse mode string "
11571155
"\"%s\"; expected one of {\"elf.exe\", "
1158-
"\"elf.dso\", \"pe.exe\"}", parser.s);
1156+
"\"elf.dso\", \"pe.exe\", \"pe.dll\"}", parser.s);
11591157
break;
11601158
case PARAM_UNKNOWN:
11611159
parseAndDiscardObject(parser);

src/e9patch/e9loader_pe.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,7 @@ static NO_INLINE void e9debug_impl(void *stderr, write_console_t WriteConsole,
254254

255255
extern "C"
256256
{
257-
void *e9loader(PEB *peb, const struct e9_config_s *config,
258-
int32_t reason);
257+
void *e9loader(PEB *peb, const struct e9_config_s *config);
259258
}
260259

261260
asm (
@@ -451,14 +450,16 @@ static int32_t e9nt_read_file_wapper(intptr_t handle, intptr_t event,
451450
/*
452451
* Main loader code.
453452
*/
454-
void *e9loader(PEB *peb, const struct e9_config_s *config, int32_t reason)
453+
void *e9loader(PEB *peb, const struct e9_config_s *config)
455454
{
456455
// Step (0): Sanity checks & initialization:
457456
const uint8_t *loader_base = (const uint8_t *)config;
458457
const uint8_t *image_base = loader_base - config->base;
459458
void *entry = (void *)(image_base + config->entry);
460-
if (reason != DLL_PROCESS_ATTACH)
459+
static bool inited = false;
460+
if (inited)
461461
return entry; // Enforce single execution
462+
inited = true;
462463

463464
// Step (1): Parse the PEB/LDR for kernel32.dll and our image path:
464465
PEB_LDR_DATA* ldr = peb->Ldr;
@@ -472,7 +473,7 @@ void *e9loader(PEB *peb, const struct e9_config_s *config, int32_t reason)
472473
const LDR_DATA_TABLE_ENTRY* entry =
473474
CONTAINING_RECORD(curr, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
474475
const UNICODE_STRING *name = &entry->FullDllName;
475-
if (entry->DllBase == peb->ImageBaseAddress)
476+
if (entry->DllBase == (void *)image_base)
476477
self = name->Buffer;
477478
name++; // BaseDllName immediately follows FullDllName
478479
if (e9wcscasecmp(name->Buffer, L"kernel32.dll") == 0)

src/e9patch/e9patch.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,10 @@ static void usage(FILE *stream, const char *progname)
295295
"\n"
296296
"\t--mem-rebase[=ADDR]\n"
297297
"\t\tRebase the binary to the absolute address ADDR. Only\n"
298-
"\t\trelevant for Windows PE binaries. The special value \"auto\"\n"
299-
"\t\twill cause a suitable base address to be chose automatically.\n"
300-
"\t\tThe special value \"none\" leaves the original base intact.\n"
298+
"\t\trelevant for Windows PE binaries. The special values \"auto\"\n"
299+
"\t\tor \"random\" will cause a suitable base address to be chosen\n"
300+
"\t\tautomatically/randomly. The special value \"none\" leaves the\n"
301+
"\t\toriginal base intact.\n"
301302
"\t\tDefault: none (disabled)\n"
302303
"\n"
303304
"\t--tactic-B1[=false]\n"
@@ -581,9 +582,11 @@ void parseOptions(int argc, char * const argv[], bool api)
581582
case OPTION_MEM_REBASE:
582583
option_mem_rebase_set = true;
583584
if (strcmp(optarg, "auto") == 0)
584-
option_mem_rebase = -1;
585+
option_mem_rebase = OPTION_REBASE_AUTO;
586+
else if (strcmp(optarg, "random") == 0)
587+
option_mem_rebase = OPTION_REBASE_RANDOM;
585588
else if (strcmp(optarg, "none") == 0)
586-
option_mem_rebase = 0x0;
589+
option_mem_rebase = OPTION_REBASE_NONE;
587590
else
588591
option_mem_rebase = parseIntOptArg("--mem-rebase", optarg,
589592
0x100000000ll, 0xffff00000000ll, /*hex=*/true);
@@ -671,12 +674,14 @@ int realMain(int argc, char **argv)
671674
const char *mode_str = "???";
672675
switch (B->mode)
673676
{
674-
case MODE_ELF_EXECUTABLE:
677+
case MODE_ELF_EXE:
675678
mode_str = "Linux ELF executable"; break;
676-
case MODE_ELF_SHARED_OBJECT:
679+
case MODE_ELF_DSO:
677680
mode_str = "Linux ELF dynamic shared object"; break;
678-
case MODE_PE_EXECUTABLE:
681+
case MODE_PE_EXE:
679682
mode_str = "Windows PE executable"; break;
683+
case MODE_PE_DLL:
684+
mode_str = "Windows PE dynamic link library"; break;
680685
}
681686

682687
printf("\n\n-----------------------------------------------\n");

src/e9patch/e9patch.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,10 @@ struct PEInfo
385385
*/
386386
enum Mode
387387
{
388-
MODE_ELF_EXECUTABLE, // Binary is an ELF executable.
389-
MODE_ELF_SHARED_OBJECT, // Binary is an ELF shared object.
390-
MODE_PE_EXECUTABLE, // Binary is a PE executable.
388+
MODE_ELF_EXE, // Linux ELF executable.
389+
MODE_ELF_DSO, // Linux ELF shared object.
390+
MODE_PE_EXE, // Windows PE executable.
391+
MODE_PE_DLL, // Windows PE DLL.
391392
};
392393

393394
/*
@@ -517,6 +518,13 @@ extern bool option_loader_phdr_set;
517518
extern bool option_loader_static_set;
518519
extern bool option_mem_rebase_set;
519520

521+
/*
522+
* Special values for option_mem_rebase.
523+
*/
524+
#define OPTION_REBASE_NONE 0
525+
#define OPTION_REBASE_AUTO -1
526+
#define OPTION_REBASE_RANDOM -2
527+
520528
/*
521529
* Global statistics.
522530
*/

src/e9patch/e9pe.cpp

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
#include <cstdint>
2020

21+
#include <unistd.h>
2122
#include <sys/mman.h>
23+
#include <sys/syscall.h>
2224

2325
#include "e9alloc.h"
2426
#include "e9elf.h"
@@ -134,14 +136,24 @@ typedef struct _IMAGE_BASE_RELOCATION
134136
/*
135137
* Simple string hash function.
136138
*/
137-
uint64_t hash(const char *s)
139+
static uint64_t hash(const char *s)
138140
{
139141
uint64_t h = 777799777ull;
140142
while (*s)
141143
h = (3333331ull * h) ^ (0xe9e9ea1bull * (uint64_t)*s++);
142144
return h;
143145
}
144146

147+
/*
148+
* Simple random number function.
149+
*/
150+
static uint64_t random(const char *s)
151+
{
152+
uint64_t r;
153+
(void)syscall(SYS_getrandom, &r, sizeof(r), 0);
154+
return r;
155+
}
156+
145157
/*
146158
* Parse a Windows PE executable.
147159
*/
@@ -196,16 +208,6 @@ void parsePE(Binary *B)
196208
error("failed to parse PE file \"%s\"; invalid image base (0x%lx), "
197209
"expected a multiple of virtual allocation granularity (%u)",
198210
filename, image_base, WINDOWS_VIRTUAL_ALLOC_SIZE);
199-
bool have_relocs =
200-
(opt_hdr->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
201-
!= 0;
202-
uint64_t image_base_min = 0x100000000;
203-
if ((option_mem_rebase == 0 || !have_relocs) &&
204-
image_base < image_base_min)
205-
error("failed to parse PE file \"%s\"; image base (0x%lx) must be "
206-
"(>=0x%lx)%s", filename, image_base, image_base_min,
207-
(have_relocs? " (hint: see the `--mem-rebase' option)":
208-
", but relocations are stripped"));
209211
PIMAGE_SECTION_HEADER shdr =
210212
(PIMAGE_SECTION_HEADER)&opt_hdr->DataDirectory[
211213
opt_hdr->NumberOfRvaAndSizes];
@@ -331,16 +333,17 @@ size_t emitPE(Binary *B, const MappingSet &mappings, size_t mapping_size)
331333
uint32_t addr_of_entry = size_of_image + (uint32_t)(size - config_offset);
332334
PIMAGE_TLS_DIRECTORY64 tls = (PIMAGE_TLS_DIRECTORY64)findPEData(B,
333335
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress);
336+
intptr_t image_base = (intptr_t)opt_hdr->ImageBase;
334337
intptr_t *callbacks = (tls != nullptr?
335-
(intptr_t *)findPEData(B, tls->AddressOfCallBacks - opt_hdr->ImageBase):
338+
(intptr_t *)findPEData(B, tls->AddressOfCallBacks - image_base):
336339
nullptr);
337340
bool use_tls = (callbacks != nullptr && callbacks[0] != 0x0);
338341
if (use_tls)
339342
{
340343
// This PE file uses TLS callbacks, which are called before the entry
341344
// point. Thus we inject the loader here:
342-
config->entry = (intptr_t)callbacks[0] - (intptr_t)opt_hdr->ImageBase;
343-
callbacks[0] = (intptr_t)opt_hdr->ImageBase + (intptr_t)addr_of_entry;
345+
config->entry = (intptr_t)callbacks[0] - image_base;
346+
callbacks[0] = image_base + (intptr_t)addr_of_entry;
344347
}
345348
else
346349
{
@@ -404,13 +407,15 @@ size_t emitPE(Binary *B, const MappingSet &mappings, size_t mapping_size)
404407
* Windows ASLR depends on text relocations, which is not compatible with
405408
* static binary rewriting. Thus, it must be disabled...
406409
*/
407-
bool have_relocs =
408-
(opt_hdr->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
409-
!= 0;
410410
uint32_t relocs =
411411
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
412412
uint32_t relocs_size =
413413
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
414+
bool have_relocs =
415+
((file_hdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) == 0) &&
416+
((opt_hdr->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
417+
!= 0) &&
418+
relocs != 0x0 && relocs_size > 0;
414419
opt_hdr->DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
415420
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
416421
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0;
@@ -419,24 +424,33 @@ size_t emitPE(Binary *B, const MappingSet &mappings, size_t mapping_size)
419424
/*
420425
* Rebase the exectuable (if necessary).
421426
*/
427+
intptr_t image_base_min = 0x100000000;
428+
if ((option_mem_rebase == 0 || !have_relocs) &&
429+
image_base < image_base_min)
430+
error("failed to parse PE file \"%s\"; image base (0x%lx) must be "
431+
"(>=0x%lx)%s", B->filename, image_base, image_base_min,
432+
(have_relocs? " (hint: see the `--mem-rebase' option)":
433+
", but relocations are stripped"));
422434
const uint8_t *relocs_base = findPEData(B, relocs);
423435
intptr_t rebase_delta = 0;
424436
switch (option_mem_rebase)
425437
{
426-
case -1:
438+
case OPTION_REBASE_AUTO: case OPTION_REBASE_RANDOM:
427439
{
428440
// Auto rebase
429441
uint64_t lo = 0x100000000000ull, hi = 0xB00000000000ull;
430-
uint64_t base = hash(B->filename) % (hi - lo) + lo;
442+
uint64_t r = (option_mem_rebase == OPTION_REBASE_AUTO?
443+
hash(B->filename): random(B->filename));
444+
uint64_t base = r % (hi - lo) + lo;
431445
base -= base % mapping_align;
432-
rebase_delta = (intptr_t)base - (intptr_t)opt_hdr->ImageBase;
446+
rebase_delta = (intptr_t)base - image_base;
433447
break;
434448
}
435-
case 0:
449+
case OPTION_REBASE_NONE:
436450
// No rebase
437451
break;
438452
default:
439-
rebase_delta = option_mem_rebase - (intptr_t)opt_hdr->ImageBase;
453+
rebase_delta = option_mem_rebase - image_base;
440454
break;
441455
}
442456
if (rebase_delta != 0 && !have_relocs)

src/e9patch/e9tactics.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,13 @@ static Bounds makeBounds(Binary &B, const Trampoline *T, const Instr *I,
260260
// Step (7): Apply the user-specified bounds (if any).
261261
lo = std::max(lo, option_mem_lb);
262262
hi = std::min(hi, option_mem_ub);
263-
if (B.mode != MODE_PE_EXECUTABLE)
264-
hi = std::min(hi, option_loader_base);
263+
switch (B.mode)
264+
{
265+
case MODE_ELF_EXE: case MODE_ELF_DSO:
266+
hi = std::min(hi, option_loader_base);
267+
default:
268+
break;
269+
}
265270

266271
return {lo, hi};
267272
}

0 commit comments

Comments
 (0)