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
pe_reconstructor.cpp
Go to the documentation of this file.
1#include "pe_reconstructor.h"
2
4
5#include <fstream>
6
7namespace pesieve {
8 inline bool shift_artefacts(PeArtefacts& artefacts, size_t shift_size)
9 {
10 artefacts.ntFileHdrsOffset += shift_size;
11 artefacts.secHdrsOffset += shift_size;
12 return true;
13 }
14}; //namespace pesieve
15
16
17//WARNING: this function shifts also offsets saved in the artefacts
19{
20 BYTE *vBuf = this->peBuffer.vBuf;
21 const size_t vBufSize = this->peBuffer.vBufSize;
22 if (vBuf == nullptr) return 0;
23
24 if (!this->artefacts.hasNtHdrs()) return 0;
25
26 const size_t dos_pe_size = sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_SIGNATURE);
27 const size_t nt_offset = this->artefacts.dropPeBase(this->artefacts.ntFileHdrsOffset);
28 if (nt_offset == INVALID_OFFSET || nt_offset >= dos_pe_size) {
29 return 0;
30 }
31 //TODO: shift the header
32 if (!this->artefacts.hasSectionHdrs()) return 0; //cannot proceed
33
34 size_t shift_size = dos_pe_size - nt_offset;
35 size_t hdrs_end = this->artefacts.secHdrsOffset + (this->artefacts.secCount + 1)* sizeof(IMAGE_SECTION_HEADER);
36 if (!peconv::is_padding(vBuf + hdrs_end, shift_size, 0)) {
37 return 0; // no empty space, cannot proceed
38 }
39 size_t hdrs_size = this->artefacts.dropPeBase(hdrs_end);
40 BYTE *new_nt_ptr = vBuf + this->artefacts.peBaseOffset + shift_size;
41 if (!peconv::validate_ptr(vBuf, vBufSize, new_nt_ptr, hdrs_size)) {
42 return 0;
43 }
44 if (nt_offset < sizeof(IMAGE_NT_SIGNATURE)) {
45 return 0;
46 }
47 const size_t pe_offset = nt_offset - sizeof(IMAGE_NT_SIGNATURE);
48 IMAGE_DOS_HEADER dos_template = { 0 };
49 dos_template.e_magic = IMAGE_DOS_SIGNATURE;
50 dos_template.e_lfanew = LONG(pe_offset + shift_size);
51
52 //check mz signature:
53 BYTE *mz_ptr = vBuf + this->artefacts.peBaseOffset;
54 if (!peconv::validate_ptr(vBuf, vBufSize, mz_ptr, sizeof(IMAGE_DOS_HEADER))) {
55 return 0;
56 }
57 //check PE signature:
58 DWORD* pe_ptr = (DWORD*)(vBuf + this->artefacts.peBaseOffset + dos_template.e_lfanew);
59 if (!peconv::validate_ptr(vBuf, vBufSize, pe_ptr, sizeof(DWORD))) {
60 return 0;
61 }
62 //all checks passed, do the actual headers shift:
63 memmove(new_nt_ptr, (vBuf + this->artefacts.peBaseOffset), hdrs_size);
64
65 //write the DOS header:
66 memcpy(mz_ptr, &dos_template, sizeof(IMAGE_DOS_HEADER));
67
68 //write the PE signature:
69 *pe_ptr = IMAGE_NT_SIGNATURE;
70
71 shift_artefacts(this->artefacts, shift_size);
72 return shift_size;
73}
74
76{
78
79 ULONGLONG moduleBase = artefacts.regionStart + artefacts.peBaseOffset;
80 if (!peBuffer.readRemote(moduleBase, artefacts.calculatedImgSize)) {
81 return false;
82 }
83 size_t shift_size = shiftPeHeader();
84 if (shift_size) {
85 std::cout << "[!] The PE header was shifted by: " << std::hex << shift_size << std::endl;
86 }
87 bool is_pe_hdr = false;
88 if (this->artefacts.hasNtHdrs() && reconstructFileHdr()) {
89 is_pe_hdr = reconstructPeHdr();
90 }
91 if (!is_pe_hdr) {
92 return false;
93 }
94
95 //do not modify section headers if the PE is in raw format, or no unmapping requested
96 if (!peconv::is_pe_raw(peBuffer.vBuf, peBuffer.vBufSize)) {
97
98 if (!fixSectionsVirtualSize(peBuffer.processHndl) || !fixSectionsCharacteristics(peBuffer.processHndl)) {
99 return false;
100 }
101 const ULONGLONG base_candidate = peconv::find_base_candidate(peBuffer.vBuf, peBuffer.vBufSize);
102 if (base_candidate) {
103 peconv::update_image_base(peBuffer.vBuf, (ULONGLONG)base_candidate);
104 }
105 }
106 return peBuffer.isValidPe();
107}
108
110{
111 BYTE *vBuf = this->peBuffer.vBuf;
112 const size_t vBufSize = this->peBuffer.vBufSize;
113 if (!vBuf) return false;
114
115 if (!this->artefacts.hasSectionHdrs()) {
116 return false;
117 }
118 ULONGLONG sec_offset = this->artefacts.dropPeBase(this->artefacts.secHdrsOffset);
119 BYTE *hdr_ptr = (sec_offset + vBuf);
120
121 size_t max_sec_size = 0;
122
123 IMAGE_SECTION_HEADER* prev_sec = nullptr;
124 IMAGE_SECTION_HEADER* curr_sec = (IMAGE_SECTION_HEADER*)(hdr_ptr);
125
126 const ULONGLONG pe_img_base = (ULONGLONG)artefacts.peImageBase();
127
128 const size_t hdr_sec_count = peconv::get_sections_count(vBuf, vBufSize);
129 const size_t sec_count = artefacts.secCount > hdr_sec_count ? artefacts.secCount : hdr_sec_count;
130
131 size_t i = 0;
132 for (i = 0; i < sec_count; i++, curr_sec++) {
133 if (!peconv::validate_ptr(vBuf, vBufSize, curr_sec, sizeof(IMAGE_SECTION_HEADER))) {
134 // buffer finished
135 break;
136 }
137 const DWORD sec_rva = curr_sec->VirtualAddress;
138 const DWORD sec_size = curr_sec->Misc.VirtualSize;
139
140 const ULONGLONG sec_va = pe_img_base + sec_rva;
141 size_t real_sec_size = peconv::fetch_region_size(processHandle, (PBYTE)sec_va);
142
143 // if the RVA is out of scope, and the calculated size is 0:
144 if (!peconv::validate_ptr(vBuf, vBufSize, vBuf + sec_rva, sizeof(BYTE)) && !real_sec_size) {
145#ifdef _DEBUG
146 std::cout << i << "# Invalid section found: " << std::hex
147 << sec_rva << " of size: " << sec_size << std::endl;
148#endif
149 break;
150 }
151
152 if (sec_size > real_sec_size) {
153 curr_sec->Misc.VirtualSize = DWORD(real_sec_size);
154#ifdef _DEBUG
155 std::cout << i << "# Fixed section " << std::hex << sec_rva << " size: " << std::hex
156 << sec_size << " vs real: " << real_sec_size << std::endl;
157#endif
158 }
159
160 max_sec_size = (real_sec_size > max_sec_size) ? real_sec_size : max_sec_size;
161
162 if (prev_sec && curr_sec->Misc.VirtualSize > 0) {
163 const ULONGLONG prev_sec_end = static_cast<ULONGLONG>(prev_sec->VirtualAddress) + static_cast<ULONGLONG>(prev_sec->Misc.VirtualSize);
164 if (prev_sec_end > curr_sec->VirtualAddress) {
165 if (curr_sec->VirtualAddress > prev_sec->VirtualAddress) {
166 DWORD diff = curr_sec->VirtualAddress - prev_sec->VirtualAddress;
167 prev_sec->Misc.VirtualSize = diff;
168#ifdef _DEBUG
169 std::cout << "Trimmed section: " << std::dec << i << std::endl;
170#endif
171 }
172 }
173 }
174 if (curr_sec->Misc.VirtualSize > 0) {
175 prev_sec = curr_sec;
176 }
177 }
178
179 IMAGE_FILE_HEADER* file_hdr = const_cast<IMAGE_FILE_HEADER*>(peconv::get_file_hdr(vBuf, vBufSize));
180 if (file_hdr && (i > 0)) {
181 // set the actual number of valid sections:
182 file_hdr->NumberOfSections = MASK_TO_WORD(i);
183 }
184
185 if (max_sec_size == 0) {
186 return false;
187 }
188 return true;
189}
190
192{
193 BYTE *vBuf = this->peBuffer.vBuf;
194 const size_t vBufSize = this->peBuffer.vBufSize;
195 if (!vBuf) return false;
196
197 if (!this->artefacts.hasSectionHdrs()) {
198 return false;
199 }
200
201 ULONGLONG sec_offset = this->artefacts.dropPeBase(this->artefacts.secHdrsOffset);
202 const BYTE *hdr_ptr = (sec_offset + vBuf);
203 IMAGE_SECTION_HEADER* curr_sec = (IMAGE_SECTION_HEADER*)(hdr_ptr);
204
205 const DWORD sec_all_flags = IMAGE_SCN_TYPE_NO_PAD
206 | IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_CNT_UNINITIALIZED_DATA
207 | IMAGE_SCN_LNK_NRELOC_OVFL | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_NOT_CACHED
208 | IMAGE_SCN_MEM_NOT_PAGED | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
209 | IMAGE_SCN_MEM_WRITE
210 | IMAGE_SCN_NO_DEFER_SPEC_EXC | IMAGE_SCN_GPREL;
211
212 for (size_t i = 0; i < artefacts.secCount; i++, curr_sec++) {
213 if (!is_valid_section(vBuf, vBufSize, (BYTE*)curr_sec, 0)) {
214 break;
215 }
216 //leave only the flags that are valid
217 const DWORD charact = curr_sec->Characteristics;
218 curr_sec->Characteristics = charact & sec_all_flags;
219#ifdef DEBUG
220 if (charact != curr_sec->Characteristics) {
221 std::cout << "Section characteristics overwriten\n";
222 }
223#endif
224 }
225 return true;
226}
227
229{
230 BYTE *vBuf = this->peBuffer.vBuf;
231 const size_t vBufSize = this->peBuffer.vBufSize;
232 if (!vBuf) return false;
233
234 if (!this->artefacts.hasNtHdrs()) {
235 return false;
236 }
237 size_t nt_offset = this->artefacts.dropPeBase(this->artefacts.ntFileHdrsOffset);
238 BYTE* nt_ptr = (BYTE*)((ULONGLONG)vBuf + nt_offset);
239 if (is_valid_file_hdr(vBuf, vBufSize, nt_ptr, 0)) {
240 return true;
241 }
242 IMAGE_FILE_HEADER* hdr_candidate = (IMAGE_FILE_HEADER*)nt_ptr;
243 if (!peconv::validate_ptr(vBuf, vBufSize, hdr_candidate, sizeof(IMAGE_FILE_HEADER))) {
244 // probably buffer finished
245 return false;
246 }
247
248 size_t opt_hdr_size = 0;
249 if (artefacts.is64bit) {
250 hdr_candidate->Machine = IMAGE_FILE_MACHINE_AMD64;
251 opt_hdr_size = sizeof(IMAGE_OPTIONAL_HEADER64);
252 }
253 else {
254 hdr_candidate->Machine = IMAGE_FILE_MACHINE_I386;
255 opt_hdr_size = sizeof(IMAGE_OPTIONAL_HEADER32);
256 }
257 if (this->artefacts.secHdrsOffset) {
258 const size_t sec_offset = this->artefacts.dropPeBase(this->artefacts.secHdrsOffset);
259 size_t calc_offset = sec_offset - (nt_offset + sizeof(IMAGE_FILE_HEADER));
260
261 if (calc_offset != opt_hdr_size) {
262 std::cout << "[WARNING] Calculated sections header offset is different than the saved one!\n";
263 }
264 hdr_candidate->NumberOfSections = WORD(this->artefacts.secCount);
265 hdr_candidate->SizeOfOptionalHeader = WORD(calc_offset);
266 }
267 hdr_candidate->NumberOfSymbols = 0;
268 hdr_candidate->PointerToSymbolTable = 0;
269 return true;
270}
271
273{
274 BYTE *vBuf = this->peBuffer.vBuf;
275 const size_t vBufSize = this->peBuffer.vBufSize;
276 if (!vBuf) return false;
277
278 if (!this->artefacts.hasNtHdrs()) {
279 return false;
280 }
281 ULONGLONG nt_offset = this->artefacts.dropPeBase(this->artefacts.ntFileHdrsOffset);
282 BYTE* nt_ptr = (BYTE*)((ULONGLONG)vBuf + nt_offset);
283 BYTE *pe_ptr = nt_ptr - sizeof(DWORD);
284
285 if (!peconv::validate_ptr(vBuf, vBufSize, pe_ptr, sizeof(DWORD))) {
286 return false;
287 }
288 IMAGE_NT_HEADERS32 *nt32 = (IMAGE_NT_HEADERS32*)pe_ptr;
289 //write signature:
290 nt32->Signature = IMAGE_NT_SIGNATURE;
291 IMAGE_FILE_HEADER *file_hdr = &nt32->FileHeader;
292
293 bool is64bit = (file_hdr->Machine == IMAGE_FILE_MACHINE_AMD64) ? true : false;
294
295 if (nt32->FileHeader.SizeOfOptionalHeader == 0) {
296 nt32->FileHeader.SizeOfOptionalHeader = is64bit ? sizeof(IMAGE_OPTIONAL_HEADER64) : sizeof(IMAGE_OPTIONAL_HEADER32);
297 }
298 LONG pe_offset = LONG((ULONGLONG)pe_ptr - (ULONGLONG)vBuf);
299 IMAGE_DOS_HEADER* dosHdr = (IMAGE_DOS_HEADER*) vBuf;
300 dosHdr->e_magic = IMAGE_DOS_SIGNATURE;
301 dosHdr->e_lfanew = pe_offset;
302
303 bool is_fixed = false;
304 if (is64bit) {
305 is_fixed = overwrite_opt_hdr<IMAGE_OPTIONAL_HEADER64>(vBuf, vBufSize, (IMAGE_OPTIONAL_HEADER64*)&nt32->OptionalHeader, this->artefacts);
306 }
307 else {
308 is_fixed = overwrite_opt_hdr<IMAGE_OPTIONAL_HEADER32>(vBuf, vBufSize, &nt32->OptionalHeader, this->artefacts);
309 }
310 if (!is_fixed) {
311 return false;
312 }
313 if (!peconv::get_nt_hdrs(vBuf, vBufSize)) {
314 return false;
315 }
316 return true;
317}
#define INVALID_OFFSET
A report about the PE artefact detected in the workingset.
bool fixSectionsVirtualSize(HANDLE processHandle)
bool fixSectionsCharacteristics(HANDLE processHandle)
const PeArtefacts origArtefacts
bool is_valid_file_hdr(BYTE *loadedData, size_t loadedSize, BYTE *hdr_ptr, DWORD charact)
bool overwrite_opt_hdr(BYTE *vBuf, size_t vBufSize, IMAGE_OPTIONAL_HEADER_T *opt_hdr_ptr, PeArtefacts &artefacts)
bool shift_artefacts(PeArtefacts &artefacts, size_t shift_size)
bool is_valid_section(BYTE *loadedData, size_t loadedSize, BYTE *hdr_ptr, DWORD charact)