libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
imports_uneraser.cpp
Go to the documentation of this file.
2
3#include <iostream>
4
5using namespace peconv;
6
7LPVOID search_name(std::string name, const char* modulePtr, size_t moduleSize)
8{
9 const char* namec = name.c_str();
10 const size_t searched_len = name.length() + 1; // with terminating NULL
11 const char* found_ptr = std::search(modulePtr, modulePtr + moduleSize, namec, namec + searched_len);
12 if (found_ptr == NULL) {
13 return NULL;
14 }
15 size_t o = found_ptr - modulePtr;
16 if (o < moduleSize) {
17 return (LPVOID)(found_ptr);
18 }
19 return NULL;
20}
21
22bool ImportsUneraser::writeFoundDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &found_name)
23{
24#ifdef _DEBUG
25 std::cout << "Found name:" << found_name << std::endl;
26#endif
27 LPSTR name_ptr = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
28 size_t full_name_len = found_name.length() + 1; // with terminating zero
29 if (!validate_ptr(modulePtr, moduleSize, name_ptr, full_name_len)) {
30 //corner case: allow to save the name at the very end of the buffer, without the terminating zero
31 full_name_len--;
32 if (!validate_ptr(modulePtr, moduleSize, name_ptr, full_name_len)) {
33 return false; //invalid pointer, cannot save
34 }
35 }
36 memcpy(name_ptr, found_name.c_str(), full_name_len);
37 return true;
38}
39
40bool ImportsUneraser::uneraseDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &dll_name)
41{
42 LPSTR name_ptr = nullptr;
43 if (lib_desc->Name != 0) {
44 name_ptr = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
45 }
46 if (name_ptr == nullptr || !validate_ptr(modulePtr, moduleSize, name_ptr, sizeof(char) * MIN_DLL_LEN)) {
47 //try to get the cave:
48 DWORD cave_size = DWORD(dll_name.length() + 1 + 5); //ending null + padding
49 PBYTE ptr = find_ending_cave(modulePtr, moduleSize, cave_size);
50 if (ptr == nullptr) {
51 std::cerr << "Cannot save the DLL name: " << dll_name << std::endl;
52 return false;
53 }
54 DWORD cave_rva = static_cast<DWORD>(ptr - modulePtr);
55 lib_desc->Name = cave_rva;
56 }
57
58 if (writeFoundDllName(lib_desc, dll_name)) {
59 return true; // written the found name
60 }
61 return false;
62}
63
64template <typename FIELD_T>
65bool ImportsUneraser::findNameInBinaryAndFill(IMAGE_IMPORT_DESCRIPTOR* lib_desc,
66 LPVOID call_via_ptr,
67 LPVOID thunk_ptr,
68 const FIELD_T ordinal_flag,
69 std::map<ULONGLONG, std::set<ExportedFunc>> &addr_to_func
70)
71{
72 if (call_via_ptr == NULL || modulePtr == NULL || lib_desc == NULL) {
73 return false; //malformed input
74 }
75 IMAGE_DATA_DIRECTORY *importsDir = get_directory_entry((BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
76 if (!importsDir) return false;
77
78 const DWORD impAddr = importsDir->VirtualAddress; //start of the import table
79
80 FIELD_T *call_via_val = (FIELD_T*)call_via_ptr;
81 if (*call_via_val == 0) {
82 //nothing to fill, probably the last record
83 return false;
84 }
85 ULONGLONG searchedAddr = ULONGLONG(*call_via_val);
86 bool is_name_saved = false;
87
88 FIELD_T lastOrdinal = 0; //store also ordinal of the matching function
89 std::set<ExportedFunc>::iterator funcname_itr = addr_to_func[searchedAddr].begin();
90
91 for (funcname_itr = addr_to_func[searchedAddr].begin();
92 funcname_itr != addr_to_func[searchedAddr].end();
93 ++funcname_itr)
94 {
95 const ExportedFunc &found_func = *funcname_itr;
96 lastOrdinal = found_func.funcOrdinal;
97
98 const char* names_start = ((const char*) modulePtr + impAddr);
99 BYTE* found_ptr = (BYTE*) search_name(found_func.funcName, names_start, moduleSize - (names_start - (const char*)modulePtr));
100 if (!found_ptr) {
101 //name not found in the binary
102 //TODO: maybe it is imported by ordinal?
103 continue;
104 }
105
106 const ULONGLONG name_offset = (ULONGLONG)found_ptr - (ULONGLONG)modulePtr;
107#ifdef _DEBUG
108 //if it is not the first name from the list, inform about it:
109 if (funcname_itr != addr_to_func[searchedAddr].begin()) {
110 std::cout << ">[*][" << std::hex << searchedAddr << "] " << found_func.toString() << std::endl;
111 }
112 std::cout <<"[+] Found the name at: " << std::hex << name_offset << std::endl;
113#endif
114 PIMAGE_IMPORT_BY_NAME imp_field = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(name_offset - sizeof(WORD)); // substract the size of Hint
115 //TODO: validate more...
116 memcpy(thunk_ptr, &imp_field, sizeof(FIELD_T));
117#ifdef _DEBUG
118 std::cout << "[+] Wrote found to offset: " << std::hex << call_via_ptr << std::endl;
119#endif
120 is_name_saved = true;
121 break;
122 }
123 //name not found or could not be saved - fill the ordinal instead:
124 if (!is_name_saved && lastOrdinal != 0) {
125#ifdef _DEBUG
126 std::cout << "[+] Filling ordinal: " << lastOrdinal << std::endl;
127#endif
128 FIELD_T ord_thunk = lastOrdinal | ordinal_flag;
129 memcpy(thunk_ptr, &ord_thunk, sizeof(FIELD_T));
130 is_name_saved = true;
131 }
132 return is_name_saved;
133}
134
135template <typename FIELD_T, typename IMAGE_THUNK_DATA_T>
136bool ImportsUneraser::writeFoundFunction(IMAGE_THUNK_DATA_T* desc, const FIELD_T ordinal_flag, const ExportedFunc &foundFunc)
137{
138 if (foundFunc.isByOrdinal) {
139 FIELD_T ordinal = foundFunc.funcOrdinal | ordinal_flag;
140 FIELD_T* by_ord = (FIELD_T*) desc;
141 *by_ord = ordinal;
142#ifdef _DEBUG
143 std::cout << "[+] Saved ordinal" << std::endl;
144#endif
145 return true;
146 }
147
148 PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME) ((ULONGLONG) modulePtr + desc->u1.AddressOfData);
149
150 LPSTR func_name_ptr = reinterpret_cast<LPSTR>(by_name->Name);
151 std::string found_name = foundFunc.funcName;
152 bool is_nameptr_valid = validate_ptr(modulePtr, moduleSize, func_name_ptr, found_name.length());
153 // try to save the found name under the pointer:
154 if (is_nameptr_valid) {
155 by_name->Hint = MASK_TO_WORD(foundFunc.funcOrdinal);
156 memcpy(func_name_ptr, found_name.c_str(), found_name.length() + 1); // with the ending '\0'
157#ifdef _DEBUG
158 std::cout << "[+] Saved name" << std::endl;
159#endif
160 return true;
161 }
162 return false;
163}
164
165template <typename FIELD_T, typename IMAGE_THUNK_DATA_T>
167 IN OUT IMAGE_IMPORT_DESCRIPTOR* lib_desc,
168 IN const FIELD_T ordinal_flag,
169 IN std::map<ULONGLONG, std::set<ExportedFunc>> &addr_to_func,
170 OUT OPTIONAL ImpsNotCovered* notCovered
171)
172{
173 if (lib_desc == NULL) return false;
174
175 FIELD_T call_via = lib_desc->FirstThunk;
176 if (call_via == NULL) return false;
177
178 size_t processed_imps = 0;
179 size_t recovered_imps = 0;
180
181 FIELD_T thunk_addr = lib_desc->OriginalFirstThunk;
182 if (thunk_addr == NULL) {
183 thunk_addr = call_via;
184 }
185
186 BYTE* call_via_ptr = (BYTE*)((ULONGLONG)modulePtr + call_via);
187 BYTE* thunk_ptr = (BYTE*)((ULONGLONG)modulePtr + thunk_addr);
188 for (;
189 call_via_ptr != NULL && thunk_ptr != NULL;
190 call_via_ptr += sizeof(FIELD_T), thunk_ptr += sizeof(FIELD_T)
191 )
192 {
193 FIELD_T *thunk_val = (FIELD_T*)thunk_ptr;
194 FIELD_T *call_via_val = (FIELD_T*)call_via_ptr;
195 if (*call_via_val == 0) {
196 //nothing to fill, probably the last record
197 break;
198 }
199 IMAGE_THUNK_DATA_T* desc = (IMAGE_THUNK_DATA_T*)thunk_ptr;
200 if (desc->u1.Function == NULL) {
201 break;
202 }
203 ULONGLONG searchedAddr = ULONGLONG(*call_via_val);
204 std::map<ULONGLONG,std::set<ExportedFunc>>::const_iterator found_itr = addr_to_func.find(searchedAddr);
205 if (found_itr == addr_to_func.end() || found_itr->second.size() == 0) {
206 //not found, move on
207 if (notCovered) {
208 const DWORD thunkRVA = MASK_TO_DWORD((ULONG_PTR)call_via_ptr - (ULONG_PTR)modulePtr);
209 notCovered->insert(thunkRVA, searchedAddr);
210 }
211 continue;
212 }
213 std::set<ExportedFunc>::const_iterator funcname_itr = found_itr->second.begin();
214 const peconv::ExportedFunc &foundFunc = *funcname_itr;
215
216#ifdef _DEBUG
217 std::cout << "[*][" << std::hex << searchedAddr << "] " << funcname_itr->toString() << std::endl;
218#endif
219 bool is_name_saved = writeFoundFunction<FIELD_T, IMAGE_THUNK_DATA_T>(desc, ordinal_flag, *funcname_itr);
220 if (!is_name_saved) {
221 is_name_saved = findNameInBinaryAndFill<FIELD_T>(lib_desc, call_via_ptr, thunk_ptr, ordinal_flag, addr_to_func);
222 }
223 processed_imps++;
224 if (is_name_saved) recovered_imps++;
225 }
226
227 return (recovered_imps == processed_imps);
228}
229
230bool ImportsUneraser::uneraseDllImports(IN OUT IMAGE_IMPORT_DESCRIPTOR* lib_desc, IN ImportedDllCoverage &dllCoverage, OUT OPTIONAL ImpsNotCovered* notCovered)
231{
232 //everything mapped, now recover it:
233 bool is_filled = false;
234 if (!is64) {
235 is_filled = fillImportNames<DWORD, IMAGE_THUNK_DATA32>(lib_desc, IMAGE_ORDINAL_FLAG32, dllCoverage.addrToFunc, notCovered);
236 } else {
237 is_filled = fillImportNames<ULONGLONG, IMAGE_THUNK_DATA64>(lib_desc, IMAGE_ORDINAL_FLAG64, dllCoverage.addrToFunc, notCovered);
238 }
239 if (!is_filled) {
240 std::cerr << "[-] Could not fill some import names!" << std::endl;
241 return false;
242 }
243 return is_filled;
244}
#define MASK_TO_WORD(val)
Definition buffer_util.h:13
#define MASK_TO_DWORD(val)
Definition buffer_util.h:12
std::string toString() const
bool writeFoundDllName(IMAGE_IMPORT_DESCRIPTOR *lib_desc, const std::string &dll_name)
bool fillImportNames(IN OUT IMAGE_IMPORT_DESCRIPTOR *lib_desc, IN const FIELD_T ordinal_flag, IN std::map< ULONGLONG, std::set< ExportedFunc > > &addr_to_func, OUT OPTIONAL ImpsNotCovered *not_covered)
bool uneraseDllName(IMAGE_IMPORT_DESCRIPTOR *lib_desc, const std::string &dll_name)
bool writeFoundFunction(IMAGE_THUNK_DATA_T *desc, const FIELD_T ordinal_flag, const ExportedFunc &foundFunc)
bool uneraseDllImports(IN OUT IMAGE_IMPORT_DESCRIPTOR *lib_desc, IN ImportedDllCoverage &dllCoverage, OUT OPTIONAL ImpsNotCovered *not_covered)
bool findNameInBinaryAndFill(IMAGE_IMPORT_DESCRIPTOR *lib_desc, LPVOID call_via_ptr, LPVOID thunk_ptr, const FIELD_T ordinal_flag, std::map< ULONGLONG, std::set< ExportedFunc > > &addr_to_func)
#define MIN_DLL_LEN
Definition fix_imports.h:21
LPVOID search_name(std::string name, const char *modulePtr, size_t moduleSize)
A definition of ImportsUneraser class - for recovery of a partialy erased Import Table.
bool validate_ptr(IN const void *buffer_bgn, IN size_t buffer_size, IN const void *field_bgn, IN size_t field_size)
PBYTE find_ending_cave(BYTE *module_ptr, size_t module_size, const DWORD cave_size, const DWORD cave_charact=IMAGE_SCN_MEM_READ)
Definition caves.cpp:11
IMAGE_DATA_DIRECTORY * get_directory_entry(IN const BYTE *pe_buffer, IN DWORD dir_id, IN bool allow_empty=false)