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