Skip to content

Commit 160a354

Browse files
committed
Allow Windows PE binaries to be rebased.
This is supported via the `--mem-rebase' option. Only works if relocations are available.
1 parent 87e42d4 commit 160a354

4 files changed

Lines changed: 141 additions & 20 deletions

File tree

src/e9patch/e9elf.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,10 @@ size_t emitElf(const Binary *B, const MappingSet &mappings,
501501
phdr->p_align = PAGE_SIZE;
502502

503503
stat_output_file_size = size;
504+
505+
if (option_mem_rebase_set)
506+
warning("ignoring `--mem-rebase' option for Linux ELF binary");
507+
504508
return size;
505509
}
506510

src/e9patch/e9patch.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ intptr_t option_mem_lb = RELATIVE_ADDRESS_MIN;
5757
intptr_t option_mem_ub = RELATIVE_ADDRESS_MAX;
5858
size_t option_mem_mapping_size = PAGE_SIZE;
5959
bool option_mem_multi_page = true;
60+
intptr_t option_mem_rebase = 0x0;
6061
std::set<intptr_t> option_trap;
6162
bool option_trap_all = false;
6263
bool option_trap_entry = false;
@@ -65,6 +66,7 @@ static std::string option_output("-");
6566
bool option_loader_base_set = false;
6667
bool option_loader_phdr_set = false;
6768
bool option_loader_static_set = false;
69+
bool option_mem_rebase_set = false;
6870

