libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
imports_loader.cpp
Go to the documentation of this file.
2
3#include "peconv/util.h"
4#include "peconv/logger.h"
5
6using namespace peconv;
7
9{
10public:
11 FillImportThunks(BYTE* _modulePtr, size_t _moduleSize, t_function_resolver* func_resolver)
12 : ImportThunksCallback(_modulePtr, _moduleSize), funcResolver(func_resolver)
13 {
14 }
15
16 virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
17 {
18 if (this->is64b) {
19#ifdef _WIN64 // loader is 64 bit, allow to load imports for 64-bit payload:
20 IMAGE_THUNK_DATA64* desc = reinterpret_cast<IMAGE_THUNK_DATA64*>(origFirstThunkPtr);
21 ULONGLONG* call_via = reinterpret_cast<ULONGLONG*>(firstThunkPtr);
22 return processThunks_tpl<ULONGLONG, IMAGE_THUNK_DATA64>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG64);
23#else
24 LOG_ERROR("Cannot fill imports into 64-bit PE via 32-bit loader.");
25 return false;
26#endif
27 }
28 else {
29#ifndef _WIN64 // loader is 32 bit, allow to load imports for 32-bit payload:
30 IMAGE_THUNK_DATA32* desc = reinterpret_cast<IMAGE_THUNK_DATA32*>(origFirstThunkPtr);
31 DWORD* call_via = reinterpret_cast<DWORD*>(firstThunkPtr);
32 return processThunks_tpl<DWORD, IMAGE_THUNK_DATA32>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG32);
33#else
34 LOG_ERROR("Cannot fill imports into 32-bit PE via 64-bit loader.");
35 return false;
36#endif
37 }
38 }
39
40protected:
41 template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
42 bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA* desc, T_FIELD* call_via, T_FIELD ordinal_flag)
43 {
44 static_assert(sizeof(T_FIELD) >= sizeof(ULONG_PTR), "T_FIELD must be wide enough to hold a function pointer");
45
46 if (!this->funcResolver) {
47 return false;
48 }
49
50 const bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0;
51
52 FARPROC hProc = nullptr;
53 if (is_by_ord) {
54 T_FIELD raw_ordinal = desc->u1.Ordinal & (~ordinal_flag);
55 LOG_DEBUG("raw ordinal: 0x%llx.", (unsigned long long)raw_ordinal);
56 hProc = funcResolver->resolve_func(lib_name, MAKEINTRESOURCEA(static_cast<WORD>(raw_ordinal)));
57
58 }
59 else {
60 PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData);
61 if (!validate_ptr(modulePtr, moduleSize, by_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
62 LOG_ERROR("Invalid pointer to IMAGE_IMPORT_BY_NAME.");
63 return false;
64 }
65 const LPSTR func_name = reinterpret_cast<LPSTR>(by_name->Name);
66 if (!is_valid_string(modulePtr, moduleSize, func_name)) {
67 LOG_ERROR("Invalid pointer to function name.");
68 return false;
69 }
70 LOG_DEBUG("name: %s.", func_name);
71 hProc = this->funcResolver->resolve_func(lib_name, func_name);
72 }
73 if (!hProc) {
74 LOG_ERROR("Could not resolve the function.");
75 return false;
76 }
77
78 (*call_via) = reinterpret_cast<T_FIELD>(hProc);
79 return true;
80 }
81
82 //fields:
84};
85
86//---
87
92{
93public:
94 CollectThunksCallback(BYTE* _vBuf, size_t _vBufSize, std::set<DWORD>& _fields)
95 : ImportThunksCallback(_vBuf, _vBufSize), vBuf(_vBuf), vBufSize(_vBufSize),
96 fields(_fields)
97 {
98 }
99
100 virtual bool processThunks(LPSTR libName, ULONG_PTR origFirstThunkPtr, ULONG_PTR va)
101 {
102 if (!va) {
103 return false; // invalid thunk
104 }
105 const ULONGLONG module_base = reinterpret_cast<ULONGLONG>(this->vBuf);
106 if (va < module_base) {
107 return false; // not this module
108 }
109 if (va >= module_base + this->vBufSize) {
110 return false; // not this module
111 }
112 DWORD thunk_rva = MASK_TO_DWORD(va - module_base);
113 fields.insert(thunk_rva);
114 return true;
115 }
116
117 std::set<DWORD>& fields;
118 BYTE* vBuf;
119 size_t vBufSize;
120};
121
122//---
123
125{
126public:
127 CollectImportsCallback(BYTE* _modulePtr, size_t _moduleSize, std::map<DWORD, ExportedFunc*> &_thunkToFunc)
128 : ImportThunksCallback(_modulePtr, _moduleSize), thunkToFunc(_thunkToFunc)
129 {
130 }
131
132 virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
133 {
134 if (this->is64b) {
135 IMAGE_THUNK_DATA64* desc = reinterpret_cast<IMAGE_THUNK_DATA64*>(origFirstThunkPtr);
136 ULONGLONG* call_via = reinterpret_cast<ULONGLONG*>(firstThunkPtr);
137 return processThunks_tpl<ULONGLONG, IMAGE_THUNK_DATA64>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG64);
138
139 }
140 else {
141
142 IMAGE_THUNK_DATA32* desc = reinterpret_cast<IMAGE_THUNK_DATA32*>(origFirstThunkPtr);
143 DWORD* call_via = reinterpret_cast<DWORD*>(firstThunkPtr);
144 return processThunks_tpl<DWORD, IMAGE_THUNK_DATA32>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG32);
145 }
146 }
147
148protected:
149 template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
150 bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA* desc, T_FIELD* call_via, T_FIELD ordinal_flag)
151 {
152 if (!call_via) {
153 return false;
154 }
155
156 const std::string short_name = peconv::get_dll_shortname(lib_name);
157 const bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0;
158 ExportedFunc *func = nullptr;
159
160 if (is_by_ord) {
161 T_FIELD raw_ordinal = desc->u1.Ordinal & (~ordinal_flag);
162 func = new ExportedFunc(short_name, IMAGE_ORDINAL64(raw_ordinal));
163 }
164 else {
165 PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData);
166 if (!validate_ptr(modulePtr, moduleSize, by_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
167 LOG_ERROR("Invalid pointer to IMAGE_IMPORT_BY_NAME.");
168 return false;
169 }
170 const LPSTR func_name = reinterpret_cast<LPSTR>(by_name->Name);
171 if (!is_valid_string(modulePtr, moduleSize, func_name)) {
172 LOG_ERROR("Invalid pointer to function name.");
173 return false;
174 }
175 const WORD ordinal = by_name->Hint;
176 func = new ExportedFunc(short_name, func_name, ordinal);
177 }
178 if (!func) {
179 return false;
180 }
181 const DWORD rva = MASK_TO_DWORD((ULONG_PTR)call_via - (ULONG_PTR)modulePtr);
182 thunkToFunc[rva] = func;
183 return true;
184 }
185
186 std::map<DWORD, ExportedFunc*> &thunkToFunc;
187};
188
189
190//---
191
192template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
193bool process_imp_functions_tpl(BYTE* modulePtr, size_t module_size, LPSTR lib_name, DWORD call_via, DWORD thunk_addr, IN ImportThunksCallback *callback)
194{
195 static_assert(sizeof(T_FIELD) == sizeof(T_IMAGE_THUNK_DATA),"T_FIELD and T_IMAGE_THUNK_DATA must have the same size");
196
197 bool is_ok = true;
198
199 T_FIELD *thunks = (T_FIELD*)((ULONGLONG)modulePtr + thunk_addr);
200 T_FIELD *callers = (T_FIELD*)((ULONGLONG)modulePtr + call_via);
201
202 for (size_t index = 0; true; index++) {
203 T_IMAGE_THUNK_DATA* desc = reinterpret_cast<T_IMAGE_THUNK_DATA*>((LPVOID) &thunks[index]);
204 if (!validate_ptr(modulePtr, module_size, desc, sizeof(T_IMAGE_THUNK_DATA))) {
205 is_ok = false;
206 break;
207 }
208 if (desc->u1.Function == 0) {
209 LOG_DEBUG("Desc[%d], RVA = 0x%llx is empty. Finishing.", (int)index, (unsigned long long)((ULONG_PTR)&desc->u1.Function - (ULONG_PTR)modulePtr) );
210 // Probably the last record
211 break;
212 }
213 if (!validate_ptr(modulePtr, module_size, &callers[index], sizeof(T_FIELD))) {
214 is_ok = false;
215 break;
216 }
217 T_FIELD ordinal_flag = (sizeof(T_FIELD) == sizeof(ULONGLONG)) ? IMAGE_ORDINAL_FLAG64 : IMAGE_ORDINAL_FLAG32;
218 bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0;
219 if (!is_by_ord) {
220 PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData);
221 if (!validate_ptr(modulePtr, module_size, by_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
222 is_ok = false;
223 break;
224 }
225 }
226 // Basic thunk/caller pointers are verified here.
227 // Callback may perform deeper validation of import-specific data such as names.
228 if (callback && !callback->processThunks(lib_name, (ULONG_PTR)&thunks[index], (ULONG_PTR)&callers[index])) {
229 is_ok = false;
230 }
231 }
232 return is_ok;
233}
234
235//Walk through the table of imported DLLs (starting from the given descriptor) and execute the callback each time when the new record was found
236bool process_dlls(BYTE* modulePtr, size_t module_size, IMAGE_IMPORT_DESCRIPTOR *first_desc, IN ImportThunksCallback *callback)
237{
238 bool isAllFilled = true;
239 LOG_DEBUG("---IMP---");
240 const bool is64 = is64bit((BYTE*)modulePtr);
241 IMAGE_IMPORT_DESCRIPTOR* lib_desc = nullptr;
242
243 for (size_t i = 0; true; i++) {
244 lib_desc = &first_desc[i];
245 if (!validate_ptr(modulePtr, module_size, lib_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
246 break;
247 }
248 if (!lib_desc->OriginalFirstThunk && !lib_desc->FirstThunk) {
249 break;
250 }
251 LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);
252 if (!peconv::is_valid_import_name(modulePtr, module_size, lib_name)) {
253 //invalid name
254 return false;
255 }
256 DWORD call_via = lib_desc->FirstThunk;
257 DWORD thunk_addr = lib_desc->OriginalFirstThunk;
258 if (!thunk_addr) {
259 thunk_addr = lib_desc->FirstThunk;
260 }
261 LOG_DEBUG("Imported Lib: 0x%lx : 0x%lx : 0x%lx", lib_desc->FirstThunk, lib_desc->OriginalFirstThunk, lib_desc->Name);
262 bool all_solved = false;
263 if (is64) {
264 all_solved = process_imp_functions_tpl<ULONGLONG, IMAGE_THUNK_DATA64>(modulePtr, module_size, lib_name, call_via, thunk_addr, callback);
265 }
266 else {
267 all_solved = process_imp_functions_tpl<DWORD, IMAGE_THUNK_DATA32>(modulePtr, module_size, lib_name, call_via, thunk_addr, callback);
268 }
269 if (!all_solved) {
270 isAllFilled = false;
271 }
272 }
273 LOG_DEBUG("---------");
274 return isAllFilled;
275}
276
277bool peconv::process_import_table(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ImportThunksCallback *callback)
278{
279 if (moduleSize == 0) { //if not given, try to fetch
280 moduleSize = peconv::get_image_size((const BYTE*)modulePtr);
281 }
282 if (moduleSize == 0) return false;
283
284 IMAGE_DATA_DIRECTORY *importsDir = get_directory_entry((BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
285 if (!importsDir) {
286 return true; //no import table
287 }
288 const DWORD impAddr = importsDir->VirtualAddress;
289 IMAGE_IMPORT_DESCRIPTOR *first_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + (ULONG_PTR)modulePtr);
290 if (!peconv::validate_ptr(modulePtr, moduleSize, first_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
291 return false;
292 }
293 return process_dlls(modulePtr, moduleSize, first_desc, callback);
294}
295
296bool peconv::load_imports(BYTE* modulePtr, t_function_resolver* func_resolver)
297{
298 size_t moduleSize = peconv::get_image_size((const BYTE*)modulePtr);
299 if (moduleSize == 0) return false;
300
301 bool is64 = is64bit((BYTE*)modulePtr);
302 bool is_loader64 = false;
303#ifdef _WIN64
304 is_loader64 = true;
305#endif
306 if (is64 != is_loader64) {
307 LOG_ERROR("Loader/Payload bitness mismatch.");
308 return false;
309 }
310
311 default_func_resolver default_res;
312 if (!func_resolver) {
313 func_resolver = &default_res;
314 }
315
316 FillImportThunks callback(modulePtr, moduleSize, func_resolver);
317 return peconv::process_import_table(modulePtr, moduleSize, &callback);
318}
319
320// A valid name must contain printable characters. Empty name is also acceptable (may have been erased)
321bool peconv::is_valid_import_name(const PBYTE modulePtr, const size_t moduleSize, LPSTR lib_name)
322{
323 bool is_terminated = false;
324 for (size_t i = 0; i < MAX_PATH; i++) {
325 if (!peconv::validate_ptr(modulePtr, moduleSize, lib_name, sizeof(char))) {
326 return false;
327 }
328 char next_char = *lib_name;
329 if (next_char == '\0') {
330 is_terminated = true;
331 break;
332 }
333 if (next_char < 0x20 || next_char >= 0x7E) {
334 return false;
335 }
336 lib_name++;
337 }
338 return is_terminated;
339}
340
341namespace {
342 template <typename FIELD_T>
343 bool _has_valid_import_table(const PBYTE modulePtr, size_t moduleSize, size_t maxCount = 0)
344 {
345 IMAGE_DATA_DIRECTORY* importsDir = get_directory_entry((BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
346 if (!importsDir) return false;
347
348 const DWORD impAddr = importsDir->VirtualAddress;
349 if (impAddr == 0 || impAddr >= moduleSize) {
350 return false;
351 }
352 IMAGE_IMPORT_DESCRIPTOR* lib_desc = nullptr;
353 size_t parsedSize = 0;
354 size_t valid_records = 0;
355
356 bool is_terminated = false;
357 while (parsedSize < moduleSize) { // import table size doesn't matter
358
359 if (maxCount != 0 && valid_records >= maxCount) {
360 break; // break when the hardlimit was hit
361 }
362 lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)((ULONG_PTR)impAddr + parsedSize + (ULONG_PTR)modulePtr);
363 if (!peconv::validate_ptr(modulePtr, moduleSize, lib_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
364 return false;
365 }
366 parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR);
367
368 if (!lib_desc->OriginalFirstThunk && !lib_desc->FirstThunk) {
369 is_terminated = true;
370 break;
371 }
372 LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);
373 if (!is_valid_import_name(modulePtr, moduleSize, lib_name)) return false;
374
375 DWORD call_via = lib_desc->FirstThunk;
376 DWORD thunk_addr = lib_desc->OriginalFirstThunk;
377 if (!thunk_addr) thunk_addr = lib_desc->FirstThunk;
378
379 FIELD_T* thunks = (FIELD_T*)((ULONGLONG)modulePtr + thunk_addr);
380 if (!peconv::validate_ptr(modulePtr, moduleSize, thunks, sizeof(FIELD_T))) return false;
381
382 FIELD_T* callers = (FIELD_T*)((ULONGLONG)modulePtr + call_via);
383 if (!peconv::validate_ptr(modulePtr, moduleSize, callers, sizeof(FIELD_T))) return false;
384
385 valid_records++;
386 }
387 return is_terminated && (valid_records > 0);
388 }
389};
390
391bool peconv::has_valid_import_table(const PBYTE modulePtr, size_t moduleSize, size_t maxCount)
392{
393 if (peconv::is64bit(modulePtr)) {
394 return _has_valid_import_table<ULONGLONG>(modulePtr, moduleSize, maxCount);
395 }
396 return _has_valid_import_table<DWORD>(modulePtr, moduleSize, maxCount);
397}
398
399bool peconv::collect_thunks(IN BYTE* modulePtr, IN SIZE_T moduleSize, OUT std::set<DWORD>& thunk_rvas)
400{
401 CollectThunksCallback collector(modulePtr, moduleSize, thunk_rvas);
402 return peconv::process_import_table(modulePtr, moduleSize, &collector);
403}
404
405bool peconv::collect_imports(IN BYTE* modulePtr, IN SIZE_T moduleSize, OUT ImportsCollection &collection)
406{
407 CollectImportsCallback collector(modulePtr, moduleSize, collection.thunkToFunc);
408 return peconv::process_import_table(modulePtr, moduleSize, &collector);
409}
410
#define MASK_TO_DWORD(val)
Definition: buffer_util.h:12
CollectImportsCallback(BYTE *_modulePtr, size_t _moduleSize, std::map< DWORD, ExportedFunc * > &_thunkToFunc)
bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA *desc, T_FIELD *call_via, T_FIELD ordinal_flag)
virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
std::map< DWORD, ExportedFunc * > & thunkToFunc
virtual bool processThunks(LPSTR libName, ULONG_PTR origFirstThunkPtr, ULONG_PTR va)
CollectThunksCallback(BYTE *_vBuf, size_t _vBufSize, std::set< DWORD > &_fields)
std::set< DWORD > & fields
FillImportThunks(BYTE *_modulePtr, size_t _moduleSize, t_function_resolver *func_resolver)
bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA *desc, T_FIELD *call_via, T_FIELD ordinal_flag)
virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
t_function_resolver * funcResolver
virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name)=0
bool process_dlls(BYTE *modulePtr, size_t module_size, IMAGE_IMPORT_DESCRIPTOR *first_desc, IN ImportThunksCallback *callback)
bool process_imp_functions_tpl(BYTE *modulePtr, size_t module_size, LPSTR lib_name, DWORD call_via, DWORD thunk_addr, IN ImportThunksCallback *callback)
Parsing and filling the Import Table.
Compile-time configurable logging macros for peconv.
#define LOG_DEBUG(fmt,...)
Definition: logger.h:80
#define LOG_ERROR(fmt,...)
Definition: logger.h:60
bool has_valid_import_table(const PBYTE modulePtr, size_t moduleSize, size_t max_count=0)
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 process_import_table(IN BYTE *modulePtr, IN SIZE_T moduleSize, IN ImportThunksCallback *callback)
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 collect_thunks(IN BYTE *modulePtr, IN SIZE_T moduleSize, OUT std::set< DWORD > &thunk_rvas)
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)
bool collect_imports(IN BYTE *modulePtr, IN SIZE_T moduleSize, OUT ImportsCollection &collection)
std::string get_dll_shortname(const std::string &str)
bool load_imports(BYTE *modulePtr, t_function_resolver *func_resolver=nullptr)
Miscellaneous utility functions.