libPeConv
A library to load, manipulate, dump PE files.
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
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
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
199size_t ExportsMapper::add_to_lookup(std::string moduleName, HMODULE modulePtr, ULONGLONG moduleBase)
200{
201 IMAGE_EXPORT_DIRECTORY* exp = get_export_directory(modulePtr);
202 if (exp == NULL) {
203 return 0;
204 }
205 size_t module_size = peconv::get_image_size(reinterpret_cast<const PBYTE>(modulePtr));
206 if (!is_valid_export_table(exp, modulePtr, module_size)) {
207 return 0;
208 }
209 std::string dllName = get_dll_shortname(moduleName);
210 this->dll_shortname_to_path[dllName] = moduleName;
211
212 std::map<PDWORD, DWORD> va_to_ord;
213 size_t functCount = make_ord_lookup_tables(modulePtr, module_size, va_to_ord);
214
215 //go through names:
216
217 size_t forwarded_ctr = 0;
218 SIZE_T namesCount = exp->NumberOfNames;
219
220 DWORD funcsListRVA = exp->AddressOfFunctions;
221 DWORD funcNamesListRVA = exp->AddressOfNames;
222 DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
223
224 size_t mapped_ctr = 0;
225
226 for (SIZE_T i = 0; i < namesCount; i++) {
227 DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
228 WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*) modulePtr + i * sizeof(WORD));
229 DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + (*nameIndex) * sizeof(DWORD));
230 if (*funcRVA == 0) {
231#ifdef _DEBUG
232 std::cout << ">>> Skipping 0 function address at RVA:" << std::hex << (BYTE*)funcRVA - (BYTE*)modulePtr << "(name)\n";
233#endif
234 //skip if the function RVA is 0 (empty export)
235 continue;
236 }
237
238 LPSTR name = (LPSTR)(*nameRVA + (BYTE*) modulePtr);
239 if (!peconv::validate_ptr(modulePtr, module_size, name, sizeof(char))) break;
240
241 DWORD funcOrd = get_ordinal(funcRVA, va_to_ord);
242 DWORD callRVA = *funcRVA;
243 ExportedFunc currFunc(dllName, name, funcOrd);
244
245 int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
246 if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
247 if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
248 }
249 //go through unnamed functions exported by ordinals:
250 std::map<PDWORD, DWORD>::iterator ord_itr = va_to_ord.begin();
251 for (;ord_itr != va_to_ord.end(); ++ord_itr) {
252
253 DWORD* funcRVA = ord_itr->first;
254 DWORD callRVA = *funcRVA;
255 ExportedFunc currFunc(dllName, ord_itr->second);
256
257 int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
258 if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
259 if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
260 }
261#ifdef _DEBUG
262 std::cout << "Finished exports parsing, mapped: "<< mapped_ctr << " forwarded: " << forwarded_ctr << std::endl;
263#endif
264 return mapped_ctr;
265}
std::string toString() const
std::map< std::string, std::string > dll_shortname_to_path
size_t add_to_lookup(std::string moduleName, HMODULE modulePtr, ULONGLONG moduleBase)
void associateVaAndFunc(ULONGLONG va, const ExportedFunc &func)
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...
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
size_t forwarder_name_len(BYTE *fPtr)
DWORD get_image_size(IN const BYTE *payload)
std::string format_dll_func(const std::string &str)
IMAGE_EXPORT_DIRECTORY * get_export_directory(IN HMODULE modulePtr)
std::string get_dll_shortname(const std::string &str)