libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
exports_mapper.cpp
Go to the documentation of this file.
2#include <algorithm>
3#include <iostream>
4
5
6using namespace peconv;
7
8void ExportsMapper::print_va_to_func(std::stringstream &stream) const
9{
10 std::map<ULONGLONG, std::set<ExportedFunc>>::const_iterator itr;
11
12 for (itr = va_to_func.begin(); itr != va_to_func.end(); ++itr) {
13
14 stream << std::hex << itr->first << " :\n";
15
16 std::set<ExportedFunc>::const_iterator itr2;
17 const std::set<ExportedFunc> &funcs = itr->second;
18
19 for (itr2 = funcs.begin(); itr2 != funcs.end(); ++itr2) {
20 stream << "\t" << itr2->toString() << "\n";
21 }
22 }
23}
24
25void ExportsMapper::print_func_to_va(std::stringstream &stream) const
26{
27 std::map<ExportedFunc, ULONGLONG>::const_iterator itr;
28 for (itr = func_to_va.begin(); itr != func_to_va.end(); ++itr) {
29 stream << itr->first.toString() << " : "
30 << std::hex << itr->second << "\n";
31 }
32}
33
34ULONGLONG rebase_va(ULONGLONG va, ULONGLONG currBase, ULONGLONG targetBase)
35{
36 if (currBase == targetBase) {
37 return va;
38 }
39 ULONGLONG rva = va - (ULONGLONG) currBase;
40 return rva + targetBase;
41}
42
43size_t ExportsMapper::make_ord_lookup_tables(
44 PVOID modulePtr,
45 size_t moduleSize,
46 std::map<PDWORD, DWORD> &va_to_ord
47 )
48{
49 IMAGE_EXPORT_DIRECTORY* exp = peconv::get_export_directory((HMODULE) modulePtr);
50 if (exp == NULL) return 0;
51
52 SIZE_T functCount = exp->NumberOfFunctions;
53 DWORD funcsListRVA = exp->AddressOfFunctions;
54 DWORD ordBase = exp->Base;
55
56 //go through names:
57 for (DWORD i = 0; i < functCount; i++) {
58 DWORD* recordRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
59 if (*recordRVA == 0) {
60#ifdef _DEBUG_EX
61 std::cout << ">>> Skipping 0 function address at RVA:" << std::hex << (BYTE*)recordRVA - (BYTE*)modulePtr<< "(ord)\n";
62#endif
63 //skip if the function RVA is 0 (empty export)
64 continue;
65 }
66 if (!peconv::validate_ptr(modulePtr, moduleSize, recordRVA, sizeof(DWORD))) {
67 break;
68 }
69 DWORD ordinal = ordBase + i;
70 va_to_ord[recordRVA] = ordinal;
71 }
72 return functCount;
73}
74
75size_t ExportsMapper::resolve_forwarders(const ULONGLONG va, ExportedFunc &currFunc)
76{
77 size_t resolved = 0;
78 //resolve forwarders of this function (if any):
79 std::map<ExportedFunc, std::set<ExportedFunc>>::iterator fItr = forwarders_lookup.find(currFunc);
80 if (fItr != forwarders_lookup.end()) {
81 //printf("[+] Forwarders (%d):\n", fItr->second.size());
82 std::set<ExportedFunc>::iterator sItr;
83 for (sItr = fItr->second.begin(); sItr != fItr->second.end(); ++sItr) {
84 //printf("-> %s\n", sItr->c_str());
85 associateVaAndFunc(va, *sItr);
86 resolved++;
87 }
88 }
89 return resolved;
90}
91
92bool ExportsMapper::add_forwarded(ExportedFunc &currFunc, DWORD callRVA, PBYTE modulePtr, size_t moduleSize)
93{
94 PBYTE fPtr = modulePtr + callRVA;
95 if (!peconv::validate_ptr(modulePtr, moduleSize, fPtr, 1)) {
96 return false;
97 }
98 if (peconv::forwarder_name_len(fPtr) < 1) {
99 return false; //not forwarded
100 }
101 std::string forwardedFunc = format_dll_func((char*)fPtr);
102 if (forwardedFunc.length() == 0) {
103 return false; //not forwarded
104 }
105
106 ExportedFunc forwarder(forwardedFunc);
107 if (!forwarder.isValid()) {
108#ifdef _DEBUG
109 std::cerr << "Skipped invalid forwarder" << std::endl;
110#endif
111 return false;
112 }
113 forwarders_lookup[forwarder].insert(currFunc);
114
115 if (func_to_va[forwarder] != 0) {
116 ULONGLONG va = func_to_va[forwarder];
117 associateVaAndFunc(va, currFunc);
118 }
119 return true;
120}
121
122DWORD get_ordinal(PDWORD recordPtr, std::map<PDWORD, DWORD> &va_to_ord)
123{
124 std::map<PDWORD, DWORD>::iterator ord_itr = va_to_ord.find(recordPtr);
125 if (ord_itr == va_to_ord.end()) {
126 //ordinal not found
127 return -1;
128 }
129 DWORD ordinal = ord_itr->second;
130 va_to_ord.erase(ord_itr);
131 return ordinal;
132}
133
134bool ExportsMapper::add_to_maps(ULONGLONG va, ExportedFunc &currFunc)
135{
136 associateVaAndFunc(va, currFunc);
137 resolve_forwarders(va, currFunc);
138 return true;
139}
140
141bool is_valid_export_table(IMAGE_EXPORT_DIRECTORY* exp, HMODULE modulePtr, const size_t module_size)
142{
143 if (exp == nullptr) return false;
144
145 const SIZE_T namesCount = exp->NumberOfNames;
146 const SIZE_T funcCount = exp->NumberOfFunctions;
147
148 const DWORD funcsListRVA = exp->AddressOfFunctions;
149 const DWORD funcNamesListRVA = exp->AddressOfNames;
150 const DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
151
152 for (DWORD i = 0; i < funcCount; i++) {
153 DWORD* recordRVA = (DWORD*)(funcsListRVA + (BYTE*)modulePtr + i * sizeof(DWORD));
154 if (!peconv::validate_ptr(modulePtr, module_size, recordRVA, sizeof(DWORD))) {
155 return false;
156 }
157 }
158
159 for (SIZE_T i = 0; i < namesCount; i++) {
160 DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*)modulePtr + i * sizeof(DWORD));
161 WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*)modulePtr + i * sizeof(WORD));
162 if ((!peconv::validate_ptr(modulePtr, module_size, nameRVA, sizeof(DWORD)))
163 || (!peconv::validate_ptr(modulePtr, module_size, nameIndex, sizeof(WORD))))
164 {
165 return false;
166 }
167 DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*)modulePtr + (*nameIndex) * sizeof(DWORD));
168 if (!peconv::validate_ptr(modulePtr, module_size, funcRVA, sizeof(DWORD)))
169 {
170 return false;
171 }
172 }
173 return true;
174}
175
176ExportsMapper::ADD_FUNC_RES ExportsMapper::add_function_to_lookup(HMODULE modulePtr, ULONGLONG moduleBase, size_t moduleSize, ExportedFunc &currFunc, DWORD callRVA)
177{
178 if (add_forwarded(currFunc, callRVA, (BYTE*)modulePtr, moduleSize)) {
179#ifdef _DEBUG_EX
180 char* fPtr = (char*)modulePtr + callRVA;
181 std::cout << "FWD " << currFunc.toString() << " -> " << fPtr << "\n";
182#endif
183 return ExportsMapper::RES_FORWARDED;
184 }
185
186 ULONGLONG callVa = callRVA + moduleBase;
187 if (!peconv::validate_ptr((BYTE*)moduleBase, moduleSize, (BYTE*)callVa, sizeof(ULONGLONG))) {
188 // this may happen when the function was forwarded and it is already filled
189#ifdef _DEBUG
190 std::cout << "Validation failed: " << currFunc.toString() << "\n";
191#endif
192 return ExportsMapper::RES_INVALID;
193 }
194 //not forwarded, simple case:
195 add_to_maps(callVa, currFunc);
196 return ExportsMapper::RES_MAPPED;
197}
198
199
200size_t ExportsMapper::add_to_lookup(std::string moduleName, HMODULE modulePtr, size_t module_size, ULONGLONG moduleBase)
201{
202 IMAGE_EXPORT_DIRECTORY* exp = get_export_directory(modulePtr);
203 if (exp == NULL) {
204 return 0;
205 }
206 if (module_size == 0) {
207 module_size = peconv::get_image_size(reinterpret_cast<const PBYTE>(modulePtr));
208 if (module_size == 0) return 0;
209 }
210 if (!is_valid_export_table(exp, modulePtr, module_size)) {
211 return 0;
212 }
213 const bool is64b = peconv::is64bit(reinterpret_cast<const PBYTE>(modulePtr));
214 DllInfo info(moduleBase, module_size, is64b, moduleName);
215 dll_base_to_info[moduleBase] = info;
216
217 const std::string dllName = info.shortName;
218 this->dll_shortname_to_base[dllName].insert(moduleBase);
219
220 std::map<PDWORD, DWORD> va_to_ord;
221 size_t functCount = make_ord_lookup_tables(modulePtr, module_size, va_to_ord);
222
223 //go through names:
224
225 size_t forwarded_ctr = 0;
226 SIZE_T namesCount = exp->NumberOfNames;
227
228 DWORD funcsListRVA = exp->AddressOfFunctions;
229 DWORD funcNamesListRVA = exp->AddressOfNames;
230 DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
231
232 size_t mapped_ctr = 0;
233
234 for (SIZE_T i = 0; i < namesCount; i++) {
235 DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
236 WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*) modulePtr + i * sizeof(WORD));
237 DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + (*nameIndex) * sizeof(DWORD));
238 if (*funcRVA == 0) {
239#ifdef _DEBUG_EX
240 std::cout << ">>> Skipping 0 function address at RVA:" << std::hex << (BYTE*)funcRVA - (BYTE*)modulePtr << "(name)\n";
241#endif
242 //skip if the function RVA is 0 (empty export)
243 continue;
244 }
245
246 LPSTR name = (LPSTR)(*nameRVA + (BYTE*) modulePtr);
247 if (!peconv::validate_ptr(modulePtr, module_size, name, sizeof(char))) break;
248
249 DWORD funcOrd = get_ordinal(funcRVA, va_to_ord);
250 DWORD callRVA = *funcRVA;
251 ExportedFunc currFunc(dllName, name, funcOrd);
252
253 int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
254 if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
255 if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
256 }
257 //go through unnamed functions exported by ordinals:
258 std::map<PDWORD, DWORD>::iterator ord_itr = va_to_ord.begin();
259 for (;ord_itr != va_to_ord.end(); ++ord_itr) {
260
261 DWORD* funcRVA = ord_itr->first;
262 DWORD callRVA = *funcRVA;
263 ExportedFunc currFunc(dllName, ord_itr->second);
264
265 int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
266 if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
267 if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
268 }
269#ifdef _DEBUG
270 std::cout << "Finished exports parsing, mapped: "<< mapped_ctr << " forwarded: " << forwarded_ctr << std::endl;
271#endif
272 return mapped_ctr;
273}
274
275size_t ExportsMapper::get_dll_paths(IN std::string short_name, OUT std::set<std::string>& paths) const
276{
277 std::map<std::string, std::set<ULONGLONG>>::const_iterator itr = this->dll_shortname_to_base.find(short_name);
278 if (itr == this->dll_shortname_to_base.end()) {
279 return 0;
280 }
281 size_t added = 0;
282 const std::set<ULONGLONG>& bases = itr->second;
283 std::set<ULONGLONG>::const_iterator bItr;
284 for (bItr = bases.begin(); bItr != bases.end(); ++bItr) {
285 ULONGLONG base = *bItr;
286 const std::string path = get_dll_path(base);
287 paths.insert(path);
288 added++;
289 }
290 return added;
291}
292
293std::string ExportsMapper::get_dll_path(std::string short_name) const
294{
295 std::map<std::string, std::set<ULONGLONG>>::const_iterator itr = this->dll_shortname_to_base.find(short_name);
296 if (itr == this->dll_shortname_to_base.end()) {
297 return "";
298 }
299 const std::set<ULONGLONG>& bases = itr->second;
300 std::set<ULONGLONG>::const_iterator bItr;
301 for (bItr = bases.begin(); bItr != bases.end(); ++bItr) {
302 ULONGLONG base = *bItr;
303 const std::string path = get_dll_path(base);
304 if (path.length()) {
305 return path;
306 }
307 }
308 return "";
309}
std::string toString() const
void print_va_to_func(std::stringstream &stream) const
size_t get_dll_paths(IN std::string short_name, OUT std::set< std::string > &paths) const
std::map< ULONGLONG, DllInfo > dll_base_to_info
std::map< std::string, std::set< ULONGLONG > > dll_shortname_to_base
size_t add_to_lookup(std::string moduleName, HMODULE modulePtr, size_t moduleSize, ULONGLONG moduleBase)
void associateVaAndFunc(ULONGLONG va, const ExportedFunc &func)
std::string get_dll_path(ULONGLONG base) const
std::map< ExportedFunc, ULONGLONG > func_to_va
void print_func_to_va(std::stringstream &stream) const
std::map< ExportedFunc, std::set< ExportedFunc > > forwarders_lookup
std::map< ULONGLONG, std::set< ExportedFunc > > va_to_func
ULONGLONG rebase_va(ULONGLONG va, ULONGLONG currBase, ULONGLONG targetBase)
bool is_valid_export_table(IMAGE_EXPORT_DIRECTORY *exp, HMODULE modulePtr, const size_t module_size)
DWORD get_ordinal(PDWORD recordPtr, std::map< PDWORD, DWORD > &va_to_ord)
A definition of ExportsMapper class. Creates a lookup of all the exported functions from the supplied...
size_t forwarder_name_len(BYTE *fPtr)
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 is64bit(IN const BYTE *pe_buffer)
std::string format_dll_func(const std::string &str)
IMAGE_EXPORT_DIRECTORY * get_export_directory(IN HMODULE modulePtr)
std::string shortName