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