Skip to content

Commit 18b948b

Browse files
paliGoGoOtaku
authored andcommitted
Add delayed import handling
1 parent 26272c3 commit 18b948b

4 files changed

Lines changed: 205 additions & 31 deletions

File tree

lib/libpe/imports.c

Lines changed: 168 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,51 @@ static uint32_t get_dll_count(pe_ctx_t *ctx) {
7373
return count;
7474
}
7575

76-
static uint32_t get_functions_count(pe_ctx_t *ctx, uint64_t offset) {
77-
uint64_t ofs = offset;
76+
static uint32_t get_delay_dll_count(pe_ctx_t *ctx) {
77+
uint32_t count = 0;
78+
79+
const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
80+
if (dir == NULL)
81+
return count;
82+
83+
const uint64_t va = dir->VirtualAddress;
84+
if (va == 0) {
85+
// TODO: report error?
86+
return count;
87+
}
88+
89+
uint64_t ofs = pe_rva2ofs(ctx, va);
90+
91+
while (1) {
92+
IMAGE_DELAYLOAD_DESCRIPTOR *dd = LIBPE_PTR_ADD(ctx->map_addr, ofs);
93+
if (!pe_can_read(ctx, dd, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) {
94+
// TODO: Should we report something?
95+
return count;
96+
}
97+
98+
if (!dd->ImportNameTableRVA)
99+
break;
100+
101+
ofs += sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
102+
103+
const uint64_t aux = ofs; // Store current ofs
104+
ofs = pe_rva2ofs(ctx, dd->DllNameRVA);
105+
if (ofs == 0)
106+
break;
107+
108+
ofs = pe_rva2ofs(ctx, dd->ImportNameTableRVA);
109+
if (ofs == 0)
110+
break;
111+
112+
count++;
113+
ofs = aux; // Restore previous ofs
114+
}
115+
116+
return count;
117+
}
118+
119+
static uint32_t get_functions_count(pe_ctx_t *ctx, uint64_t offset, bool rva_based) {
120+
uint64_t ofs = offset - (rva_based ? 0 : ctx->pe.imagebase);
78121
uint32_t count = 0;
79122

80123
while (1) {
@@ -94,7 +137,8 @@ static uint32_t get_functions_count(pe_ctx_t *ctx, uint64_t offset) {
94137
bool is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
95138

96139
if (!is_ordinal) {
97-
const uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
140+
const uint32_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
141+
const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
98142
const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
99143
if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME)))
100144
return count;
@@ -116,7 +160,8 @@ static uint32_t get_functions_count(pe_ctx_t *ctx, uint64_t offset) {
116160
bool is_ordinal = (thunk_type & (IMAGE_ORDINAL_MASK(ctx))) != 0;
117161

118162
if (!is_ordinal) {
119-
uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
163+
uint64_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
164+
uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
120165
const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
121166
if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME)))
122167
return count;
@@ -133,9 +178,9 @@ static uint32_t get_functions_count(pe_ctx_t *ctx, uint64_t offset) {
133178
return count;
134179
}
135180

136-
static pe_err_e parse_imported_functions(pe_ctx_t *ctx, pe_imported_dll_t *imported_dll, uint64_t offset) {
181+
static pe_err_e parse_imported_functions(pe_ctx_t *ctx, pe_imported_dll_t *imported_dll, uint64_t offset, bool rva_based) {
137182
imported_dll->err = LIBPE_E_OK;
138-
imported_dll->functions_count = get_functions_count(ctx, offset);
183+
imported_dll->functions_count = get_functions_count(ctx, offset, rva_based);
139184

140185
imported_dll->functions = calloc(imported_dll->functions_count, sizeof(pe_imported_function_t));
141186
if (imported_dll->functions == NULL) {
@@ -150,7 +195,7 @@ static pe_err_e parse_imported_functions(pe_ctx_t *ctx, pe_imported_dll_t *impor
150195
bool is_ordinal = false;
151196
uint16_t ordinal = 0;
152197
uint16_t hint = 0;
153-
uint64_t ofs = offset;
198+
uint64_t ofs = offset - (rva_based ? 0 : ctx->pe.imagebase);
154199

155200
for (uint32_t i=0; i < imported_dll->functions_count; i++) {
156201
switch (ctx->pe.optional_hdr.type) {
@@ -177,7 +222,8 @@ static pe_err_e parse_imported_functions(pe_ctx_t *ctx, pe_imported_dll_t *impor
177222
hint = 0;
178223
ordinal = (thunk->u1.Ordinal & ~(IMAGE_ORDINAL_MASK(ctx))) & 0xffff;
179224
} else {
180-
const uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
225+
const uint32_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
226+
const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
181227
const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
182228
if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
183229
imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
@@ -217,7 +263,8 @@ static pe_err_e parse_imported_functions(pe_ctx_t *ctx, pe_imported_dll_t *impor
217263
hint = 0; // No hint
218264
ordinal = (thunk->u1.Ordinal & ~(IMAGE_ORDINAL_MASK(ctx))) & 0xffff;
219265
} else {
220-
uint64_t imp_ofs = pe_rva2ofs(ctx, thunk->u1.AddressOfData);
266+
const uint64_t rva = thunk->u1.AddressOfData - (rva_based ? 0 : ctx->pe.imagebase);
267+
const uint64_t imp_ofs = pe_rva2ofs(ctx, rva);
221268
const IMAGE_IMPORT_BY_NAME *imp_name = LIBPE_PTR_ADD(ctx->map_addr, imp_ofs);
222269
if (!pe_can_read(ctx, imp_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
223270
imported_dll->err = LIBPE_E_ALLOCATION_FAILURE;
@@ -265,28 +312,44 @@ pe_imports_t *pe_imports(pe_ctx_t *ctx) {
265312
imports->err = LIBPE_E_OK;
266313

267314
imports->dll_count = get_dll_count(ctx);
268-
if (imports->dll_count == 0)
315+
imports->delay_dll_count = get_delay_dll_count(ctx);
316+
if (imports->dll_count == 0 && imports->delay_dll_count == 0)
269317
return imports;
270318

271319
// Allocate array to store DLLs
272-
imports->dlls = calloc(imports->dll_count, sizeof(pe_imported_dll_t));
273-
if (imports->dlls == NULL) {
274-
imports->err = LIBPE_E_ALLOCATION_FAILURE;
275-
return imports;
320+
if (imports->dll_count != 0) {
321+
imports->dlls = calloc(imports->dll_count, sizeof(pe_imported_dll_t));
322+
if (imports->dlls == NULL) {
323+
imports->err = LIBPE_E_ALLOCATION_FAILURE;
324+
return imports;
325+
}
276326
}
277327

278-
const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
279-
if (dir == NULL) {
280-
return imports;
328+
// Allocate array to store delay loaded DLLs
329+
if (imports->delay_dll_count != 0) {
330+
imports->delay_dlls = calloc(imports->delay_dll_count, sizeof(pe_imported_dll_t));
331+
if (imports->delay_dlls == NULL) {
332+
imports->err = LIBPE_E_ALLOCATION_FAILURE;
333+
return imports;
334+
}
281335
}
282336

283-
const uint64_t va = dir->VirtualAddress;
284-
if (va == 0) {
285-
// TODO: report error?
286-
return imports;
287-
}
337+
uint64_t ofs = 0;
288338

289-
uint64_t ofs = pe_rva2ofs(ctx, va);
339+
if (imports->dll_count != 0) {
340+
const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_IMPORT);
341+
if (dir == NULL) {
342+
return imports;
343+
}
344+
345+
const uint64_t va = dir->VirtualAddress;
346+
if (va == 0) {
347+
// TODO: report error?
348+
return imports;
349+
}
350+
351+
ofs = pe_rva2ofs(ctx, va);
352+
}
290353

291354
for (uint32_t i=0; i < imports->dll_count; i++) {
292355
IMAGE_IMPORT_DESCRIPTOR *id = LIBPE_PTR_ADD(ctx->map_addr, ofs);
@@ -333,7 +396,78 @@ pe_imports_t *pe_imports(pe_ctx_t *ctx) {
333396
break;
334397
}
335398

336-
pe_err_e parse_err = parse_imported_functions(ctx, dll, ofs);
399+
// IMAGE_IMPORT_DESCRIPTOR is always RVA based.
400+
pe_err_e parse_err = parse_imported_functions(ctx, dll, ofs, true);
401+
if (parse_err != LIBPE_E_OK) {
402+
imports->err = parse_err;
403+
return imports;
404+
}
405+
406+
ofs = aux; // Restore previous ofs
407+
}
408+
409+
if (imports->delay_dll_count != 0) {
410+
const IMAGE_DATA_DIRECTORY *dir = pe_directory_by_entry(ctx, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
411+
if (dir == NULL) {
412+
return imports;
413+
}
414+
415+
const uint64_t va = dir->VirtualAddress;
416+
if (va == 0) {
417+
// TODO: report error?
418+
return imports;
419+
}
420+
421+
ofs = pe_rva2ofs(ctx, va);
422+
}
423+
424+
for (uint32_t i=0; i < imports->delay_dll_count; i++) {
425+
IMAGE_DELAYLOAD_DESCRIPTOR *dd = LIBPE_PTR_ADD(ctx->map_addr, ofs);
426+
if (!pe_can_read(ctx, dd, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) {
427+
break;
428+
}
429+
430+
if (!dd->ImportNameTableRVA)
431+
break;
432+
433+
ofs += sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
434+
const uint64_t aux = ofs; // Store current ofs
435+
436+
ofs = pe_rva2ofs(ctx, dd->DllNameRVA - (dd->Attributes.u1.RvaBased ? 0 : ctx->pe.imagebase));
437+
if (ofs == 0)
438+
break;
439+
440+
const char *dll_name_ptr = LIBPE_PTR_ADD(ctx->map_addr, ofs);
441+
if (!pe_can_read(ctx, dll_name_ptr, 1)) {
442+
// TODO: Should we report something?
443+
break;
444+
}
445+
446+
pe_imported_dll_t * const dll = &imports->delay_dlls[i];
447+
448+
// Allocate string to store DLL name
449+
const size_t dll_name_size = MAX_DLL_NAME;
450+
dll->name = calloc(1, dll_name_size);
451+
if (dll->name == NULL) {
452+
imports->err = LIBPE_E_ALLOCATION_FAILURE;
453+
return imports;
454+
}
455+
456+
// Validate whether it's ok to access at least 1 byte after dll_name_ptr.
457+
// It might be '\0', for example.
458+
strncpy(dll->name, dll_name_ptr, dll_name_size-1);
459+
// Because `strncpy` does not guarantee to NUL terminate the string itself, this must be done explicitly.
460+
dll->name[dll_name_size - 1] = '\0';
461+
462+
ofs = pe_rva2ofs(ctx, dd->ImportNameTableRVA);
463+
if (ofs == 0) {
464+
break;
465+
}
466+
467+
// IMAGE_DELAYLOAD_DESCRIPTOR v1 is absolute address based and v2 is RVA based.
468+
// LINK.EXE from Visual C++ 6.0 generates IMAGE_DELAYLOAD_DESCRIPTOR v1.
469+
// LINK.EXE from Visual C++ 7.0/2002 and new generates IMAGE_DELAYLOAD_DESCRIPTOR v2.
470+
pe_err_e parse_err = parse_imported_functions(ctx, dll, ofs, dd->Attributes.u1.RvaBased);
337471
if (parse_err != LIBPE_E_OK) {
338472
imports->err = parse_err;
339473
return imports;
@@ -358,6 +492,16 @@ void pe_imports_dealloc(pe_imports_t *obj) {
358492
free(dll->name);
359493
free(dll->functions);
360494
}
495+
for (uint32_t i=0; i < obj->delay_dll_count; i++) {
496+
const pe_imported_dll_t *dll = &obj->delay_dlls[i];
497+
for (uint32_t j=0; j < dll->functions_count; j++) {
498+
const pe_imported_function_t *function = &dll->functions[j];
499+
free(function->name);
500+
}
501+
free(dll->name);
502+
free(dll->functions);
503+
}
361504
free(obj->dlls);
505+
free(obj->delay_dlls);
362506
free(obj);
363507
}

lib/libpe/include/libpe/dir_import.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@ typedef struct {
4242
uint32_t FirstThunk;
4343
} IMAGE_IMPORT_DESCRIPTOR;
4444

45+
typedef struct {
46+
union {
47+
uint32_t AllAttributes;
48+
struct {
49+
uint32_t RvaBased : 1; // Delay load version 2
50+
uint32_t ReservedAttributes : 31;
51+
} u1;
52+
} Attributes;
53+
uint32_t DllNameRVA; // RVA to the name of the target library (NULL-terminate ASCII string)
54+
uint32_t ModuleHandleRVA; // RVA to the HMODULE caching location (PHMODULE)
55+
uint32_t ImportAddressTableRVA; // RVA to the start of the IAT (PIMAGE_THUNK_DATA)
56+
uint32_t ImportNameTableRVA; // RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData)
57+
uint32_t BoundImportAddressTableRVA; // RVA to an optional bound IAT
58+
uint32_t UnloadInformationTableRVA; // RVA to an optional unload info table
59+
uint32_t TimeDateStamp; // 0 if not bound, Otherwise, date/time of the target DLL
60+
} IMAGE_DELAYLOAD_DESCRIPTOR;
61+
4562
// import name entry
4663
typedef struct {
4764
uint16_t Hint;

lib/libpe/include/libpe/imports.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ typedef struct {
4646
pe_err_e err;
4747
uint32_t dll_count;
4848
pe_imported_dll_t *dlls; // array of DLLs
49+
uint32_t delay_dll_count;
50+
pe_imported_dll_t *delay_dlls;
4951
} pe_imports_t;
5052

5153
void pe_imports_dealloc(pe_imports_t *imports);

src/readpe.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,14 +1049,8 @@ static void print_exports(pe_ctx_t *ctx)
10491049
output_close_scope(); // Exported functions
10501050
}
10511051

1052-
static void print_imports(pe_ctx_t *ctx)
1052+
static void print_import_library(const pe_imported_dll_t *dll)
10531053
{
1054-
output_open_scope("Imported functions", OUTPUT_SCOPE_TYPE_ARRAY);
1055-
1056-
const pe_imports_t *imports = pe_imports(ctx);
1057-
for (size_t i=0; i < imports->dll_count; i++) {
1058-
const pe_imported_dll_t *dll = &imports->dlls[i];
1059-
output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT);
10601054
output("Name", dll->name);
10611055
output_open_scope("Functions", OUTPUT_SCOPE_TYPE_ARRAY);
10621056

@@ -1079,8 +1073,25 @@ static void print_imports(pe_ctx_t *ctx)
10791073
}
10801074

10811075
output_close_scope(); // Functions
1076+
}
1077+
1078+
static void print_imports(pe_ctx_t *ctx)
1079+
{
1080+
output_open_scope("Imported functions", OUTPUT_SCOPE_TYPE_ARRAY);
1081+
1082+
const pe_imports_t *imports = pe_imports(ctx);
1083+
for (size_t i=0; i < imports->dll_count; i++) {
1084+
const pe_imported_dll_t *dll = &imports->dlls[i];
1085+
output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT);
1086+
print_import_library(dll);
10821087
output_close_scope(); // Library
10831088
}
1089+
for (size_t i=0; i < imports->delay_dll_count; i++) {
1090+
const pe_imported_dll_t *dll = &imports->delay_dlls[i];
1091+
output_open_scope("Delay Loaded Library", OUTPUT_SCOPE_TYPE_OBJECT);
1092+
print_import_library(dll);
1093+
output_close_scope(); // Delay Loaded Library
1094+
}
10841095

10851096
output_close_scope(); // Imported functions
10861097
}

0 commit comments

Comments
 (0)