libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
delayed_imports_loader.cpp
Go to the documentation of this file.
3#include "peconv/relocate.h"
4#include "peconv/logger.h"
5#include "peconv/util.h"
6
7IMAGE_DELAYLOAD_DESCRIPTOR* peconv::get_delayed_imps(IN const BYTE* modulePtr, IN const size_t moduleSize, OUT size_t &dir_size)
8{
9 dir_size = 0;
10 IMAGE_DATA_DIRECTORY *d_imps_dir = peconv::get_directory_entry(modulePtr, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
11 if (!d_imps_dir) {
12 return nullptr;
13 }
14 BYTE* dimps_table = (BYTE*)((ULONGLONG) modulePtr + d_imps_dir->VirtualAddress);
15 const size_t min_size = sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
16 if (d_imps_dir->Size < min_size) {
17 return nullptr;
18 }
19 if (!peconv::validate_ptr((LPVOID)modulePtr, moduleSize, dimps_table, min_size)) {
20 return nullptr;
21 }
22 dir_size = d_imps_dir->Size;
23 return reinterpret_cast<IMAGE_DELAYLOAD_DESCRIPTOR*> (dimps_table);
24}
25
26template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
28 BYTE* modulePtr, const size_t moduleSize,
29 const ULONGLONG img_base,
30 LPSTR lib_name,
31 const T_FIELD ordinal_flag,
33 peconv::t_function_resolver* func_resolver,
34 const std::unordered_set<ULONGLONG> &reloc_values
35)
36{
37 if (!peconv::validate_ptr(modulePtr, moduleSize, desc, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) {
38 LOG_ERROR("Invalid IMAGE_DELAYLOAD_DESCRIPTOR");
39 return false;
40 }
41
42 // Helper to convert VA -> RVA if the address is in relocation table
43 auto convert_va_to_rva = [&](ULONGLONG& addr) -> bool
44 {
45 if (reloc_values.find(addr) != reloc_values.end()) {
46 if (addr < img_base) {
47 LOG_ERROR("Invalid VA: 0x%llx cannot convert safely", addr);
48 return false;
49 }
50 addr -= img_base;
51 }
52 return true;
53 };
54
55 ULONGLONG iat_addr = desc->ImportAddressTableRVA; // may be VA or RVA
56 ULONGLONG thunk_addr = desc->ImportNameTableRVA; // may be VA or RVA
57
58 if (!convert_va_to_rva(iat_addr) || !convert_va_to_rva(thunk_addr)) {
59 return false;
60 }
61
62 if (iat_addr > moduleSize || thunk_addr > moduleSize) {
63 return false;
64 }
65
66 LOG_INFO("iat_addr: 0x%llx, thunk_addr: 0x%llx", iat_addr, thunk_addr);
67
68 T_FIELD* record_va = (T_FIELD*)((ULONGLONG)modulePtr + iat_addr);
69 T_IMAGE_THUNK_DATA* thunk_va = (T_IMAGE_THUNK_DATA*)((ULONGLONG)modulePtr + thunk_addr);
70
71 for (; ; record_va++, thunk_va++) {
72 if (!peconv::validate_ptr(modulePtr, moduleSize, record_va, sizeof(T_FIELD)) ||
73 !peconv::validate_ptr(modulePtr, moduleSize, thunk_va, sizeof(T_IMAGE_THUNK_DATA))) {
74 return false;
75 }
76
77 if (*record_va == 0) break; // end of table
78
79 ULONGLONG iat_rva = static_cast<ULONGLONG>(*record_va); // may be VA
80 if (!convert_va_to_rva(iat_rva)) return false;
81
82 LOG_DEBUG("IAT VA: 0x%llx RVA: 0x%llx", static_cast<unsigned long long>(*record_va), static_cast<unsigned long long>(iat_rva));
83
84 const T_FIELD* iat_record_ptr = (T_FIELD*)((ULONGLONG)modulePtr + iat_rva);
85 if (!peconv::validate_ptr(modulePtr, moduleSize, iat_record_ptr, sizeof(T_FIELD))) {
86 return false;
87 }
88
89 FARPROC hProc = nullptr;
90 if (thunk_va->u1.Ordinal & ordinal_flag) {
91 T_FIELD raw_ordinal = thunk_va->u1.Ordinal & (~ordinal_flag);
92 LOG_DEBUG("ord: 0x%llx", static_cast<unsigned long long>(raw_ordinal));
93 if (func_resolver) {
94 hProc = func_resolver->resolve_func(lib_name, MAKEINTRESOURCEA(raw_ordinal));
95 }
96 }
97 else {
98 ULONGLONG name_rva = thunk_va->u1.AddressOfData;
99 if (!convert_va_to_rva(name_rva)) return false;
100
101 PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + name_rva);
102 if (!peconv::validate_ptr(modulePtr, moduleSize, by_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
103 LOG_ERROR("Invalid pointer to IMAGE_IMPORT_BY_NAME");
104 return false;
105 }
106
107 LPSTR func_name = reinterpret_cast<LPSTR>(by_name->Name);
108 if (!peconv::is_valid_import_name(modulePtr, moduleSize, func_name)) {
109 continue;
110 }
111
112 LOG_DEBUG("func: %s", func_name);
113 if (func_resolver) {
114 hProc = func_resolver->resolve_func(lib_name, func_name);
115 }
116 }
117
118 if (hProc) {
119 //rather than loading it via proxy function, we just overwrite the thunk like normal IAT:
120 *record_va = (T_FIELD) hProc;
121 LOG_DEBUG("Delayload Function resolved");
122 }
123 else {
124 LOG_DEBUG("Delayload Function not resolved");
125 }
126 }
127
128 return true;
129}
130
131bool peconv::load_delayed_imports(BYTE* modulePtr, ULONGLONG moduleBase, t_function_resolver* func_resolver)
132{
133 if (!peconv::get_directory_entry(modulePtr, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT)) {
134 return true; // nothing to resolve
135 }
136 const bool is_64bit = peconv::is64bit(modulePtr);
137 bool is_loader64 = false;
138#ifdef _WIN64
139 is_loader64 = true;
140#endif
141 if (is_64bit != is_loader64) {
142 LOG_ERROR("Loader/Payload bitness mismatch.");
143 return false;
144 }
145
146 const size_t module_size = peconv::get_image_size(modulePtr);
147 default_func_resolver default_res;
148 if (!func_resolver) {
149 func_resolver = &default_res;
150 }
151 size_t table_size = 0;
152 IMAGE_DELAYLOAD_DESCRIPTOR *first_desc = get_delayed_imps(modulePtr, module_size, table_size);
153 if (!first_desc) {
154 return false;
155 }
156
157 // Collect relocations for VA detection
158 std::unordered_set<ULONGLONG> reloc_values;
159 CollectRelocs callback(modulePtr, module_size, peconv::is64bit(modulePtr), reloc_values);
160 process_relocation_table(modulePtr, module_size, &callback);
161
162 // Helper to convert VA -> RVA if the address is in relocation table
163 auto convert_va_to_rva = [&](ULONGLONG& addr) -> bool
164 {
165 if (reloc_values.find(addr) != reloc_values.end()) {
166 if (addr < moduleBase) {
167 LOG_ERROR("Invalid VA: 0x%llx cannot convert safely", addr);
168 return false;
169 }
170 addr -= moduleBase;
171 }
172 return true;
173 };
174
175 LOG_DEBUG("Delay-import table found, table_size = %zu bytes.", table_size);
176 bool is_ok = true;
177 size_t max_count = table_size / sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
178 for (size_t i = 0; i < max_count; i++) {
179 IMAGE_DELAYLOAD_DESCRIPTOR *desc = &first_desc[i];
180 if (!validate_ptr(modulePtr, module_size, desc, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) break;
181 if (!desc->DllNameRVA) {
182 break;
183 }
184 ULONGLONG dll_name_rva = desc->DllNameRVA;
185 if (!convert_va_to_rva(dll_name_rva)) {
186 return false;
187 }
188
189 char* dll_name = (char*)((ULONGLONG) modulePtr + dll_name_rva);
190 if (!is_valid_string(modulePtr, module_size, dll_name)) continue;
191 LOG_DEBUG("Processing delayed imports for: %s", dll_name);
192 if (is_64bit) {
193#ifdef _WIN64
194 is_ok &= parse_delayed_desc<ULONGLONG,IMAGE_THUNK_DATA64>(modulePtr, module_size, moduleBase, dll_name, IMAGE_ORDINAL_FLAG64, desc, func_resolver, reloc_values);
195#else
196 return false;
197#endif
198 }
199 else {
200#ifndef _WIN64
201 is_ok &= parse_delayed_desc<DWORD, IMAGE_THUNK_DATA32>(modulePtr, module_size, moduleBase, dll_name, IMAGE_ORDINAL_FLAG32, desc, func_resolver, reloc_values);
202#else
203 return false;
204#endif
205 }
206 }
207 return is_ok;
208}
virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name)=0
bool parse_delayed_desc(BYTE *modulePtr, const size_t moduleSize, const ULONGLONG img_base, LPSTR lib_name, const T_FIELD ordinal_flag, IMAGE_DELAYLOAD_DESCRIPTOR *desc, peconv::t_function_resolver *func_resolver, const std::unordered_set< ULONGLONG > &reloc_values)
Parsing and filling the Delayload Import Table.
struct _IMAGE_DELAYLOAD_DESCRIPTOR IMAGE_DELAYLOAD_DESCRIPTOR
Parsing and filling the Import Table.
Compile-time configurable logging macros for peconv.
#define LOG_DEBUG(fmt,...)
Definition: logger.h:80
#define LOG_INFO(fmt,...)
Definition: logger.h:74
#define LOG_ERROR(fmt,...)
Definition: logger.h:60
bool load_delayed_imports(BYTE *modulePtr, const ULONGLONG moduleBase, t_function_resolver *func_resolver=nullptr)
bool is_valid_string(LPVOID modulePtr, const size_t moduleSize, const CHAR_T *name_ptr, const size_t max_len=260)
Definition: util.h:61
bool validate_ptr(IN const void *buffer_bgn, IN size_t buffer_size, IN const void *field_bgn, IN size_t field_size)
Definition: buffer_util.cpp:9
DWORD get_image_size(IN const BYTE *payload)
bool process_relocation_table(IN PVOID modulePtr, IN SIZE_T moduleSize, IN RelocBlockCallback *callback)
Definition: relocate.cpp:111
bool is64bit(IN const BYTE *pe_buffer)
bool is_valid_import_name(const PBYTE modulePtr, const size_t moduleSize, LPSTR lib_name)
IMAGE_DATA_DIRECTORY * get_directory_entry(IN const BYTE *pe_buffer, IN DWORD dir_id, IN bool allow_empty=false)
IMAGE_DELAYLOAD_DESCRIPTOR * get_delayed_imps(IN const BYTE *modulePtr, IN const size_t moduleSize, OUT size_t &dir_size)
Operating on PE file's relocations table.
Miscellaneous utility functions.