PE-sieve
Scans all running processes. Recognizes and dumps a variety of potentially malicious implants (replaced/implanted PEs, shellcodes, hooks, in-memory patches).
Loading...
Searching...
No Matches
module_data.cpp
Go to the documentation of this file.
1#include "module_data.h"
2
7#include "artefact_scanner.h"
8
9#include <psapi.h>
10#pragma comment(lib,"psapi.lib")
11
12using namespace pesieve::util;
13
15//---
17{
19 if (my_name.length() == 0 || my_name.length() > MAX_PATH) {
20 //invalid length
21 return false;
22 }
23 memcpy(this->szModName, my_name.c_str(), my_name.length());
24
25 // autoswitch the path to Wow64 mode if needed:
27 return true;
28}
29
31{
32 //disable FS redirection by default
33 bool is_ok = _loadOriginal(true);
34 if (!is_ok) {
35 //if loading with FS redirection has failed, try without
36 is_ok = _loadOriginal(false);
37 }
38 return is_ok;
39}
40
41bool pesieve::ModuleData::_loadOriginal(bool disableFSredir)
42{
43 if (strlen(this->szModName) == 0) {
44 loadModuleName();
45 }
46 //just in case if something was loaded before...
47 peconv::free_pe_buffer(original_module, original_size);
48
49 BOOL isRedirDisabled = FALSE;
50 PVOID old_val;
51 if (disableFSredir) {
52 isRedirDisabled = wow64_disable_fs_redirection(&old_val);
53 // try to load with FS redirection disabled
54 }
55 if (this->useCache) {
56 original_module = cache.loadCached(szModName, original_size);
57 }
58 else {
59 original_module = peconv::load_pe_module(szModName, original_size, false, false);
60 }
61
62 if (isRedirDisabled) {
64 }
65 if (!original_module) {
66 return false;
67 }
68 this->is_dot_net = isDotNetManagedCode();
69 return true;
70}
71
72
73bool pesieve::ModuleData::loadRelocatedFields(std::set<DWORD>& fields_rvas)
74{
75 if (!original_module || !original_size) {
76 return false;
77 }
78 //---
79 class CollectRelocField : public peconv::RelocBlockCallback
80 {
81 public:
82 CollectRelocField(ModuleData &_mod, std::set<DWORD>& _fields)
83 : RelocBlockCallback(_mod.is64bit()), mod(_mod), fields(_fields)
84 {
85 }
86
87 virtual bool processRelocField(ULONG_PTR relocField)
88 {
89 DWORD reloc_rva = mod.vaToRva(relocField, (ULONG_PTR)mod.original_module);
90 fields.insert(reloc_rva);
91 return true;
92 }
93
94 std::set<DWORD> &fields;
95 ModuleData &mod;
96 };
97 //---
98 if (!peconv::has_valid_relocation_table(original_module, original_size)) {
99 // No reloc table
100 return false;
101 }
102 CollectRelocField collector(*this, fields_rvas);
103 if (!peconv::process_relocation_table(original_module, original_size, &collector)) {
104 // Could not collect relocations
105 return false;
106 }
107 if (fields_rvas.size()) {
108 return true;
109 }
110 return false;
111}
112
113bool pesieve::ModuleData::loadImportThunks(std::set<DWORD>& thunk_rvas)
114{
115 if (!original_module || !original_size) {
116 return false;
117 }
118 if (!peconv::has_valid_import_table(original_module, original_size)) {
119 // No import table
120 return false;
121 }
122 if (!peconv::collect_thunks(original_module, original_size, thunk_rvas)) {
123 // Could not collect thunks
124 return false;
125 }
126 if (thunk_rvas.size()) {
127 return true;
128 }
129 return false;
130}
131
132bool pesieve::ModuleData::loadImportsList(peconv::ImportsCollection &collection)
133{
134 if (!original_module || !original_size) {
135 return false;
136 }
137 if (!peconv::has_valid_import_table(original_module, original_size)) {
138 // No import table
139 return false;
140 }
141 if (!peconv::collect_imports(original_module, original_size, collection)) {
142 // Could not collect imports
143 return false;
144 }
145 return true;
146}
147
149{
150 if (!original_module) return false;
151
152 ULONGLONG original_base = peconv::get_image_base(original_module);
153 if (original_base == new_base) {
154 return true; // already relocated
155 }
156 if (peconv::has_relocations(original_module)
157 && !peconv::relocate_module(original_module, original_size, new_base, original_base))
158 {
159#ifdef _DEBUG
160 std::cerr << "[!] Relocating module failed!" << std::endl;
161#endif
162 return false;
163 }
164 peconv::update_image_base(original_module, new_base);
165 return true;
166}
167
168
170{
171 std::string mapped_name = pesieve::RemoteModuleData::getMappedName(processHandle, this->moduleHandle);
172 std::string module_name = this->szModName;
173 bool is_same = (to_lowercase(mapped_name) == to_lowercase(module_name));
174
175 size_t mod_name_len = module_name.length();
176 if (!is_same && mod_name_len > 0) {
177 //check Wow64
178 char path_copy[MAX_PATH] = { 0 };
179 memcpy(path_copy, this->szModName, mod_name_len);
180 convert_to_wow64_path(path_copy);
181 is_same = (to_lowercase(mapped_name) == to_lowercase(path_copy));
182 if (is_same) {
183 this->switchToWow64Path();
184 return true;
185 }
186 }
187 return false;
188}
189
191{
192 BOOL isWow64 = FALSE;
193 if (!is_process_wow64(this->processHandle, &isWow64)) {
194 //failed to retrieve the info...
195 return false;
196 }
197 if (isWow64) {
198 if (pesieve::util::convert_to_wow64_path(szModName)) return true;
199 }
200 return false;
201}
202
204{
205 const std::string path = RemoteModuleData::getMappedName(this->processHandle, this->moduleHandle);
206 const size_t len = path.length();
207 if (len >= MAX_PATH) {
208 return false;
209 }
210 ::memcpy(szModName, path.c_str(), len);
211 szModName[len] = 0;
212 return true;
213}
214
216{
217 if (!switchToWow64Path()) return false;
218
219 //reload it and check again...
220 peconv::free_pe_buffer(original_module, original_size);
221 if (this->useCache) {
222 original_module = cache.loadCached(szModName, original_size);
223 }
224 else {
225 original_module = peconv::load_pe_module(szModName, original_size, false, false);
226 }
227 if (!original_module) {
228 std::cout << "[-] Failed to reload: " << szModName << "\n";
229 return false;
230 }
231 std::cout << "[+] Reloaded: " << szModName << "\n";
232 return true;
233}
234
236{
237 //has a directory entry for .NET header
238 IMAGE_DATA_DIRECTORY* dotNetDir = peconv::get_directory_entry(this->original_module, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
239 if (dotNetDir == nullptr) {
240 //does not have .NET directory
241 return false;
242 }
243
244 if (!peconv::get_dotnet_hdr(this->original_module, this->original_size, dotNetDir)){
245 return false;
246 }
247#ifdef _DEBUG
248 std::cout << "This is a .NET module" << std::endl;
249#endif
250 return true;
251}
252
253//----
254
255std::string pesieve::RemoteModuleData::getModuleName(HANDLE processHandle, HMODULE modBaseAddr)
256{
257 char filename[MAX_PATH] = { 0 };
258 if (!GetModuleFileNameExA(processHandle, modBaseAddr, filename, MAX_PATH)) {
259 return "";
260 }
261 std::string basic_filename = pesieve::util::convert_to_win32_path(filename);
262 std::string expanded = pesieve::util::expand_path(basic_filename);
263 if (expanded.length() == 0) {
264 return filename;
265 }
266 return expanded;
267}
268
269std::string pesieve::RemoteModuleData::getMappedName(HANDLE processHandle, LPVOID modBaseAddr)
270{
271 char filename[MAX_PATH] = { 0 };
272 if (GetMappedFileNameA(processHandle, modBaseAddr, filename, MAX_PATH) == 0) {
273 return "";
274 }
275 std::string expanded = pesieve::util::expand_path(filename);
276 if (expanded.length() == 0) {
277 return filename;
278 }
279 return expanded;
280}
281
282bool pesieve::RemoteModuleData::loadImportsList(peconv::ImportsCollection& collection)
283{
284 if (!isFullImageLoaded()) {
285 return false;
286 }
287 if (!peconv::has_valid_import_table(imgBuffer, imgBufferSize)) {
288 // No import table
289 return false;
290 }
291 if (!peconv::collect_imports(imgBuffer, imgBufferSize, collection)) {
292 // Could not collect imports
293 return false;
294 }
295 return true;
296}
297
299{
300 this->isHdrReady = false;
301 if (!loadHeader()) {
302 return false;
303 }
304 this->isHdrReady = true;
305 return true;
306}
307
309{
310 if (this->isFullImageLoaded()) {
311 return true;
312 }
313 this->imgBuffer = peconv::alloc_pe_buffer(mod_size, PAGE_READWRITE);
314 this->imgBufferSize = peconv::read_remote_pe(this->processHandle, (PBYTE)this->modBaseAddr, mod_size, this->imgBuffer, mod_size);
315 if (this->imgBufferSize == mod_size) {
316 return true;
317 }
318 this->freeFullImage();
319 return false;
320}
321
323{
324 if (this->isFullImageLoaded()) {
325 return true;
326 }
327 size_t mod_size = this->getHdrImageSize();
328 if (_loadFullImage(mod_size)) {
329 return true;
330 }
331 //try again with calculated size:
332 mod_size = calcImgSize();
333 return _loadFullImage(mod_size);
334}
335
337{
338 if (!peconv::read_remote_pe_header(this->processHandle, (PBYTE)this->modBaseAddr, this->headerBuffer, peconv::MAX_HEADER_SIZE, this->isReflection)) {
339 return false;
340 }
341 return true;
342}
343
344ULONGLONG pesieve::RemoteModuleData::getRemoteSectionVa(const size_t section_num)
345{
346 if (!this->isInitialized()) return NULL;
347
348 PIMAGE_SECTION_HEADER section_hdr = peconv::get_section_hdr(headerBuffer, peconv::MAX_HEADER_SIZE, section_num);
349 if ((section_hdr == NULL) || section_hdr->SizeOfRawData == 0) {
350 return NULL;
351 }
352 return (ULONGLONG) modBaseAddr + section_hdr->VirtualAddress;
353}
354
355bool pesieve::RemoteModuleData::isSectionEntry(const size_t section_number)
356{
357 if (!this->isInitialized()) {
358 return false;
359 }
360 const DWORD ep_va = peconv::get_entry_point_rva(this->headerBuffer);
361 if (ep_va == 0) {
362 return false;
363 }
364 PIMAGE_SECTION_HEADER sec_hdr = peconv::get_section_hdr(this->headerBuffer, peconv::MAX_HEADER_SIZE, section_number);
365 if (!sec_hdr) {
366 return false;
367 }
368 if (ep_va >= sec_hdr->VirtualAddress
369 && ep_va < (sec_hdr->VirtualAddress + sec_hdr->Misc.VirtualSize))
370 {
371 return true;
372 }
373 return false;
374}
375
376bool pesieve::RemoteModuleData::isSectionExecutable(const size_t section_number, bool allow_data, bool allow_inaccessible)
377{
378 //for special cases when the section is not set executable in headers, but in reality is executable...
379 //get the section header from the module:
380 ULONGLONG start_va = getRemoteSectionVa(section_number);
381 if (start_va == NULL) {
382 return false;
383 }
384 MEMORY_BASIC_INFORMATION page_info = { 0 };
385
386 SIZE_T out = VirtualQueryEx(processHandle, (LPCVOID) start_va, &page_info, sizeof(page_info));
387 if (out != sizeof(page_info)) {
388#ifdef _DEBUG
389 std::cerr << "Cannot retrieve remote section info" << std::endl;
390#endif
391 return false;
392 }
393#ifdef _DEBUG
394 std::cout << std::hex << "Sec: " << section_number << " VA: " << start_va << " t: " << page_info.Type << " p: " << page_info.Protect << std::endl;
395#endif
396
397 if (pesieve::util::is_executable(page_info.Type, page_info.Protect)) {
398 //std::cout << std::hex << "p1 Sec: " << section_number << " VA: " << start_va << " t: " << page_info.Type << " p: " << page_info.Protect << std::endl;
399 return true;
400 }
401 if (allow_data) {
402 if (pesieve::util::is_readable(page_info.Type, page_info.Protect)) {
403 //std::cout << std::hex << "p1 Sec: " << section_number << " VA: " << start_va << " t: " << page_info.Type << " p: " << page_info.Protect << std::endl;
404 return true;
405 }
406 }
407 if (allow_inaccessible) {
408 if (pesieve::util::is_normal_inaccessible(page_info.State, page_info.Type, page_info.Protect)) {
409 //std::cout << "[" << section_number << "] Inaccessible section found!\n";
410 return true;
411 }
412 }
413 return false;
414}
415
416bool pesieve::RemoteModuleData::hasExecutableSection(bool allow_data, bool allow_inaccessible)
417{
418 size_t sec_count = peconv::get_sections_count(this->headerBuffer, peconv::MAX_HEADER_SIZE);
419 for (size_t i = 0; i < sec_count ; i++) {
420 if (isSectionExecutable(i, allow_data, allow_inaccessible)) {
421 return true;
422 }
423 }
424 return false;
425}
426
427//calculate image size basing on the sizes of sections
429{
430 if (!isHdrReady) return 0;
431
432 return ArtefactScanner::calcImgSize(this->processHandle, this->modBaseAddr, this->headerBuffer, peconv::MAX_HEADER_SIZE);
433}
static size_t calcImgSize(HANDLE processHandle, HMODULE modBaseAddr, BYTE *headerBuffer, size_t headerBufferSize, IMAGE_SECTION_HEADER *hdr_ptr=NULL)
Loads a module from the disk, corresponding to the module in the scanned process' memory.
Definition module_data.h:15
bool relocateToBase(ULONGLONG new_base)
bool loadRelocatedFields(std::set< DWORD > &fields_rvas)
bool _loadOriginal(bool disableFSredir)
char szModName[MAX_PATH]
bool loadImportsList(peconv::ImportsCollection &collection)
bool loadImportThunks(std::set< DWORD > &fields_rvas)
BYTE * loadCached(LPSTR szModName, size_t &original_size)
bool _loadFullImage(size_t v_size)
bool loadImportsList(peconv::ImportsCollection &collection)
static std::string getModuleName(HANDLE _processHandle, HMODULE _modBaseAddr)
bool hasExecutableSection(bool allow_data, bool allow_inaccessible)
bool isSectionExecutable(const size_t section_number, bool allow_data, bool allow_inaccessible)
static std::string getMappedName(HANDLE _processHandle, LPVOID _modBaseAddr)
ULONGLONG getRemoteSectionVa(const size_t section_num)
bool isSectionEntry(const size_t section_number)
pesieve::ModulesCache cache
std::string expand_path(const std::string &path)
std::string to_lowercase(std::string)
BOOL wow64_disable_fs_redirection(OUT PVOID *OldValue)
BOOL is_process_wow64(IN HANDLE processHandle, OUT BOOL *isProcWow64)
bool is_readable(DWORD mapping_type, DWORD protection)
std::string convert_to_win32_path(const std::string &path)
bool is_normal_inaccessible(DWORD state, DWORD mapping_type, DWORD protection)
BOOL(CALLBACK *_MiniDumpWriteDump)(HANDLE hProcess
bool is_executable(DWORD mapping_type, DWORD protection)
DWORD(__stdcall *_PssCaptureSnapshot)(HANDLE ProcessHandle
bool convert_to_wow64_path(char *szModName)
BOOL wow64_revert_fs_redirection(IN PVOID OldValue)
int MAX_PATH
Definition pesieve.py:10