libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
fix_imports.cpp
Go to the documentation of this file.
3#include "peconv/file_util.h"
4
5#include "peconv/logger.h"
6#include <algorithm>
7
8using namespace peconv;
9
10template <typename FIELD_T>
11size_t find_addresses_to_fill(FIELD_T call_via, FIELD_T thunk_addr, LPVOID modulePtr, size_t moduleSize, IN const peconv::ExportsMapper& exportsMap, OUT std::set<ULONGLONG> &addresses)
12{
13 size_t addrCounter = 0;
14 do {
15 LPVOID call_via_ptr = (LPVOID)((ULONGLONG)modulePtr + call_via);
16 if (call_via_ptr == nullptr) break;
17
18 LPVOID thunk_ptr = (LPVOID)((ULONGLONG)modulePtr + thunk_addr);
19 if (thunk_ptr == nullptr) break;
20
21 if (!validate_ptr(modulePtr, moduleSize, thunk_ptr, sizeof(FIELD_T))) {
22 break;
23 }
24 if (!validate_ptr(modulePtr, moduleSize, call_via_ptr, sizeof(FIELD_T))) {
25 break;
26 }
27 FIELD_T *thunk_val = reinterpret_cast<FIELD_T*>(thunk_ptr);
28 FIELD_T *call_via_val = reinterpret_cast<FIELD_T*>(call_via_ptr);
29 if ((*call_via_val) == 0) {
30 //nothing to fill, probably the last record
31 break;
32 }
33
34 ULONGLONG searchedAddr = ULONGLONG(*call_via_val);
35 if (exportsMap.find_export_by_va(searchedAddr) != nullptr) {
36 addresses.insert(searchedAddr);
37 addrCounter++;
38 }
39 //---
40 call_via += sizeof(FIELD_T);
41 thunk_addr += sizeof(FIELD_T);
42 } while (true);
43
44 return addrCounter;
45}
46
47std::set<std::string> get_all_dlls_exporting_function(ULONGLONG func_addr, const peconv::ExportsMapper& exportsMap)
48{
49 std::set<std::string> currDllNames;
50 //1. Get all the functions from all accessible DLLs that correspond to this address:
51 const std::set<ExportedFunc>* exports_for_va = exportsMap.find_exports_by_va(func_addr);
52 if (!exports_for_va) {
53 LOG_WARNING("Cannot find any DLL exporting: 0x%llx.", (unsigned long long)func_addr);
54 return currDllNames; //empty
55 }
56 //2. Iterate through their DLL names and add them to a set:
57 for (std::set<ExportedFunc>::iterator strItr = exports_for_va->begin();
58 strItr != exports_for_va->end();
59 ++strItr)
60 {
61 currDllNames.insert(strItr->libName);
62 }
63 return currDllNames;
64}
65
66std::set<std::string> get_dlls_intersection(const std::set<std::string> &dllNames, const std::set<std::string> &currDllNames)
67{
68 std::set<std::string> resultSet;
69 std::set_intersection(dllNames.begin(), dllNames.end(),
70 currDllNames.begin(), currDllNames.end(),
71 std::inserter(resultSet, resultSet.begin())
72 );
73 return resultSet;
74}
75
76//find the name of the DLL that can cover all the addresses of imported functions
77std::string find_covering_dll(std::set<ULONGLONG> &addresses, const peconv::ExportsMapper& exportsMap)
78{
79 std::set<std::string> mainDllsSet;
80 std::set<std::string> reserveDllSet;
81 bool isFresh = true;
82
83 // the earliest addresses are more significant for the final decision on what DLL to choose
84 // so, they should be processed at the end
85 std::set<ULONGLONG>::iterator addrItr;
86
87 for (addrItr = addresses.begin(); addrItr != addresses.end(); ++addrItr) {
88 ULONGLONG searchedAddr = *addrItr;
89 //---
90 // 1. Find all the DLLs exporting this particular function (can be forwarded etc)
91 std::set<std::string> currDllNames = get_all_dlls_exporting_function(searchedAddr, exportsMap);
92
93 //2. Which of those DLLs covers also previous functions from this series?
94 if (isFresh) {
95 //if no other function was processed before, set the current DLL set as the total set
96 mainDllsSet = currDllNames;
97 isFresh = false;
98 continue;
99 }
100 // find the intersection between the total set and the current set
101 std::set<std::string> resultSet = get_dlls_intersection(mainDllsSet, currDllNames);
102 if (resultSet.size() > 0) {
103 //found intersection, overwrite the main set
104 mainDllsSet = resultSet;
105 continue;
106 }
107 // if no intersection found in the main set, check if there is any in the reserved set:
108 resultSet = get_dlls_intersection(reserveDllSet, currDllNames);
109 if (resultSet.size() > 0) {
110 //found intersection, overwrite the main set
111 reserveDllSet = mainDllsSet; // move the current to the reserve
112 mainDllsSet = resultSet;
113 continue;
114 }
115 // no intersection found with any of the sets:
116 reserveDllSet = currDllNames; //set is as a reserved DLL: to be used if it will reoccur
117 }
118 if (mainDllsSet.size() > 0) {
119 const std::string main_dll = *(mainDllsSet.begin());
120 return main_dll;
121 }
122 return "";
123}
124
126{
127 std::string found_name = find_covering_dll(this->addresses, this->exportsMap);
128 if (found_name.length() == 0) {
129 LOG_WARNING("Cannot find a covering DLL.");
130 return false;
131 }
132 this->dllName = found_name;
133 LOG_DEBUG("Found DLL name: %s.", found_name.c_str());
134 return true;
135}
136
137size_t map_addresses_to_functions(std::set<ULONGLONG> &addresses,
138 IN const std::string &chosenDll,
139 IN const peconv::ExportsMapper& exportsMap,
140 OUT std::map<ULONGLONG, std::set<ExportedFunc>> &addr_to_func,
141 OUT std::set<ULONGLONG> &not_found
142)
143{
144 std::set<ULONGLONG> coveredAddresses;
145 std::set<ULONGLONG>::iterator addrItr;
146 for (addrItr = addresses.begin(); addrItr != addresses.end(); ++addrItr) {
147
148 ULONGLONG searchedAddr = *addrItr;
149
150 const std::set<ExportedFunc>* exports_for_va = exportsMap.find_exports_by_va(searchedAddr);
151 if (exports_for_va == nullptr) {
152 not_found.insert(searchedAddr);
153 LOG_WARNING("Cannot find any DLL exporting: 0x%llx.", (unsigned long long)searchedAddr);
154 continue;
155 }
156
157 for (std::set<ExportedFunc>::iterator strItr = exports_for_va->begin();
158 strItr != exports_for_va->end();
159 ++strItr)
160 {
161 std::string dll_name = strItr->libName;
162 if (dll_name != chosenDll) {
163 continue;
164 }
165 ExportedFunc func = *strItr;
166 addr_to_func[searchedAddr].insert(func);
167 coveredAddresses.insert(searchedAddr);
168 }
169 if (addr_to_func.find(searchedAddr) == addr_to_func.end()) {
170 const ExportedFunc* func = exportsMap.find_export_by_va(searchedAddr);
171 not_found.insert(searchedAddr);
172 if (func) {
173 LOG_WARNING("Function '%s' not found in the covering DLL: %s.", func->toString().c_str(), chosenDll.c_str());
174 }
175 else {
176 LOG_WARNING("Function at [0x%llx] not found in the covering DLL: %s.", (unsigned long long)searchedAddr, chosenDll.c_str());
177 }
178 }
179 }
180 return coveredAddresses.size();
181}
182
184{
185 //reset all stored info:
186 this->mappedDllName = dll;
187 if (this->addrToFunc.size() > 0) {
188 this->addrToFunc.clear();
189 }
190 this->notFound.clear();
191
192 const size_t coveredCount = map_addresses_to_functions(this->addresses, dll, this->exportsMap, this->addrToFunc, this->notFound);
193 if (notFound.size()) {
194 LOG_WARNING("Not all addresses are covered! Not found: %zu.", notFound.size());
195 } else {
196 LOG_DEBUG("All addresses covered.");
197 }
198 return coveredCount;
199}
200
201void ImpsNotCovered::insert(DWORD thunkRVA, ULONGLONG searchedAddr)
202{
203 LOG_WARNING("Function not recovered: [0x%llx].", (unsigned long long)searchedAddr);
204 thunkToAddr[thunkRVA] = searchedAddr;
205}
206
207
208bool peconv::fix_imports(IN OUT PVOID modulePtr, IN size_t moduleSize, IN const peconv::ExportsMapper& exportsMap, OUT OPTIONAL peconv::ImpsNotCovered* notCovered)
209{
210 bool skip_bound = false; // skip boud imports?
211 IMAGE_DATA_DIRECTORY *importsDir = peconv::get_directory_entry((const BYTE*) modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
212 if (!importsDir) {
213 return true; // done! no imports -> nothing to fix
214 }
215 bool is64 = peconv::is64bit((BYTE*)modulePtr);
216 DWORD maxSize = importsDir->Size;
217 DWORD impAddr = importsDir->VirtualAddress;
218
219 IMAGE_IMPORT_DESCRIPTOR* lib_desc = nullptr;
220 DWORD parsedSize = 0;
221 LOG_DEBUG("---IMP---");
222
223 while (parsedSize < maxSize) {
224
225 lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR) modulePtr);
226 if (!validate_ptr(modulePtr, moduleSize, lib_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
227 LOG_ERROR("Invalid import descriptor pointer.");
228 return false;
229 }
230 parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR);
231 if (!lib_desc->OriginalFirstThunk && !lib_desc->FirstThunk) {
232 break;
233 }
234 const bool is_bound = (lib_desc->TimeDateStamp == (-1));
235 if (is_bound && skip_bound) {
236 continue;
237 }
238 LOG_DEBUG("Imported Lib: %x : %x : %x", lib_desc->FirstThunk, lib_desc->OriginalFirstThunk, lib_desc->Name);
239
240 std::string lib_name = "";
241 if (lib_desc->Name != 0) {
242 LPSTR name_ptr = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
243 if (validate_ptr(modulePtr, moduleSize, name_ptr, sizeof(char) * MIN_DLL_LEN)) {
244 lib_name = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
245 }
246 }
247
248 DWORD call_via = lib_desc->FirstThunk;
249 DWORD thunk_addr = lib_desc->OriginalFirstThunk; // warning: it can be NULL!
250 std::set<ULONGLONG> addresses;
251 if (!is64) {
252 find_addresses_to_fill<DWORD>(call_via, thunk_addr, modulePtr, moduleSize, exportsMap, addresses);
253 } else {
254 find_addresses_to_fill<ULONGLONG>(call_via, thunk_addr, modulePtr, moduleSize, exportsMap, addresses);
255 }
256 ImportedDllCoverage dllCoverage(addresses, exportsMap);
257 bool is_all_covered = dllCoverage.findCoveringDll();
258 bool is_lib_erased = false;
259
260 lib_name = get_dll_shortname(lib_name); //without extension
261
262 if (lib_name.length() == 0) {
263 is_lib_erased = true;
264 if (is_all_covered) {
265 // set a name of the covering DLL:
266 lib_name = dllCoverage.dllName;
267 }
268 }
269 if (lib_name.length() == 0) {
270 //could not find a relevant DLL
271 continue;
272 }
273 LOG_DEBUG("Lib: %s.", lib_name.c_str());
274 if (!dllCoverage.mapAddressesToFunctions(lib_name)) {
275 // cannot find any functions imported from this DLL
276 continue;
277 }
278 //everything mapped, now recover it:
279 ImportsUneraser impUneraser(modulePtr, moduleSize);
280 if (!impUneraser.uneraseDllImports(lib_desc, dllCoverage, notCovered)) {
281 return false;
282 }
283 if (is_lib_erased) {
284 const std::string dll_with_ext = exportsMap.get_dll_fullname(dllCoverage.dllName);
285 impUneraser.uneraseDllName(lib_desc, dll_with_ext);
286 }
287 }
288 LOG_DEBUG("---------");
289 return true;
290}
std::string toString() const
const std::set< ExportedFunc > * find_exports_by_va(ULONGLONG va) const
const peconv::ExportsMapper & exportsMap
Definition: fix_imports.h:117
size_t mapAddressesToFunctions(const std::string &_mappedDllName)
std::set< ULONGLONG > notFound
Definition: fix_imports.h:95
std::set< ULONGLONG > & addresses
Definition: fix_imports.h:112
std::map< ULONGLONG, std::set< ExportedFunc > > addrToFunc
Definition: fix_imports.h:90
bool uneraseDllName(IMAGE_IMPORT_DESCRIPTOR *lib_desc, const std::string &dll_name)
bool uneraseDllImports(IN OUT IMAGE_IMPORT_DESCRIPTOR *lib_desc, IN ImportedDllCoverage &dllCoverage, OUT OPTIONAL ImpsNotCovered *not_covered)
void insert(DWORD thunkRVA, ULONGLONG searchedAddr)
std::map< DWORD, ULONGLONG > thunkToAddr
Definition: fix_imports.h:41
Functions related to operations on files. Wrappers for read/write.
std::string find_covering_dll(std::set< ULONGLONG > &addresses, const peconv::ExportsMapper &exportsMap)
Definition: fix_imports.cpp:77
size_t map_addresses_to_functions(std::set< ULONGLONG > &addresses, IN const std::string &chosenDll, IN const peconv::ExportsMapper &exportsMap, OUT std::map< ULONGLONG, std::set< ExportedFunc > > &addr_to_func, OUT std::set< ULONGLONG > &not_found)
std::set< std::string > get_all_dlls_exporting_function(ULONGLONG func_addr, const peconv::ExportsMapper &exportsMap)
Definition: fix_imports.cpp:47
size_t find_addresses_to_fill(FIELD_T call_via, FIELD_T thunk_addr, LPVOID modulePtr, size_t moduleSize, IN const peconv::ExportsMapper &exportsMap, OUT std::set< ULONGLONG > &addresses)
Definition: fix_imports.cpp:11
std::set< std::string > get_dlls_intersection(const std::set< std::string > &dllNames, const std::set< std::string > &currDllNames)
Definition: fix_imports.cpp:66
Functions and classes responsible for fixing Import Table. A definition of ImportedDllCoverage class.
#define MIN_DLL_LEN
Definition: fix_imports.h:21
A definition of ImportsUneraser class - for recovery of a partialy erased Import Table.
#define LOG_DEBUG(fmt,...)
Definition: logger.h:56
#define LOG_ERROR(fmt,...)
Definition: logger.h:36
#define LOG_WARNING(fmt,...)
Definition: logger.h:44
bool fix_imports(IN OUT PVOID modulePtr, IN size_t moduleSize, IN const peconv::ExportsMapper &exportsMap, OUT OPTIONAL peconv::ImpsNotCovered *notCovered)
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
bool is64bit(IN const BYTE *pe_buffer)
IMAGE_DATA_DIRECTORY * get_directory_entry(IN const BYTE *pe_buffer, IN DWORD dir_id, IN bool allow_empty=false)
std::string get_dll_shortname(const std::string &str)