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