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