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 "peconv/logger.h"
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 (!peconv::validate_ptr(modulePtr, moduleSize, recordRVA, sizeof(DWORD))) {
60 break;
61 }
62 if (*recordRVA == 0) {
63 LOG_INFO("Skipping 0 function address at RVA: 0x%llx (ord).", (unsigned long long)((BYTE*)recordRVA - (BYTE*)modulePtr));
64 //skip if the function RVA is 0 (empty export)
65 continue;
66 }
67 DWORD ordinal = ordBase + i;
68 va_to_ord[recordRVA] = ordinal;
69 }
70 return functCount;
71}
72
73size_t ExportsMapper::resolve_forwarders(const ULONGLONG va, ExportedFunc &currFunc)
74{
75 size_t resolved = 0;
76 //resolve forwarders of this function (if any):
77 std::map<ExportedFunc, std::set<ExportedFunc>>::iterator fItr = forwarders_lookup.find(currFunc);
78 if (fItr != forwarders_lookup.end()) {
79 std::set<ExportedFunc>::iterator sItr;
80 for (sItr = fItr->second.begin(); sItr != fItr->second.end(); ++sItr) {
81 associateVaAndFunc(va, *sItr);
82 resolved++;
83 }
84 }
85 return resolved;
86}
87
88bool ExportsMapper::add_forwarded(ExportedFunc &currFunc, DWORD callRVA, PBYTE modulePtr, size_t moduleSize)
89{
90 PBYTE fPtr = modulePtr + callRVA;
91 if (!peconv::validate_ptr(modulePtr, moduleSize, fPtr, 1)) {
92 return false;
93 }
94 if (peconv::forwarder_name_len(fPtr) < 1) {
95 return false; //not forwarded
96 }
97 std::string forwardedFunc = format_dll_func((char*)fPtr);
98 if (forwardedFunc.length() == 0) {
99 return false; //not forwarded
100 }
101
102 ExportedFunc forwarder(forwardedFunc);
103 if (!forwarder.isValid()) {
104 LOG_INFO("Skipped invalid forwarder.");
105 return false;
106 }
107 forwarders_lookup[forwarder].insert(currFunc);
108
109 if (func_to_va[forwarder] != 0) {
110 ULONGLONG va = func_to_va[forwarder];
111 associateVaAndFunc(va, currFunc);
112 }
113 return true;
114}
115
116DWORD get_ordinal(PDWORD recordPtr, std::map<PDWORD, DWORD> &va_to_ord)
117{
118 std::map<PDWORD, DWORD>::iterator ord_itr = va_to_ord.find(recordPtr);
119 if (ord_itr == va_to_ord.end()) {
120 //ordinal not found
121 return -1;
122 }
123 DWORD ordinal = ord_itr->second;
124 va_to_ord.erase(ord_itr);
125 return ordinal;
126}
127
128bool ExportsMapper::add_to_maps(ULONGLONG va, ExportedFunc &currFunc)
129{
130 associateVaAndFunc(va, currFunc);
131 resolve_forwarders(va, currFunc);
132 return true;
133}
134
135bool is_valid_export_table(IMAGE_EXPORT_DIRECTORY* exp, HMODULE modulePtr, const size_t module_size)
136{
137 if (exp == nullptr) return false;
138
139 const SIZE_T namesCount = exp->NumberOfNames;
140 const SIZE_T funcCount = exp->NumberOfFunctions;
141
142 const DWORD funcsListRVA = exp->AddressOfFunctions;
143 const DWORD funcNamesListRVA = exp->AddressOfNames;
144 const DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
145
146 for (DWORD i = 0; i < funcCount; i++) {
147 DWORD* recordRVA = (DWORD*)(funcsListRVA + (BYTE*)modulePtr + i * sizeof(DWORD));
148 if (!peconv::validate_ptr(modulePtr, module_size, recordRVA, sizeof(DWORD))) {
149 return false;
150 }
151 }
152
153 for (SIZE_T i = 0; i < namesCount; i++) {
154 DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*)modulePtr + i * sizeof(DWORD));
155 WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*)modulePtr + i * sizeof(WORD));
156 if ((!peconv::validate_ptr(modulePtr, module_size, nameRVA, sizeof(DWORD)))
157 || (!peconv::validate_ptr(modulePtr, module_size, nameIndex, sizeof(WORD))))
158 {
159 return false;
160 }
161 DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*)modulePtr + (*nameIndex) * sizeof(DWORD));
162 if (!peconv::validate_ptr(modulePtr, module_size, funcRVA, sizeof(DWORD)))
163 {
164 return false;
165 }
166 }
167 return true;
168}
169
170ExportsMapper::ADD_FUNC_RES ExportsMapper::add_function_to_lookup(HMODULE modulePtr, ULONGLONG moduleBase, size_t moduleSize, ExportedFunc &currFunc, DWORD callRVA)
171{
172 if (add_forwarded(currFunc, callRVA, (BYTE*)modulePtr, moduleSize)) {
173 LOG_DEBUG("FWD %s -> %p.", currFunc.toString().c_str(), (char*)modulePtr + callRVA);
174 return ExportsMapper::RES_FORWARDED;
175 }
176
177 ULONGLONG callVa = callRVA + moduleBase;
178 if (!peconv::validate_ptr((BYTE*)moduleBase, moduleSize, (BYTE*)callVa, sizeof(ULONGLONG))) {
179 // this may happen when the function was forwarded and it is already filled
180 LOG_INFO("Validation failed: %s.", currFunc.toString().c_str());
181 return ExportsMapper::RES_INVALID;
182 }
183 //not forwarded, simple case:
184 add_to_maps(callVa, currFunc);
185 return ExportsMapper::RES_MAPPED;
186}
187
188
189size_t ExportsMapper::add_to_lookup(std::string moduleName, HMODULE modulePtr, size_t module_size, ULONGLONG moduleBase)
190{
191 IMAGE_EXPORT_DIRECTORY* exp = get_export_directory(modulePtr);
192 if (exp == NULL) {
193 return 0;
194 }
195 if (module_size == 0) {
196 module_size = peconv::get_image_size(reinterpret_cast<const PBYTE>(modulePtr));
197 if (module_size == 0) return 0;
198 }
199 if (!is_valid_export_table(exp, modulePtr, module_size)) {
200 return 0;
201 }
202 const bool is64b = peconv::is64bit(reinterpret_cast<const PBYTE>(modulePtr));
203 DllInfo info(moduleBase, module_size, is64b, moduleName);
204 dll_base_to_info[moduleBase] = info;
205
206 const std::string dllName = info.shortName;
207 this->dll_shortname_to_base[dllName].insert(moduleBase);
208
209 std::map<PDWORD, DWORD> va_to_ord;
210 size_t functCount = make_ord_lookup_tables(modulePtr, module_size, va_to_ord);
211
212 //go through names:
213
214 size_t forwarded_ctr = 0;
215 SIZE_T namesCount = exp->NumberOfNames;
216
217 DWORD funcsListRVA = exp->AddressOfFunctions;
218 DWORD funcNamesListRVA = exp->AddressOfNames;
219 DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
220
221 size_t mapped_ctr = 0;
222
223 for (SIZE_T i = 0; i < namesCount; i++) {
224 DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
225 WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*) modulePtr + i * sizeof(WORD));
226 DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + (*nameIndex) * sizeof(DWORD));
227 if (*funcRVA == 0) {
228 LOG_DEBUG("Skipping 0 function address at RVA: 0x%llx (name).", (unsigned long long)((BYTE*)funcRVA - (BYTE*)modulePtr));
229 //skip if the function RVA is 0 (empty export)
230 continue;
231 }
232
233 LPSTR name = (LPSTR)(*nameRVA + (BYTE*) modulePtr);
234 if (!peconv::validate_ptr(modulePtr, module_size, name, sizeof(char))) break;
235
236 DWORD funcOrd = get_ordinal(funcRVA, va_to_ord);
237 DWORD callRVA = *funcRVA;
238 ExportedFunc currFunc(dllName, name, funcOrd);
239
240 int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
241 if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
242 if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
243 }
244 //go through unnamed functions exported by ordinals:
245 std::map<PDWORD, DWORD>::iterator ord_itr = va_to_ord.begin();
246 for (;ord_itr != va_to_ord.end(); ++ord_itr) {
247
248 DWORD* funcRVA = ord_itr->first;
249 DWORD callRVA = *funcRVA;
250 ExportedFunc currFunc(dllName, ord_itr->second);
251
252 int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
253 if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
254 if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
255 }
256 LOG_DEBUG("Finished exports parsing, mapped: %zu forwarded: %zu.", mapped_ctr, forwarded_ctr);
257 return mapped_ctr;
258}
259
260size_t ExportsMapper::get_dll_paths(IN std::string short_name, OUT std::set<std::string>& paths) const
261{
262 std::map<std::string, std::set<ULONGLONG>>::const_iterator itr = this->dll_shortname_to_base.find(short_name);
263 if (itr == this->dll_shortname_to_base.end()) {
264 return 0;
265 }
266 size_t added = 0;
267 const std::set<ULONGLONG>& bases = itr->second;
268 std::set<ULONGLONG>::const_iterator bItr;
269 for (bItr = bases.begin(); bItr != bases.end(); ++bItr) {
270 ULONGLONG base = *bItr;
271 const std::string path = get_dll_path(base);
272 paths.insert(path);
273 added++;
274 }
275 return added;
276}
277
278std::string ExportsMapper::get_dll_path(std::string short_name) const
279{
280 std::map<std::string, std::set<ULONGLONG>>::const_iterator itr = this->dll_shortname_to_base.find(short_name);
281 if (itr == this->dll_shortname_to_base.end()) {
282 return "";
283 }
284 const std::set<ULONGLONG>& bases = itr->second;
285 std::set<ULONGLONG>::const_iterator bItr;
286 for (bItr = bases.begin(); bItr != bases.end(); ++bItr) {
287 ULONGLONG base = *bItr;
288 const std::string path = get_dll_path(base);
289 if (path.length()) {
290 return path;
291 }
292 }
293 return "";
294}
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...
#define LOG_DEBUG(fmt,...)
Definition logger.h:56
#define LOG_INFO(fmt,...)
Definition logger.h:50
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