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