6971
/*
7072
* Global statistics.
@@ -140,7 +142,7 @@ void debugImpl(const char *msg, ...)
140142
* Parse an integer from an optarg.
141143
*/
142144
static intptr_t parseIntOptArg(const char *option, const char *optarg,
143-
intptr_t lb, intptr_t ub)
145+
intptr_t lb, intptr_t ub, bool hex = false)
144146
{
145147
const char *optarg_0 = optarg;
146148
bool neg = (optarg[0] == '-');
@@ -155,8 +157,18 @@ static intptr_t parseIntOptArg(const char *option, const char *optarg,
155157
r = (neg? -r: r);
156158
if (errno != 0 || end == optarg ||
157159
(end != nullptr && *end != '\0') || r < lb || r > ub)
158-
error("failed to parse argument \"%s\" for the `%s' option; "
159-
"expected a number %zd..%zd", option, optarg_0, lb, ub);
160+
{
161+
if (!hex)
162+
error("failed to parse argument \"%s\" for the `%s' option; "
163+
"expected a number within the range %zd..%zd", option,
164+
optarg_0, lb, ub);
165+
else
166+
error("failed to parse argument \"%s\" for the `%s' option; "
167+
"expected a number within the range %s0x%lx..%s0x%lx",
168+
option, optarg_0,
169+
(lb < 0? "-": ""), std::abs(lb),
170+
(ub < 0? "-": ""), std::abs(ub));
171+
}
160172
return r;
161173
}
162174

@@ -281,6 +293,13 @@ static void usage(FILE *stream, const char *progname)
281293
"\t\tEnable [disable] trampolines that cross page boundaries.\n"
282294
"\t\tDefault: true (enabled)\n"
283295
"\n"
296+
"\t--mem-rebase[=ADDR]\n"
297+
"\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"
301+
"\t\tDefault: none (disabled)\n"
302+
"\n"
284303
"\t--tactic-B1[=false]\n"
285304
"\t--tactic-B2[=false]\n"
286305
"\t--tactic-T1[=false]\n"
@@ -327,6 +346,7 @@ enum Option
327346
OPTION_MEM_LB,
328347
OPTION_MEM_MAPPING_SIZE,
329348
OPTION_MEM_MULTI_PAGE,
349+
OPTION_MEM_REBASE,
330350
OPTION_MEM_UB,
331351
OPTION_OEPILOGUE,
332352
OPTION_OEPILOGUE_SIZE,
@@ -374,6 +394,7 @@ void parseOptions(int argc, char * const argv[], bool api)
374394
{"mem-lb", req_arg, nullptr, OPTION_MEM_LB},
375395
{"mem-mapping-size", req_arg, nullptr, OPTION_MEM_MAPPING_SIZE},
376396
{"mem-multi-page", opt_arg, nullptr, OPTION_MEM_MULTI_PAGE},
397+
{"mem-rebase", req_arg, nullptr, OPTION_MEM_REBASE},
377398
{"mem-ub", req_arg, nullptr, OPTION_MEM_UB},
378399
{"output", req_arg, nullptr, OPTION_OUTPUT},
379400
{"tactic-B1", opt_arg, nullptr, OPTION_TACTIC_B1},
@@ -480,7 +501,7 @@ void parseOptions(int argc, char * const argv[], bool api)
480501
break;
481502
case OPTION_TRAP:
482503
option_trap.insert(parseIntOptArg("--trap", optarg, 0,
483-
INTPTR_MAX));
504+
INTPTR_MAX, /*hex=*/true));
484505
break;
485506
case OPTION_TRAP_ALL:
486507
option_trap_all = parseBoolOptArg("--trap-all", optarg);
@@ -491,7 +512,7 @@ void parseOptions(int argc, char * const argv[], bool api)
491512
case OPTION_LOADER_BASE:
492513
option_loader_base_set = true;
493514
option_loader_base = parseIntOptArg("--loader-base", optarg,
494-
0x0, 0x800000000);
515+
0x0, 0x800000000, /*hex=*/true);
495516
if (option_loader_base % PAGE_SIZE != 0)
496517
error("failed to parse argument \"%s\" for the "
497518
"`--loader-base' option; the loader base address "
@@ -533,11 +554,11 @@ void parseOptions(int argc, char * const argv[], bool api)
533554
break;
534555
case OPTION_MEM_LB:
535556
option_mem_lb = parseIntOptArg("--mem-lb", optarg,
536-
RELATIVE_ADDRESS_MIN, RELATIVE_ADDRESS_MAX);
557+
RELATIVE_ADDRESS_MIN, RELATIVE_ADDRESS_MAX, /*hex=*/true);
537558
break;
538559
case OPTION_MEM_UB:
539560
option_mem_ub = parseIntOptArg("--mem-ub", optarg,
540-
RELATIVE_ADDRESS_MIN, RELATIVE_ADDRESS_MAX);
561+
RELATIVE_ADDRESS_MIN, RELATIVE_ADDRESS_MAX, /*hex=*/true);
541562
break;
542563
case OPTION_MEM_MAPPING_SIZE:
543564
option_mem_mapping_size = parseIntOptArg("--mem-mapping-size",
@@ -557,6 +578,16 @@ void parseOptions(int argc, char * const argv[], bool api)
557578
option_mem_multi_page =
558579
parseBoolOptArg("--mem-multi-page", optarg);
559580
break;
581+
case OPTION_MEM_REBASE:
582+
option_mem_rebase_set = true;
583+
if (strcmp(optarg, "auto") == 0)
584+
option_mem_rebase = -1;
585+
else if (strcmp(optarg, "none") == 0)
586+
option_mem_rebase = 0x0;
587+
else
588+
option_mem_rebase = parseIntOptArg("--mem-rebase", optarg,
589+
0x100000000ll, 0xffff00000000ll, /*hex=*/true);
590+
break;
560591
default:
561592
error("failed to parse command-line options; try `--help' "
562593
"for more information");

src/e9patch/e9patch.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,11 +509,13 @@ extern bool option_trap_entry;
509509
extern size_t option_mem_granularity;
510510
extern size_t option_mem_mapping_size;
511511
extern bool option_mem_multi_page;
512+
extern intptr_t option_mem_rebase;
512513
extern intptr_t option_mem_lb;
513514
extern intptr_t option_mem_ub;
514515
extern bool option_loader_base_set;
515516
extern bool option_loader_phdr_set;
516517
extern bool option_loader_static_set;
518+
extern bool option_mem_rebase_set;
517519

518520
/*
519521
* Global statistics.

src/e9patch/e9pe.cpp

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,21 @@ struct _IMAGE_SECTION_HEADER
103103

104104
typedef struct _IMAGE_TLS_DIRECTORY64
105105
{
106-
uint64_t StartAddressOfRawData;
107-
uint64_t EndAddressOfRawData;
108-
uint64_t AddressOfIndex;
109-
uint64_t AddressOfCallBacks;
110-
int32_t SizeOfZeroFill;
111-
int32_t Characteristics;
106+
uint64_t StartAddressOfRawData;
107+
uint64_t EndAddressOfRawData;
108+
uint64_t AddressOfIndex;
109+
uint64_t AddressOfCallBacks;
110+
int32_t SizeOfZeroFill;
111+
int32_t Characteristics;
112112
} IMAGE_TLS_DIRECTORY64, *PIMAGE_TLS_DIRECTORY64;
113113

114+
typedef struct _IMAGE_BASE_RELOCATION
115+
{
116+
uint32_t VirtualAddress;
117+
uint32_t SizeOfBlock;
118+
uint16_t TypeOffset[];
119+
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
120+
114121
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
115122
#define IMAGE_SCN_MEM_READ 0x40000000
116123
#define IMAGE_SCN_MEM_WRITE 0x80000000
@@ -124,6 +131,17 @@ typedef struct _IMAGE_TLS_DIRECTORY64
124131
#define ALIGN(x, y) \
125132
((x) % (y) == 0? (x): (x) + (y) - ((x) % (y)))
126133

134+
/*
135+
* Simple string hash function.
136+
*/
137+
uint64_t hash(const char *s)
138+
{
139+
uint64_t h = 777799777ull;
140+
while (*s)
141+
h = (3333331ull * h) ^ (0xe9e9ea1bull * (uint64_t)*s++);
142+
return h;
143+
}
144+
127145
/*
128146
* Parse a Windows PE executable.
129147
*/
@@ -178,11 +196,16 @@ void parsePE(Binary *B)
178196
error("failed to parse PE file \"%s\"; invalid image base (0x%lx), "
179197
"expected a multiple of virtual allocation granularity (%u)",
180198
filename, image_base, WINDOWS_VIRTUAL_ALLOC_SIZE);
199+
bool have_relocs =
200+
(opt_hdr->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
201+
!= 0;
181202
uint64_t image_base_min = 0x100000000;
182-
if (image_base < image_base_min)
183-
error("failed to parse PE file \"%s\"; not-yet-implemented image "
184-
"base (0x%lx), must be (>=0x%lx)", filename, image_base,
185-
image_base_min);
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"));
186209
PIMAGE_SECTION_HEADER shdr =
187210
(PIMAGE_SECTION_HEADER)&opt_hdr->DataDirectory[
188211
opt_hdr->NumberOfRvaAndSizes];
@@ -224,7 +247,6 @@ static uint8_t *findData(const Binary *B, uint32_t addr)
224247
{
225248
if (addr == 0x0)
226249
return nullptr;
227-
228250
PIMAGE_FILE_HEADER file_hdr = B->pe.file_hdr;
229251
PIMAGE_SECTION_HEADER shdr = B->pe.shdr;
230252
for (uint16_t i = 0; i < file_hdr->NumberOfSections; i++)
@@ -386,20 +408,82 @@ size_t emitPE(const Binary *B, const MappingSet &mappings, size_t mapping_size)
386408
* Windows ASLR depends on text relocations, which is not compatible with
387409
* static binary rewriting. Thus, it must be disabled...
388410
*/
411+
bool have_relocs =
412+
(opt_hdr->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
413+
!= 0;
414+
uint32_t relocs =
415+
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
416+
uint32_t relocs_size =
417+
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
389418
opt_hdr->DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
390419
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
391420
opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0;
392421
file_hdr->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
393422

394-
stat_output_file_size = size;
423+
/*
424+
* Rebase the exectuable (if necessary).
425+
*/
426+
const uint8_t *relocs_base = findData(B, relocs);
427+
intptr_t rebase_delta = 0;
428+
switch (option_mem_rebase)
429+
{
430+
case -1:
431+
{
432+
// Auto rebase
433+
uint64_t lo = 0x100000000000ull, hi = 0xB00000000000ull;
434+
uint64_t base = hash(B->filename) % (hi - lo) + lo;
435+
base -= base % mapping_align;
436+
rebase_delta = (intptr_t)base - (intptr_t)opt_hdr->ImageBase;
437+
break;
438+
}
439+
case 0:
440+
// No rebase
441+
break;
442+
default:
443+
rebase_delta = option_mem_rebase - (intptr_t)opt_hdr->ImageBase;
444+
break;
445+
}
446+
if (rebase_delta != 0 && !have_relocs)
447+
warning("unable to apply `--mem-rebase' option to Windows PE binary "
448+
"\"%s\"; relocation information has been stripped", B->filename);
449+
uint32_t bytes = 0;
450+
while (rebase_delta != 0 && have_relocs &&
451+
bytes <= relocs_size - sizeof(IMAGE_BASE_RELOCATION))
452+
{
453+
const IMAGE_BASE_RELOCATION *block =
454+
(const IMAGE_BASE_RELOCATION *)(relocs_base + bytes);
455+
if (block->SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION) ||
456+
bytes + block->SizeOfBlock > relocs_size)
457+
error("invalid base relocation block size (%zu)",
458+
block->SizeOfBlock);
459+
bytes += block->SizeOfBlock;
460+
461+
uint8_t *block_base = findData(B, block->VirtualAddress);
462+
uint32_t num_entries =
463+
(block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
464+
sizeof(block->TypeOffset[0]);
465+
for (uint32_t i = 0; i < num_entries; i++)
466+
{
467+
uint16_t type = (block->TypeOffset[i] >> 12) & 0xF;
468+
uint16_t offset = (block->TypeOffset[i] & 0xFFF);
469+
if (type == 0x0)
470+
continue;
471+
if (type != 0xa)
472+
error("base relocation type (0x%x) is not-yet-implemented",
473+
type);
474+
intptr_t *ptr = (intptr_t *)(block_base + offset);
475+
*ptr += rebase_delta;
476+
}
477+
}
478+
opt_hdr->ImageBase += rebase_delta;
395479

396480
if (option_loader_base_set)
397481
warning("ignoring `--loader-base' option for Windows PE binary");
398482
if (option_loader_phdr_set)
399483
warning("ignoring `--loader-phdr' option for Windows PE binary");
400484
if (option_loader_static_set)
401485
warning("ignoring `--loader-static' option for Windows PE binary");
402-
486+
stat_output_file_size = size;
403487
return size;
404488
}
405489

0 commit comments

Comments
 (0)