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
artefact_scanner.cpp
Go to the documentation of this file.
1#include "artefact_scanner.h"
2
5
6#include <peconv.h>
7
8using namespace pesieve;
9using namespace pesieve::util;
10
11namespace pesieve {
12 namespace util {
13
14 size_t calc_offset(MemPageData &memPage, LPVOID field)
15 {
16 if (!field) return INVALID_OFFSET;
17
18 const BYTE* loadedData = memPage.getLoadedData();
19 const size_t loadedSize = memPage.getLoadedSize();
20 if (!peconv::validate_ptr(loadedData, loadedSize, field, sizeof(BYTE))) {
21 return INVALID_OFFSET;
22 }
23 return size_t((ULONG_PTR)field - (ULONG_PTR)loadedData);
24 }
25
26 size_t calc_sec_hdrs_offset(MemPageData &memPage, IMAGE_FILE_HEADER* nt_file_hdr)
27 {
28 size_t opt_hdr_size = nt_file_hdr->SizeOfOptionalHeader;
29 if (opt_hdr_size == 0) {
30 //try casual values
31 bool is64bit = (nt_file_hdr->Machine == IMAGE_FILE_MACHINE_AMD64) ? true : false;
32 opt_hdr_size = is64bit ? sizeof(IMAGE_OPTIONAL_HEADER64) : sizeof(IMAGE_OPTIONAL_HEADER32);
33 }
34 const size_t headers_size = opt_hdr_size + sizeof(IMAGE_FILE_HEADER);
35 size_t nt_offset = calc_offset(memPage, nt_file_hdr);
36 size_t sec_hdr_offset = headers_size + nt_offset;
37 return sec_hdr_offset;
38 }
39
40 size_t calc_nt_hdr_offset(MemPageData &memPage, IMAGE_SECTION_HEADER* first_sec, bool is64bit = true)
41 {
42 size_t sec_hdr_offset = calc_offset(memPage, first_sec);
43 if (sec_hdr_offset == INVALID_OFFSET) {
44 return INVALID_OFFSET;
45 }
46 size_t opt_hdr_size = is64bit ? sizeof(IMAGE_OPTIONAL_HEADER64) : sizeof(IMAGE_OPTIONAL_HEADER32);
47 const size_t headers_size = opt_hdr_size + sizeof(IMAGE_FILE_HEADER);
48 size_t nt_offset = sec_hdr_offset - headers_size;
49 return nt_offset;
50 }
51
52 bool validate_hdrs_alignment(MemPageData &memPage, IMAGE_FILE_HEADER *nt_file_hdr, IMAGE_SECTION_HEADER* _sec_hdr)
53 {
54 if (!_sec_hdr) return false;
55 if (!nt_file_hdr) return false;
56
57 size_t sec_offset_hdrs = calc_sec_hdrs_offset(memPage, nt_file_hdr);
58 size_t sec_offset = calc_offset(memPage, _sec_hdr);
59 if (sec_offset_hdrs != sec_offset) {
60 std::cout << std::hex << "sec_offset_hdrs: " << sec_offset_hdrs << " vs: " << sec_offset << "\n";
61
62 return false;
63 }
64 return true;
65 }
66
67 size_t count_section_hdrs(BYTE *loadedData, size_t loadedSize, IMAGE_SECTION_HEADER *hdr_ptr)
68 {
69 if (!loadedData || !hdr_ptr) {
70 return 0;
71 }
72 size_t counter = 0;
73 IMAGE_SECTION_HEADER* curr_sec = hdr_ptr;
74 do {
75 if (!is_valid_section(loadedData, loadedSize, (BYTE*)curr_sec, IMAGE_SCN_MEM_READ)) {
76 break;
77 }
78 curr_sec++;
79 counter++;
80 } while (true);
81
82 return counter;
83 }
84
85 IMAGE_SECTION_HEADER* get_first_section(BYTE *loadedData, size_t loadedSize, IMAGE_SECTION_HEADER *hdr_ptr)
86 {
87 IMAGE_SECTION_HEADER* prev_sec = hdr_ptr;
88 do {
89 if (!is_valid_section(loadedData, loadedSize, (BYTE*)prev_sec, IMAGE_SCN_MEM_READ)) {
90 break;
91 }
92 hdr_ptr = prev_sec;
93 prev_sec--;
94 } while (true);
95
96 return hdr_ptr;
97 }
98
99 }; //namespace util
100}; // namespace pesieve
101
102bool pesieve::is_valid_section(BYTE *loadedData, size_t loadedSize, BYTE *hdr_ptr, DWORD charact)
103{
104 PIMAGE_SECTION_HEADER hdr_candidate = (PIMAGE_SECTION_HEADER) hdr_ptr;
105 if (!peconv::validate_ptr(loadedData, loadedSize, hdr_candidate, sizeof(IMAGE_SECTION_HEADER))) {
106 // probably buffer finished
107 return false;
108 }
109 if (hdr_candidate->PointerToRelocations != 0
110 || hdr_candidate->NumberOfRelocations != 0
111 || hdr_candidate->PointerToLinenumbers != 0)
112 {
113 //values that should be NULL are not
114 return false;
115 }
116 if (charact != 0 && (hdr_candidate->Characteristics & charact) == 0) {
117 // required characteristics not found
119 return false;
120 }
122 return true;
123}
124
125ULONGLONG pesieve::ArtefactScanner::_findMZoffset(MemPageData &memPage, LPVOID sec_hdr)
126{
127 size_t hdrs_offset = calc_offset(memPage, sec_hdr);
128 if (hdrs_offset == INVALID_OFFSET) {
129 return INVALID_OFFSET;
130 }
131
132 const BYTE mz_sig[] = "MZ\x90";
133
134 BYTE *min_search = memPage.getLoadedData();
135 BYTE *start_ptr = min_search + hdrs_offset - sizeof(mz_sig);
136 size_t space = PAGE_SIZE;
137 //std::cout << "Searching the MZ header starting from: " << std::hex << hdrs_offset << "\n";
138 for (BYTE *search_ptr = start_ptr; search_ptr >= min_search && space > 0; search_ptr--, space--) {
139 if ((search_ptr[0] == mz_sig[0] && search_ptr[1] == mz_sig[1] )
140 && (search_ptr[2] == mz_sig[2] || search_ptr[2] == 0))
141 {
142 //std::cout << "MZ header found!\n";
143 return calc_offset(memPage, search_ptr);
144 }
145 }
146 //std::cout << "MZ header not found :(\n";
147 return INVALID_OFFSET;
148}
149
150ULONGLONG pesieve::ArtefactScanner::calcPeBase(MemPageData &memPage, LPVOID sec_hdr)
151{
152 ULONGLONG found_mz = _findMZoffset(memPage, sec_hdr);
153 if (found_mz != INVALID_OFFSET) {
154 return memPage.region_start + found_mz;
155 }
156
157 size_t hdrs_offset = calc_offset(memPage, sec_hdr);
158 if (hdrs_offset == INVALID_OFFSET) {
159 //std::cout << "Invalid sec_hdr_offset\n";
160 return 0;
161 }
162 //search by stub patterns
163 size_t search_start = (hdrs_offset > PAGE_SIZE) ? hdrs_offset - PAGE_SIZE: 0;
164 IMAGE_DOS_HEADER *dos_hdr = findDosHdrByPatterns(memPage, search_start, hdrs_offset);
165 size_t dos_offset = calc_offset(memPage, dos_hdr);
166 if (dos_offset != INVALID_OFFSET) {
167 return memPage.region_start + dos_offset;
168 }
169
170 //WARNING: this will be inacurate in cases if the PE is not aligned to the beginning of the page
171 size_t full_pages = hdrs_offset / PAGE_SIZE;
172 //std::cout << "Full pages: " << std::dec << full_pages << std::endl;
173 return memPage.region_start + (full_pages * PAGE_SIZE);
174}
175
176size_t pesieve::ArtefactScanner::calcImgSize(HANDLE processHandle, HMODULE modBaseAddr, BYTE* headerBuffer, size_t headerBufferSize, IMAGE_SECTION_HEADER *hdr_ptr)
177{
178 if (!hdr_ptr) {
179 hdr_ptr = peconv::get_section_hdr(headerBuffer, headerBufferSize, 0);
180 if (!hdr_ptr) return peconv::fetch_region_size(processHandle, (PBYTE)modBaseAddr);
181 }
182
183 DWORD max_addr = 0;
184
185 const ULONGLONG main_base = peconv::fetch_alloc_base(processHandle, (PBYTE)modBaseAddr);
186 for (IMAGE_SECTION_HEADER* curr_sec = hdr_ptr; ; curr_sec++)
187 {
188 //we don't know the number of sections, so we should validate each one
189 if (!is_valid_section(headerBuffer, headerBufferSize, (BYTE*)curr_sec, 0)) {
190 break;
191 }
192 if (curr_sec->Misc.VirtualSize == 0 || curr_sec->VirtualAddress == 0) {
193 continue; //skip empty sections
194 }
195
196 const DWORD sec_rva = curr_sec->VirtualAddress;
197
198 MEMORY_BASIC_INFORMATION page_info = { 0 };
199 if (!peconv::fetch_region_info(processHandle, (PBYTE)((ULONG_PTR)modBaseAddr + sec_rva), page_info)) {
200 break;
201 }
202 if ((ULONG_PTR)page_info.AllocationBase != main_base) {
203 //it can happen if the PE is in a RAW format instead of Virtual
204#ifdef _DEBUG
205 std::cout << "[!] Mismatch: region_base : " << std::hex << page_info.AllocationBase << " while main base: " << main_base << "\n";
206#endif
207 break; // out of scope
208 }
209 if (page_info.Type == 0 || page_info.Protect == 0) {
210 break; //invalid type, skip it
211 }
212 if ((page_info.State & MEM_COMMIT) == 0) {
213 continue; //skip non-commited pages
214 }
215 if (sec_rva > max_addr) {
216 max_addr = sec_rva;
217 }
218 }
219
220 size_t last_sec_size = peconv::fetch_region_size(processHandle, (PBYTE)((ULONG_PTR)modBaseAddr + max_addr));
221 size_t total_size = max_addr + last_sec_size;
222#ifdef _DEBUG
223 std::cout << "Image: " << std::hex << (ULONGLONG)modBaseAddr << " Size:" << std::hex << total_size << " max_addr: " << max_addr << std::endl;
224#endif
225 return total_size;
226}
227
228//calculate image size basing on the sizes of sections
229size_t pesieve::ArtefactScanner::calcImageSize(MemPageData &memPage, IMAGE_SECTION_HEADER *hdr_ptr, ULONGLONG pe_image_base)
230{
231 return ArtefactScanner::calcImgSize(this->processHandle, (HMODULE)pe_image_base, memPage.getLoadedData(), memPage.getLoadedSize(), hdr_ptr);
232}
233
234IMAGE_DOS_HEADER* pesieve::ArtefactScanner::findDosHdrByPatterns(MemPageData &memPage, const size_t start_offset, size_t hdrs_offset)
235{
236 BYTE* data = memPage.getLoadedData();
237 if (!data) return nullptr;
238
239 BYTE *search_ptr = data + start_offset;
240 BYTE *max_search = search_ptr + hdrs_offset;
241
242 size_t max_search_size = max_search - search_ptr;
243 if (!memPage.validatePtr(search_ptr, max_search_size)) {
244 return nullptr;
245 }
246 IMAGE_DOS_HEADER* dos_hdr = _findDosHdrByPatterns(search_ptr, max_search_size);
247 const bool is_dos_valid = memPage.validatePtr(dos_hdr, sizeof(IMAGE_DOS_HEADER));
248 if (is_dos_valid) {
249 return dos_hdr;
250 }
251 return nullptr;
252}
253
254IMAGE_DOS_HEADER* pesieve::ArtefactScanner::_findDosHdrByPatterns(BYTE *search_ptr, const size_t max_search_size)
255{
256 if (!memPage.load()) {
257 return nullptr;
258 }
259
260 const size_t patterns_count = 2;
261 const size_t pattern_size = 14;
262 BYTE stub_patterns[patterns_count][pattern_size] = { // common beginnnig of DOS stubs
263 {
264 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4,
265 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C,
266 0xCD, 0x21
267 },
268 {
269 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4,
270 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C,
271 0xCD, 0x21
272 }
273 };
274
275 const size_t dos_hdr_size = sizeof(IMAGE_DOS_HEADER);
276
277 BYTE *stub_ptr = nullptr;
278 IMAGE_DOS_HEADER *dos_ptr = nullptr;
279 for (size_t i = 0; i < patterns_count; i++) {
280 BYTE *pattern = stub_patterns[i];
281 stub_ptr = find_pattern(search_ptr, max_search_size, pattern, pattern_size);
282 if (!stub_ptr) {
283 continue;
284 }
285 size_t offset_to_bgn = sizeof(IMAGE_DOS_HEADER);
286 if ((ULONG_PTR)stub_ptr < offset_to_bgn) {
287 return nullptr;
288 }
289 dos_ptr = (IMAGE_DOS_HEADER*)((ULONG_PTR)stub_ptr - offset_to_bgn);
290 if (!peconv::validate_ptr(search_ptr, max_search_size, dos_ptr, sizeof(IMAGE_DOS_HEADER))) {
291 continue;
292 }
293 return dos_ptr;
294 }
295 return nullptr;
296}
297
298bool pesieve::ArtefactScanner::_validateSecRegions(MemPageData &memPage, LPVOID sec_hdr, size_t sec_count, ULONGLONG pe_image_base, bool is_virtual)
299{
300 if (!sec_hdr || !sec_count) {
301 return false;
302 }
303 MEMORY_BASIC_INFORMATION module_start_info = { 0 };
304 if (!peconv::fetch_region_info(processHandle, (BYTE*)pe_image_base, module_start_info)) {
305 return false;
306 }
307 IMAGE_SECTION_HEADER* curr_sec = (IMAGE_SECTION_HEADER*)sec_hdr;
308
309 for (size_t i = 0; i < sec_count; i++, curr_sec++) {
310 if (curr_sec->VirtualAddress == 0) continue;
311
312 ULONG sec_start = is_virtual ? curr_sec->VirtualAddress : curr_sec->PointerToRawData;
313 ULONGLONG last_sec_addr = pe_image_base + sec_start;
314
315 MEMORY_BASIC_INFORMATION page_info = { 0 };
316 if (!peconv::fetch_region_info(processHandle, (BYTE*)last_sec_addr, page_info)) {
317#ifdef _DEBUG
318 std::cout << std::hex << last_sec_addr << " couldn't fetch module info" << std::endl;
319#endif
320 return false;
321 }
322 if (page_info.AllocationBase != module_start_info.AllocationBase) {
323#ifdef _DEBUG
324 std::cout << "[-] SecBase mismatch: ";
325 if (curr_sec->Name) {
326 std::cout << curr_sec->Name;
327 }
328 std::cout << std::hex << i << " section: " << last_sec_addr << " alloc base: " << page_info.AllocationBase << " with module base: " << module_start_info.AllocationBase << std::endl;
329#endif
330 return false;
331 }
332 }
333 return true;
334}
335
336bool pesieve::ArtefactScanner::_validateSecRegions(MemPageData &memPage, LPVOID sec_hdr, size_t sec_count)
337{
338 if (!memPage.getLoadedData() || !sec_hdr) {
339 return 0;
340 }
341 ULONGLONG pe_image_base = this->calcPeBase(memPage, sec_hdr);
342 bool has_non_zero = false;
343
344 IMAGE_SECTION_HEADER* curr_sec = (IMAGE_SECTION_HEADER*)sec_hdr;
345 for (size_t i = 0; i < sec_count; i++, curr_sec++) {
346 if (curr_sec->VirtualAddress && curr_sec->Misc.VirtualSize) {
347 has_non_zero = true;
348 }
349 }
350 if (!has_non_zero) return false;
351
352 //validate Virtual Sections alignment
353 bool is_ok = _validateSecRegions(memPage, sec_hdr, sec_count, pe_image_base, true);
354 if (!is_ok) {
355 //maybe it is raw?
356 is_ok = _validateSecRegions(memPage, sec_hdr, sec_count, pe_image_base, false);
357#ifdef _DEBUG
358 if (!is_ok) {
359 std::cout << "[-] Raw failed!\n";
360 }
361 else {
362 std::cout << "[+] Raw OK!\n";
363 }
364#endif
365 }
366#ifdef _DEBUG
367 else {
368 std::cout << "[+] Virtual OK!\n";
369 }
370#endif
371 return is_ok;
372}
373
374BYTE* pesieve::ArtefactScanner::_findSecByPatterns(BYTE *search_ptr, const size_t max_search_size)
375{
376 if (!memPage.load()) {
377 return nullptr;
378 }
379 const DWORD charact = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
380 //find sections table
381 char sec_name[] = ".text";
382 BYTE *hdr_ptr = find_pattern(search_ptr, max_search_size, (BYTE*)sec_name, strlen(sec_name));
383 if (hdr_ptr) {
384 // if the section was found by name, check if it has valid characteristics:
385 if (is_valid_section(search_ptr, max_search_size, hdr_ptr, charact)) {
386 return hdr_ptr;
387 }
388 hdr_ptr = nullptr;
389 }
390
391 // try another pattern
392 const size_t patterns_count = 2;
393 const size_t pattern_size = sizeof(DWORD) * 4;
394 BYTE charact_patterns[patterns_count][pattern_size] = { // common characteristics
395 {
396 0x00, 0x00, 0x00, 0x00,
397 0x00, 0x00, 0x00, 0x00,
398 0x00, 0x00, 0x00, 0x00,
399 0x20, 0x00, 0x00, 0x60
400 },
401 {
402 0x00, 0x00, 0x00, 0x00,
403 0x00, 0x00, 0x00, 0x00,
404 0x00, 0x00, 0x00, 0x00,
405 0x40, 0x00, 0x00, 0xC0
406 }
407 };
408
409 for (size_t i = 0; i < patterns_count; i++) {
410 BYTE *sec_ending = charact_patterns[i];
411 const size_t sec_ending_size = pattern_size;
412 hdr_ptr = find_pattern(search_ptr, max_search_size, sec_ending, sec_ending_size);
413 if (!hdr_ptr) {
414 continue;
415 }
416 size_t offset_to_bgn = sizeof(IMAGE_SECTION_HEADER) - sec_ending_size;
417 hdr_ptr -= offset_to_bgn;
418 if (!peconv::validate_ptr(search_ptr, max_search_size, hdr_ptr, sizeof(IMAGE_SECTION_HEADER))) {
419 continue;
420 }
421 if (is_valid_section(search_ptr, max_search_size, hdr_ptr, charact)) {
422 return hdr_ptr;
423 }
424 }
425 return nullptr;
426}
427
428IMAGE_SECTION_HEADER* pesieve::ArtefactScanner::findSecByPatterns(MemPageData &memPage, const size_t max_search_size, const size_t search_offset)
429{
430 BYTE *search_ptr = search_offset + memPage.getLoadedData();
431 if (!memPage.validatePtr(search_ptr, max_search_size)) {
432 return nullptr;
433 }
434 BYTE *hdr_ptr = _findSecByPatterns(search_ptr, max_search_size);
435 if (!hdr_ptr) {
436 return nullptr;
437 }
438 // is it really the first section?
439 IMAGE_SECTION_HEADER *first_sec = get_first_section(memPage.getLoadedData(), memPage.getLoadedSize(), (IMAGE_SECTION_HEADER*) hdr_ptr);
440 if (!first_sec) {
441 return nullptr;
442 }
443 size_t count = count_section_hdrs(memPage.getLoadedData(), memPage.getLoadedSize(), first_sec);
444 if (!_validateSecRegions(memPage, first_sec, count)) {
445#ifdef _DEBUG
446 const ULONGLONG diff = (ULONGLONG)first_sec - (ULONGLONG)memPage.getLoadedData();
447 std::cout << "[!] section header: " << std::hex << (ULONGLONG)memPage.region_start << " hdr at: " << diff << " : validation failed!\n";
448#endif
449 return nullptr;
450 }
451 return (IMAGE_SECTION_HEADER*)first_sec;
452}
453
454bool pesieve::is_valid_file_hdr(BYTE *loadedData, size_t loadedSize, BYTE *hdr_ptr, DWORD charact)
455{
456 IMAGE_FILE_HEADER* hdr_candidate = (IMAGE_FILE_HEADER*)hdr_ptr;
457 if (!peconv::validate_ptr(loadedData, loadedSize, hdr_candidate, sizeof(IMAGE_FILE_HEADER))) {
458 // probably buffer finished
459 return false;
460 }
461 if (hdr_candidate->NumberOfSections > 100) {
462 return false;
463 }
464 if (hdr_candidate->NumberOfSymbols != 0 || hdr_candidate->PointerToSymbolTable != 0) {
465 return false;
466 }
467 //sanity checks of machine and optional header size:
468 size_t opt_hdr_size = 0;
469 if (hdr_candidate->Machine == IMAGE_FILE_MACHINE_I386) {
470 opt_hdr_size = sizeof(IMAGE_OPTIONAL_HEADER32);
471 }
472 else if (hdr_candidate->Machine == IMAGE_FILE_MACHINE_AMD64) {
473 opt_hdr_size = sizeof(IMAGE_OPTIONAL_HEADER64);
474 }
475 else {
476 // wrong machine ID
477 return false;
478 }
479 if (hdr_candidate->SizeOfOptionalHeader > PAGE_SIZE) {
480 return false;
481 }
482 if (!peconv::validate_ptr(loadedData, loadedSize, hdr_candidate,
483 sizeof(IMAGE_FILE_HEADER) + opt_hdr_size))
484 {
485 return false;
486 }
487 if (hdr_candidate->SizeOfOptionalHeader == opt_hdr_size) {
488 return true;
489 }
490 //check characteristics:
491 if (charact != 0 && (hdr_candidate->Characteristics & charact) == 0) {
492 return false;
493 }
494 return true;
495}
496
497IMAGE_FILE_HEADER* pesieve::ArtefactScanner::findNtFileHdr(MemPageData &memPage, const size_t start_offset, size_t stop_offset)
498{
499 BYTE* const loadedData = memPage.getLoadedData();
500 size_t const loadedSize = memPage.getLoadedSize();
501 size_t max_iter = 0; //UNLIMITED
502
503 if (!loadedData) return nullptr;
504 //std::cout << "Searching NT header, starting_offset = " << std::hex << start_offset << "\n";
505 //normalize the stop_offset:
506 if (stop_offset == INVALID_OFFSET || stop_offset == 0) {
507 stop_offset = loadedSize;
508 max_iter = 1;
509 }
510 if (stop_offset > loadedSize) {
511 stop_offset = loadedSize;
512 }
513 //check the constraints:
514 if (start_offset == INVALID_OFFSET
515 || start_offset >= loadedSize || stop_offset <= start_offset)
516 {
517 return nullptr;
518 }
519
520 BYTE* search_ptr = loadedData + start_offset;
521 size_t search_size = loadedSize - start_offset;
522
523 typedef enum {
524 ARCH_32B = 0,
525 ARCH_64B = 1,
526 ARCHS_COUNT
527 } t_archs;
528
529 WORD archs[ARCHS_COUNT] = { 0 };
530 archs[ARCH_32B] = IMAGE_FILE_MACHINE_I386;
531 archs[ARCH_64B] = IMAGE_FILE_MACHINE_AMD64;
532
533 BYTE *arch_ptr = nullptr;
534 size_t my_arch = 0;
535 for (my_arch = ARCH_32B; my_arch < ARCHS_COUNT; my_arch++) {
536 arch_ptr = find_pattern(search_ptr, search_size, (BYTE*)&archs[my_arch], sizeof(WORD), max_iter);
537 if (arch_ptr) {
538 break;
539 }
540 }
541 if (!arch_ptr) {
542 //std::cout << "No architecture pattern found...\n";
543 return nullptr;
544 }
545 DWORD charact = IMAGE_FILE_EXECUTABLE_IMAGE;
546 if (my_arch == ARCH_32B) {
547 charact |= IMAGE_FILE_32BIT_MACHINE;
548 }
549 else {
550 charact |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
551 }
552 //std::cout << "Found NT header, validating...\n";
553 if (!is_valid_file_hdr(loadedData, loadedSize, arch_ptr, charact)) {
554 return nullptr;
555 }
556 return reinterpret_cast<IMAGE_FILE_HEADER*>(arch_ptr);
557}
558
559
560IMAGE_DOS_HEADER* pesieve::ArtefactScanner::findMzPeHeader(MemPageData &memPage, const size_t search_offset)
561{
562 if (!memPage.load()) {
563 return nullptr;
564 }
565 if (memPage.getLoadedSize() <= search_offset) {
566 return nullptr;
567 }
568 const size_t scan_size = memPage.getLoadedSize() - search_offset;
569 BYTE* buffer_ptr = memPage.getLoadedData() + search_offset;
570 if (!memPage.validatePtr(buffer_ptr, scan_size)) {
571 return nullptr;
572 }
573 const size_t minimal_size = sizeof(IMAGE_DOS_HEADER)
574 + sizeof(IMAGE_FILE_HEADER)
575 + sizeof(IMAGE_OPTIONAL_HEADER32);
576
577 //scan only one page, not the full area
578 for (size_t i = 0; i < scan_size; i++) {
579 const size_t remaining_size = scan_size - i;
580 if (remaining_size < minimal_size) {
581 break;
582 }
583 const BYTE* pe_candidate = buffer_ptr + i;
584 BYTE *nt_hdr = peconv::get_nt_hdrs(pe_candidate, remaining_size);
585 if (nt_hdr != nullptr) {
586 //it was possible to retrieve the NT header, so the PE candidate passed validation
587 return (IMAGE_DOS_HEADER*)(pe_candidate);
588 }
589 }
590 return nullptr;
591}
592
594{
595 IMAGE_DOS_HEADER* dos_hdr = findMzPeHeader(aMap.memPage, search_offset);
596 if (!dos_hdr) {
597 return false;
598 }
599 if (!aMap.memPage.validatePtr(dos_hdr, sizeof(IMAGE_DOS_HEADER))) {
600 return false;
601 }
602 if (setMzPe(aMap, dos_hdr)) {
603 aMap.isMzPeFound = true;
604 }
605 return true;
606}
607
608bool pesieve::ArtefactScanner::setMzPe(ArtefactsMapping &aMap, IMAGE_DOS_HEADER* _dos_hdr)
609{
610 if (!_dos_hdr) return false;
611
612 aMap.dos_hdr = _dos_hdr;
613
614 size_t dos_hdr_offset = calc_offset(aMap.memPage, aMap.dos_hdr);
615 aMap.pe_image_base = aMap.memPage.region_start + dos_hdr_offset;
616
617 IMAGE_NT_HEADERS32* pe_hdrs = (IMAGE_NT_HEADERS32*)((ULONGLONG)_dos_hdr + _dos_hdr->e_lfanew);
618 if (!aMap.memPage.validatePtr(pe_hdrs, sizeof(IMAGE_NT_HEADERS32)))
619 {
620 return false;
621 }
622 setNtFileHdr(aMap, &pe_hdrs->FileHeader);
623 return true;
624}
625
627{
628 if (_sec_hdr == nullptr) return false;
629 const size_t sec_hdr_offset = calc_offset(aMap.memPage, _sec_hdr);
630 if (sec_hdr_offset == INVALID_OFFSET) {
631 return false;
632 }
633 MemPageData &memPage = aMap.memPage;
634 BYTE* loadedData = aMap.memPage.getLoadedData();
635 size_t loadedSize = aMap.memPage.getLoadedSize();
636
637 //validate by counting the sections:
638 size_t count = count_section_hdrs(loadedData, loadedSize, _sec_hdr);
639 if (count == 0) {
640 //std::cout << "Sections header didn't passed validation\n";
641 // sections header didn't passed validation
642 return false;
643 }
644 //if NT headers not found, search before sections header:
645 if (!aMap.nt_file_hdr) {
646 // try to find NT header relative to the sections header:
647 size_t suggested_nt_offset = calc_nt_hdr_offset(aMap.memPage, _sec_hdr, this->isProcess64bit);
648 if (suggested_nt_offset != INVALID_OFFSET && (sec_hdr_offset >= suggested_nt_offset)) {
649 aMap.nt_file_hdr = findNtFileHdr(aMap.memPage, suggested_nt_offset, sec_hdr_offset);
650 }
651 }
652 if (aMap.nt_file_hdr && (ULONG_PTR)aMap.nt_file_hdr > (ULONG_PTR)_sec_hdr) {
653 return false; //misaligned
654 }
655 aMap.sec_hdr = _sec_hdr;
656 aMap.sec_count = count;
657 if (!aMap.pe_image_base) {
658 aMap.pe_image_base = calcPeBase(aMap.memPage, (BYTE*)aMap.sec_hdr);
659 }
660 return true;
661}
662
664{
665 if (!_nt_hdr) return false;
666
667 aMap.nt_file_hdr = _nt_hdr;
668
669 MemPageData &memPage = aMap.memPage;
670 BYTE* loadedData = aMap.memPage.getLoadedData();
671
672 //calculate sections header offset from FileHeader:
673 if (!aMap.sec_hdr) {
674 // set sections headers basing on File Header, do not validate yet
675 size_t sec_hdr_offset = calc_sec_hdrs_offset(aMap.memPage, aMap.nt_file_hdr);
676 aMap.sec_hdr = (IMAGE_SECTION_HEADER*)((ULONGLONG)loadedData + sec_hdr_offset);
677 return true;
678 }
679 // sections headers were set before, validate if they match NT header:
680 if (!validate_hdrs_alignment(aMap.memPage, aMap.nt_file_hdr, aMap.sec_hdr)) {
681 aMap.nt_file_hdr = nullptr; // do not allow setting mismatching NT header
682
683 std::cout << "[WARNING] Sections header misaligned with FileHeader." << std::endl;
684 return false;
685 }
686 //validation passed:
687 return true;
688}
689
691{
692 MemPageData &memPage = aMap.memPage;
693 BYTE* loadedData = aMap.memPage.getLoadedData();
694 size_t loadedSize = aMap.memPage.getLoadedSize();
695
696 if (!aMap.sec_hdr) {
697 // if sections headers not found, don't continue
698 return nullptr;
699 }
700
701 PeArtefacts *peArt = new PeArtefacts();
702 peArt->regionStart = memPage.region_start;
703 peArt->isMzPeFound = aMap.isMzPeFound;
704
705 peArt->secHdrsOffset = calc_offset(memPage, aMap.sec_hdr);
706 peArt->secCount = count_section_hdrs(loadedData, loadedSize, aMap.sec_hdr);
707
708 // if File Header found, use it to validate or find sections headers:
709 peArt->ntFileHdrsOffset = calc_offset(memPage, aMap.nt_file_hdr);
710 //std::cout << "NT offset: " << std::hex << peArt->ntFileHdrsOffset << std::endl;
711 if (!aMap.pe_image_base) {
712 aMap.pe_image_base = calcPeBase(aMap.memPage, (BYTE*)aMap.sec_hdr);
713 }
714 peArt->peBaseOffset = size_t(aMap.pe_image_base - memPage.region_start);
715 peArt->calculatedImgSize = calcImageSize(memPage, aMap.sec_hdr, aMap.pe_image_base);
716
717 if (aMap.nt_file_hdr) {
718 peArt->isDll = ((aMap.nt_file_hdr->Characteristics & IMAGE_FILE_DLL) != 0);
719 }
720
721 if (aMap.nt_file_hdr && aMap.nt_file_hdr->Machine == IMAGE_FILE_MACHINE_I386) {
722 aMap.is64bit = false;
723 }
724 else if (aMap.nt_file_hdr && aMap.nt_file_hdr->Machine == IMAGE_FILE_MACHINE_AMD64) {
725 aMap.is64bit = true;
726 }
727 else {
728 aMap.is64bit = this->isProcess64bit;
729 }
730 peArt->is64bit = aMap.is64bit;
731 return peArt;
732}
733
735{
736 if (!_memPage.load()) {
737 return nullptr;
738 }
739
740 ArtefactsMapping bestMapping(_memPage, this->isProcess64bit);
741
742 for (size_t min_offset = start_offset; min_offset < _memPage.getLoadedSize(); min_offset++)
743 {
744 //std::cout << "Searching DOS header, min_offset: " << std::hex << min_offset << std::endl;
745
746 ArtefactsMapping aMap(_memPage, this->isProcess64bit);
747 //try to find the DOS header
748 if (findMzPe(aMap, min_offset)) {
749 const size_t dos_offset = calc_offset(_memPage, aMap.dos_hdr);
750 min_offset = (dos_offset != INVALID_OFFSET) ? dos_offset : min_offset;
751#ifdef _DEBUG
752 std::cout << std::hex << "Page: " << aMap.memPage.start_va << " Found DOS Header at: " << dos_offset << "\n";
753#endif
754 }
755 else {
756#ifdef _DEBUG
757 std::cout << std::hex << "Page: " << aMap.memPage.start_va << " Searching NT Header at: " << min_offset << "\n";
758#endif
759 IMAGE_FILE_HEADER *nt_hdr = findNtFileHdr(aMap.memPage, min_offset, _memPage.getLoadedSize());
760 setNtFileHdr(aMap, nt_hdr);
761 }
762
763 //adjust constraints for further searches:
764 size_t max_section_search = _memPage.getLoadedSize();
765 if (aMap.nt_file_hdr) {
766 const size_t nt_offset = calc_offset(_memPage, aMap.nt_file_hdr);
767 if (nt_offset != INVALID_OFFSET && nt_offset > min_offset) {
768 min_offset = nt_offset;
769 }
770 //don't search sections in full module, only in the first mem page after the NT header:
771 max_section_search = (PAGE_SIZE < _memPage.getLoadedSize()) ? PAGE_SIZE : _memPage.getLoadedSize();
772 if (max_section_search + min_offset <= _memPage.getLoadedSize()) {
773 max_section_search += min_offset; //move the search window
774 }
775 }
776
777 if (!setSecHdr(aMap, aMap.sec_hdr)) {
778 //search sections by pattens:
779 if (max_section_search > min_offset) {
780 const size_t diff = max_section_search - min_offset;
781 IMAGE_SECTION_HEADER *sec_hdr = findSecByPatterns(_memPage, diff, min_offset);
782 setSecHdr(aMap, sec_hdr);
783 }
784 }
785 if (aMap.sec_hdr) {
786 const size_t sec_offset = calc_offset(_memPage, aMap.sec_hdr);
787 if (sec_offset != INVALID_OFFSET && sec_offset > min_offset) {
788 const size_t sections_area_size = aMap.sec_count * sizeof(IMAGE_SECTION_HEADER);
789 min_offset = (sec_offset + sections_area_size);
790#ifdef _DEBUG
791 std::cout << "Setting minOffset to SecHdr end offset: " << std::hex << min_offset << "\n";
792#endif
793 }
794
795 if (!aMap.dos_hdr) {
796 const size_t start = (sec_offset > PAGE_SIZE) ? (sec_offset - PAGE_SIZE) : 0;
797 //std::cout << "Searching DOS header by patterns " << std::hex << start << "\n";
798 aMap.dos_hdr = findDosHdrByPatterns(aMap.memPage, start, sec_offset);
799 if (aMap.dos_hdr && !aMap.nt_file_hdr) {
800 IMAGE_NT_HEADERS32 *nt_ptr = (IMAGE_NT_HEADERS32*)((ULONG_PTR)aMap.dos_hdr + aMap.dos_hdr->e_lfanew);
801#ifdef _DEBUG
802 const size_t nt_offset = calc_offset(memPage, nt_ptr);
803 std::cout << "Found PE offset: " << std::hex << aMap.dos_hdr->e_lfanew << " NT offset: " << nt_offset << "\n";
804#endif
805 if (aMap.memPage.validatePtr(nt_ptr, sizeof(IMAGE_NT_HEADERS32))) {
806 setNtFileHdr(aMap, &nt_ptr->FileHeader);
807 }
808 }
809 }
810 }
811 if (!setSecHdr(aMap, aMap.sec_hdr)) {
812 aMap.sec_hdr = nullptr;
813 }
814 bestMapping = (bestMapping < aMap) ? aMap : bestMapping;
815
816 //do not continue the search if no artefacts found:
817 if (!aMap.foundAny()) break;
818
819 // adjust minimal values:
820 const size_t nt_offset = calc_offset(_memPage, aMap.nt_file_hdr);
821 const size_t sec_offset = calc_offset(_memPage, aMap.sec_hdr);
822 if (nt_offset != INVALID_OFFSET && nt_offset > min_offset) {
823 min_offset = nt_offset;
824 }
825 if (sec_offset != INVALID_OFFSET && sec_offset > min_offset) {
826 min_offset = sec_offset;
827 }
828 }
829 if (bestMapping.getScore() <= 1) {
830 return nullptr; // too low score
831 }
832 //use the best found set of artefacts:
833 return generateArtefacts(bestMapping);
834}
835
836PeArtefacts* pesieve::ArtefactScanner::findInPrevPages(ULONGLONG addr_start, ULONGLONG addr_stop)
837{
838 deletePrevPage();
839 PeArtefacts* peArt = nullptr;
840 ULONGLONG next_addr = addr_stop - PAGE_SIZE;
841 do {
842 if (next_addr < addr_start) {
843 break;
844 }
845 const size_t area_size = size_t(addr_stop - next_addr);
846 if (this->processReport.hasModuleContaining((ULONGLONG)next_addr, area_size)) {
847 //std::cout << "Aready scanned: " << std::hex << next_addr << " size: " << area_size << "\n";
848 break;
849 }
850 this->prevMemPage = new MemPageData(this->processHandle, this->pDetails.isReflection, next_addr, addr_stop);
851 peArt = findArtefacts(*prevMemPage, 0);
852 if (peArt) {
853 break;
854 }
855 next_addr -= (this->prevMemPage->region_start - PAGE_SIZE);
856 deletePrevPage();
857 } while (true);
858
859 return peArt;
860}
861
862bool pesieve::ArtefactScanner::hasShellcode(HMODULE region_start, size_t region_size, PeArtefacts &peArt)
863{
864 bool is_shellcode = false;
865 if (peArt.peBaseOffset > 0) {
866 // the total region is bigger than the PE
867 is_shellcode = true;
868 }
869 if (region_size > peArt.calculatedImgSize) {
870 // the total region is bigger than the PE
871 is_shellcode = true;
872 }
873 return is_shellcode;
874}
875
877{
878 deletePrevPage();
879
880 // it may still contain a damaged PE header...
881 ULONGLONG region_start = memPage.region_start;
882 this->artPagePtr = &memPage;
883
884 PeArtefacts *peArt = findArtefacts(memPage, 0);
885 if (!peArt && (region_start > memPage.alloc_base)) {
886 peArt = findInPrevPages(memPage.alloc_base, memPage.region_start);
887 if (prevMemPage) {
888 this->artPagePtr = prevMemPage;
889 region_start = prevMemPage->region_start;
890 }
891 }
892 if (!peArt) {
893 //no artefacts found
894 return nullptr;
895 }
896 const size_t region_size = size_t(memPage.region_end - region_start);
897
898 ArtefactScanReport *my_report = new ArtefactScanReport((HMODULE)region_start, region_size, SCAN_SUSPICIOUS, *peArt);
899 my_report->protection = memPage.protection;
900 my_report->has_shellcode = hasShellcode((HMODULE)region_start, region_size, *peArt);
901 delete peArt;
902 return my_report;
903}
#define INVALID_OFFSET
A report from the artefacts scan, generated by ArtefactScanner.
virtual ArtefactScanReport * scanRemote()
bool hasShellcode(HMODULE region_start, size_t region_size, PeArtefacts &peArt)
size_t calcImageSize(MemPageData &memPage, IMAGE_SECTION_HEADER *hdr_ptr, ULONGLONG pe_image_base)
ULONGLONG _findMZoffset(MemPageData &memPage, LPVOID hdr_ptr)
bool findMzPe(ArtefactsMapping &mapping, const size_t search_offset)
IMAGE_SECTION_HEADER * findSecByPatterns(MemPageData &memPageData, const size_t max_search_size, const size_t search_offset)
ULONGLONG calcPeBase(MemPageData &memPage, LPVOID hdr_ptr)
bool setMzPe(ArtefactsMapping &mapping, IMAGE_DOS_HEADER *_dos_hdr)
bool setNtFileHdr(ArtefactScanner::ArtefactsMapping &aMap, IMAGE_FILE_HEADER *_nt_hdr)
IMAGE_DOS_HEADER * _findDosHdrByPatterns(BYTE *search_ptr, const size_t max_search_size)
bool setSecHdr(ArtefactsMapping &mapping, IMAGE_SECTION_HEADER *_sec_hdr)
bool _validateSecRegions(MemPageData &memPage, LPVOID sec_hdr, size_t sec_count, ULONGLONG pe_image_base, bool is_virtual)
IMAGE_FILE_HEADER * findNtFileHdr(MemPageData &memPage, const size_t start_offset, size_t stop_offset=INVALID_OFFSET)
PeArtefacts * findArtefacts(MemPageData &memPage, size_t start_offset)
IMAGE_DOS_HEADER * findDosHdrByPatterns(MemPageData &memPage, const size_t start_offset, size_t stop_offset=INVALID_OFFSET)
static size_t calcImgSize(HANDLE processHandle, HMODULE modBaseAddr, BYTE *headerBuffer, size_t headerBufferSize, IMAGE_SECTION_HEADER *hdr_ptr=NULL)
PeArtefacts * findInPrevPages(ULONGLONG addr_start, ULONGLONG addr_stop)
PeArtefacts * generateArtefacts(ArtefactsMapping &aMap)
IMAGE_DOS_HEADER * findMzPeHeader(MemPageData &memPage, const size_t search_offset)
BYTE * _findSecByPatterns(BYTE *search_ptr, const size_t max_search_size)
size_t getLoadedSize(bool trimmed=false)
const PBYTE getLoadedData(bool trimmed=false)
bool validatePtr(const LPVOID field_bgn, size_t field_size)
ULONGLONG start_va
VA that was requested. May not be beginning of the region.
A report about the PE artefact detected in the workingset.
bool validate_hdrs_alignment(MemPageData &memPage, IMAGE_FILE_HEADER *nt_file_hdr, IMAGE_SECTION_HEADER *_sec_hdr)
size_t calc_offset(MemPageData &memPage, LPVOID field)
IMAGE_SECTION_HEADER * get_first_section(BYTE *loadedData, size_t loadedSize, IMAGE_SECTION_HEADER *hdr_ptr)
size_t calc_nt_hdr_offset(MemPageData &memPage, IMAGE_SECTION_HEADER *first_sec, bool is64bit=true)
BYTE * find_pattern(BYTE *buffer, size_t buf_size, BYTE *pattern_buf, size_t pattern_size, size_t max_iter=0)
DWORD(__stdcall *_PssCaptureSnapshot)(HANDLE ProcessHandle
size_t count_section_hdrs(BYTE *loadedData, size_t loadedSize, IMAGE_SECTION_HEADER *hdr_ptr)
size_t calc_sec_hdrs_offset(MemPageData &memPage, IMAGE_FILE_HEADER *nt_file_hdr)
bool is_valid_file_hdr(BYTE *loadedData, size_t loadedSize, BYTE *hdr_ptr, DWORD charact)
bool is_valid_section(BYTE *loadedData, size_t loadedSize, BYTE *hdr_ptr, DWORD charact)
#define PAGE_SIZE