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
headers_scanner.cpp
Go to the documentation of this file.
1#include "headers_scanner.h"
2#include <peconv.h>
3
4using namespace pesieve;
5
7{
9 std::cerr << "[-] Module not initialized" << std::endl;
10 return nullptr;
11 }
13 std::cerr << "[-] Failed to read the module header" << std::endl;
14 return nullptr;
15 }
16
18
19 BYTE hdr_buffer1[peconv::MAX_HEADER_SIZE] = { 0 };
20 memcpy(hdr_buffer1, remoteModData.headerBuffer, peconv::MAX_HEADER_SIZE);
21 my_report->is64 = peconv::is64bit(hdr_buffer1);
22 my_report->isDotNetModule = moduleData.isDotNet();
23 my_report->origBase = moduleData.getHdrImageBase();
25
26 size_t hdrs_size = peconv::get_hdrs_size(hdr_buffer1);
27 if (hdrs_size > peconv::MAX_HEADER_SIZE) {
28 hdrs_size = peconv::MAX_HEADER_SIZE;
29 }
30
31 BYTE hdr_buffer2[peconv::MAX_HEADER_SIZE] = { 0 };
32 memcpy(hdr_buffer2, moduleData.original_module, hdrs_size);
33
34 // some .NET modules overwrite their own headers, so at this point they should be excluded from the comparison
35 const DWORD ep1 = peconv::get_entry_point_rva(hdr_buffer1);
36 const DWORD ep2 = peconv::get_entry_point_rva(hdr_buffer2);
37 if (ep1 != ep2) {
38 my_report->epModified = true;
39 }
40 const DWORD arch1 = peconv::get_nt_hdr_architecture(hdr_buffer1);
41 const DWORD arch2 = peconv::get_nt_hdr_architecture(hdr_buffer2);
42 if (arch1 != arch2) {
43 // this often happend in .NET modules
44 //if there is an architecture mismatch it may indicate that a different version of the app was loaded (possibly legit)
45 my_report->archMismatch = true;
46 }
47
48 //normalize before comparing:
49 peconv::update_image_base(hdr_buffer1, 0);
50 peconv::update_image_base(hdr_buffer2, 0);
51
52 zeroUnusedFields(hdr_buffer1, hdrs_size);
53 zeroUnusedFields(hdr_buffer2, hdrs_size);
54
55 //compare:
56 if (memcmp(hdr_buffer1, hdr_buffer2, hdrs_size) == 0) {
57 my_report->status = SCAN_NOT_SUSPICIOUS;
58 return my_report;
59 }
60 //modifications detected, now find more details:
61 my_report->dosHdrModified = isDosHdrModified(hdr_buffer1, hdr_buffer2, hdrs_size);
62 my_report->fileHdrModified = isFileHdrModified(hdr_buffer1, hdr_buffer2, hdrs_size);
63 my_report->ntHdrModified = isNtHdrModified(hdr_buffer1, hdr_buffer2, hdrs_size);
64 my_report->secHdrModified = isSecHdrModified(hdr_buffer1, hdr_buffer2, hdrs_size);
65
66 if (moduleData.isDotNet()) {
67 const bool dotNetFileHdrModif = isFileHdrModified(hdr_buffer1, hdr_buffer2, hdrs_size, my_report->archMismatch);
68#ifdef _DEBUG
69 std::cout << "[#] .NET module detected as SUSPICIOUS\n";
70#endif
71 if (!my_report->isHdrReplaced()
72 && !my_report->dosHdrModified
73 && !dotNetFileHdrModif
74 && (my_report->epModified || (my_report->archMismatch && my_report->ntHdrModified))
75 )
76 {
77 //.NET modules may overwrite some parts of their own headers
78#ifdef _DEBUG
79 std::cout << "[#] Filtered out modifications typical for .NET files, setting as not suspicious\n";
80#endif
81 my_report->status = SCAN_NOT_SUSPICIOUS;
82 return my_report;
83 }
84 }
85 my_report->status = SCAN_SUSPICIOUS;
86 return my_report;
87}
88
89bool pesieve::HeadersScanner::zeroUnusedFields(PBYTE hdr_buffer, size_t hdrs_size)
90{
91 bool is_modified = false;
92 const size_t section_num = peconv::get_sections_count(hdr_buffer, hdrs_size);
93
94 for (size_t i = 0; i < section_num; i++) {
95 PIMAGE_SECTION_HEADER sec_hdr = peconv::get_section_hdr(hdr_buffer, hdrs_size, i);
96 if (sec_hdr == nullptr) continue;
97
98 if (sec_hdr->SizeOfRawData == 0) {
99 sec_hdr->PointerToRawData = 0;
100 is_modified = true;
101 }
102 }
103 return is_modified;
104}
105
106bool pesieve::HeadersScanner::isDosHdrModified(const PBYTE hdr_buffer1, const PBYTE hdr_buffer2, const size_t hdrs_size)
107{
108 if (hdrs_size < sizeof(IMAGE_DOS_HEADER)) { //should never happen
109 return false;
110 }
111 IMAGE_DOS_HEADER* hdr1 = (IMAGE_DOS_HEADER*)hdr_buffer1;
112 IMAGE_DOS_HEADER* hdr2 = (IMAGE_DOS_HEADER*)hdr_buffer2;
113 if (memcmp(hdr1, hdr2, sizeof(IMAGE_DOS_HEADER)) != 0) {
114 return true;
115 }
116
117 LONG new_hdr = hdr2->e_lfanew;
118 if (memcmp(hdr1, hdr2, new_hdr) != 0) {
119 return true;
120 }
121 return false;
122}
123
124bool pesieve::HeadersScanner::isSecHdrModified(const PBYTE hdr_buffer1, const PBYTE hdr_buffer2, const size_t hdrs_size)
125{
126 size_t section_num1 = peconv::get_sections_count(hdr_buffer1, hdrs_size);
127 size_t section_num2 = peconv::get_sections_count(hdr_buffer2, hdrs_size);
128 if (section_num1 != section_num2) {
129 return true;
130 }
131
132 for (size_t i = 0; i < section_num1; i++) {
133 PIMAGE_SECTION_HEADER sec_hdr1 = peconv::get_section_hdr(hdr_buffer1, hdrs_size, i);
134 PIMAGE_SECTION_HEADER sec_hdr2 = peconv::get_section_hdr(hdr_buffer2, hdrs_size, i);
135 if (!sec_hdr1 && !sec_hdr2) {
136 continue;
137 }
138 else if (!sec_hdr1 || !sec_hdr2) {
139 return true; //modified
140 }
141
142 if (sec_hdr1->VirtualAddress != sec_hdr2->VirtualAddress) {
143 return true;
144 }
145 if (sec_hdr1->Misc.VirtualSize != sec_hdr2->Misc.VirtualSize) {
146 return true;
147 }
148 if (sec_hdr1->PointerToRawData != sec_hdr2->PointerToRawData) {
149 return true;
150 }
151 }
152 return false;
153}
154
155bool pesieve::HeadersScanner::isFileHdrModified(const PBYTE hdr_buffer1, const PBYTE hdr_buffer2, const size_t hdrs_size, bool mask_arch_mismatch)
156{
157 const IMAGE_FILE_HEADER *file_hdr1 = peconv::get_file_hdr(hdr_buffer1, hdrs_size);
158 const IMAGE_FILE_HEADER *file_hdr2 = peconv::get_file_hdr(hdr_buffer2, hdrs_size);
159
160 if (!file_hdr1 && !file_hdr2) return false;
161 if (!file_hdr1 || !file_hdr2) return true;
162
163 if (memcmp(file_hdr1, file_hdr2, sizeof(IMAGE_FILE_HEADER)) == 0) {
164 return false;
165 }
166 if (mask_arch_mismatch) {
167 if (file_hdr1->Machine == file_hdr2->Machine
168 && file_hdr1->Characteristics == file_hdr2->Characteristics
169 && file_hdr1->NumberOfSections == file_hdr2->NumberOfSections
170 && file_hdr1->TimeDateStamp == file_hdr2->TimeDateStamp
171 && file_hdr1->SizeOfOptionalHeader != file_hdr2->SizeOfOptionalHeader)
172 {
173 // only the SizeOfOptionalHeader has changed
174 return false;
175 }
176 }
177 return true;
178}
179
180bool pesieve::HeadersScanner::isNtHdrModified(const PBYTE hdr_buffer1, const PBYTE hdr_buffer2, const size_t hdrs_size)
181{
182 const bool is64 = peconv::is64bit(hdr_buffer1);
183 if (peconv::is64bit(hdr_buffer2) != is64) {
184 return true;
185 }
186 const BYTE *nt1 = peconv::get_nt_hdrs(hdr_buffer1, hdrs_size);
187 const BYTE *nt2 = peconv::get_nt_hdrs(hdr_buffer2, hdrs_size);
188 if (!nt1 && !nt2) return false;
189 if (!nt1 || !nt2) return true;
190
191 const size_t nt_hdr_size = is64 ? sizeof(IMAGE_NT_HEADERS64) : sizeof(IMAGE_NT_HEADERS32);
192 if (memcmp(nt1, nt2, nt_hdr_size) == 0) {
193 return false;
194 }
195 return true;
196}
A report from the headers scan, generated by HeadersScanner.
virtual HeadersScanReport * scanRemote()
ULONGLONG getHdrImageBase()
Definition module_data.h:83
RemoteModuleData & remoteModData
BYTE headerBuffer[peconv::MAX_HEADER_SIZE]