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