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