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
results_dumper.cpp
Go to the documentation of this file.
1#include "results_dumper.h"
2
3#include <windows.h>
4#include <psapi.h>
5
6#include <fstream>
7
10#include "pe_reconstructor.h"
14
15#define DIR_SEPARATOR "\\"
16
17//---
18namespace pesieve {
19
21 {
22 if (!artefactRepot.has_pe) {
23 return "shc";
24 }
25 if (artefactRepot.artefacts.isDll) {
26 return "dll";
27 }
28 return "exe";
29 }
30
31 std::string get_dump_mode_name(peconv::t_pe_dump_mode dump_mode)
32 {
33 switch (dump_mode) {
34 case peconv::PE_DUMP_VIRTUAL:
35 return "VIRTUAL";
36 case peconv::PE_DUMP_UNMAP:
37 return "UNMAPPED";
38 case peconv::PE_DUMP_REALIGN:
39 return "REALIGNED";
40 }
41 return "";
42 }
43
45 {
46 switch (res) {
48 return "IMP_NOT_FOUND";
50 return "IMP_RECOVERY_ERROR";
52 return "IMP_RECOVERY_NOT_APPLICABLE";
54 return "";
56 return "IMP_ALREADY_OK";
58 return "IMP_DIR_FIXED";
60 return "IMP_FIXED";
62 return "IMP_RECREATED_FILTER0";
64 return "IMP_RECREATED_FILTER1";
66 return "IMP_RECREATED_FILTER2";
67 }
68 return "Undefined";
69 }
70
71 peconv::t_pe_dump_mode convert_to_peconv_dump_mode(const pesieve::t_dump_mode dump_mode)
72 {
73 switch (dump_mode) {
74 case pesieve::PE_DUMP_AUTO:
75 return peconv::PE_DUMP_AUTO;
76
77 case pesieve::PE_DUMP_VIRTUAL:
78 return peconv::PE_DUMP_VIRTUAL;
79
80 case pesieve::PE_DUMP_UNMAP:
81 return peconv::PE_DUMP_UNMAP;
82
83 case pesieve::PE_DUMP_REALIGN:
84 return peconv::PE_DUMP_REALIGN;
85 }
86 return peconv::PE_DUMP_AUTO;
87 }
88
89 bool make_dump_dir(const std::string& directory)
90 {
91 if (directory.length() == 0) {
92 return true;
93 }
95 }
96
97 std::string get_module_file_name(HANDLE processHandle, const ModuleScanReport& mod)
98 {
99 if (mod.moduleFile.length() > 0) {
100 return peconv::get_file_name(mod.moduleFile);
101 }
102
103 char szModName[MAX_PATH] = { 0 };
104 memset(szModName, 0, MAX_PATH);
105
106 std::string modulePath = "";
107 if (GetModuleFileNameExA(processHandle, (HMODULE)mod.module, szModName, MAX_PATH)) {
108 modulePath = peconv::get_file_name(szModName);
109 }
110 return modulePath;
111 }
112 //---
113}; //namespace pesieve
114
115
117{
118 std::stringstream stream;
119 size_t level = 1;
120
121 if (!process_report.hasAnyShownType(filter)) {
122 return false;
123 }
124 if (!process_report.toJSON(stream, level, filter, jdetails)) {
125 return false;
126 }
127 std::string report_all = stream.str();
128 if (report_all.length() == 0) {
129 return false;
130 }
131 //ensure that the directory is created:
132 this->dumpDir = pesieve::ResultsDumper::makeDirName(process_report.getPid());
133
134 std::ofstream json_report;
135 std::string report_path = makeOutPath("scan_report.json");
137 if (json_report.is_open() == false) {
138 return false;
139 }
140 json_report << report_all << std::endl;
141 if (json_report.is_open()) {
142 json_report.close();
143 return true;
144 }
145 return false;
146}
147
149{
150 if (!process_report.isFilled()) {
151 return false;
152 }
153 std::stringstream stream;
154 size_t level = 1;
155 process_report.toJSON(stream, level);
156 std::string report_all = stream.str();
157 if (report_all.length() == 0) {
158 return false;
159 }
160 //ensure that the directory is created:
161 this->dumpDir = pesieve::ResultsDumper::makeDirName(process_report.getPid());
162
163 std::ofstream json_report;
164 std::string report_path = makeOutPath("dump_report.json");
166 if (json_report.is_open() == false) {
167 return false;
168 }
169 json_report << report_all << std::endl;
170 if (json_report.is_open()) {
171 json_report.close();
172 return true;
173 }
174 return false;
175}
176
178 HANDLE processHandle,
179 bool isRefl,
181 const pesieve::t_dump_mode dump_mode,
182 const t_imprec_mode imprec_mode)
183{
184 if (processHandle == nullptr) {
185 return nullptr;
186 }
188 this->dumpDir = pesieve::ResultsDumper::makeDirName(process_report.getPid());
189
190 std::vector<ModuleScanReport*>::iterator itr;
191 for (itr = process_report.moduleReports.begin();
192 itr != process_report.moduleReports.end();
193 ++itr)
194 {
196 if (mod->status != SCAN_SUSPICIOUS) {
197 continue;
198 }
199 dumpModule(processHandle,
200 isRefl,
201 process_report.modulesInfo,
202 mod,
203 process_report.exportsMap,
204 dump_mode,
205 imprec_mode,
207 );
208 }
209 return dumpReport;
210}
211
213{
214 if (!mod) return false;
215
216 bool filled = false;
217
218 // first try to use cache:
220 if (wsReport && wsReport->data_cache.isFilled()) {
221 filled = module_buf.fillFromBuffer((ULONGLONG)mod->module, wsReport->data_cache);
222 }
223 // if no cache, or loading from cache failed, read from the process memory:
224 if (!filled) {
225 filled = module_buf.readRemote((ULONGLONG)mod->module, mod->moduleSize);
226 }
227 return filled;
228}
229
231 IN bool isRefl,
232 IN const ModulesInfo &modulesInfo,
234 IN const peconv::ExportsMapper *exportsMap,
235 IN const pesieve::t_dump_mode dump_mode,
236 IN const t_imprec_mode imprec_mode,
238)
239{
240 if (!mod) return false;
241
242 const bool save_imp_report = true;
243 bool is_dumped = false;
244
245 peconv::t_pe_dump_mode curr_dump_mode = convert_to_peconv_dump_mode(dump_mode);
246
247 bool dump_shellcode = false;
248 std::string payload_ext = "";
249
250 PeBuffer module_buf(processHandle, isRefl);
251 bool is_corrupt_pe = false;
253 if (artefactReport) {
255 // whenever the artefactReport is available, use it to reconstruct a PE
256 if (artefactReport->has_shellcode) {
257 dump_shellcode = true;
258 }
259 if (artefactReport->has_pe) {
260 ULONGLONG found_pe_base = artefactReport->artefacts.peImageBase();
262 if (!peRec.reconstruct()) {
263 is_corrupt_pe = true;
264 payload_ext = "corrupt_" + payload_ext;
265 if (!this->quiet) {
266 std::cout << "[-] Reconstructing PE at: " << std::hex << (ULONGLONG)found_pe_base << " failed." << std::endl;
267 }
268 }
269 }
270 }
271 // if it is not an artefact report, or reconstructing by artefacts failed, read it from the memory:
272 if (!artefactReport || is_corrupt_pe) {
273 fillModuleCopy(mod, module_buf);
274 }
275 //if no extension selected yet, do it now:
276 if (payload_ext.length() == 0) {
277 payload_ext = module_buf.isValidPe() ? "dll" : "shc";
278 }
279 const std::string module_name = get_module_file_name(processHandle, *mod);
280
281 ModuleDumpReport *modDumpReport = new ModuleDumpReport(module_buf.getModuleBase(), module_buf.getBufferSize());
282 dumpReport.appendReport(modDumpReport);
283
284 modDumpReport->dumpFileName = makeModuleDumpPath(module_buf.getModuleBase(), module_name, payload_ext);
285 modDumpReport->is_corrupt_pe = is_corrupt_pe;
286 modDumpReport->is_shellcode = !module_buf.isValidPe() && module_buf.isCode();
287
288 peconv::ImpsNotCovered notCovered;
289
290 if (module_buf.isFilled()) {
291
292 // Try to fix imports:
294 ImpReconstructor::t_imprec_res imprec_res = impRec.rebuildImportTable(exportsMap, imprec_mode);
295 modDumpReport->impRecMode = get_imprec_res_name(imprec_res);
296
297
298 module_buf.setRelocBase(mod->getRelocBase());
299 if (imprec_mode == pesieve::PE_IMPREC_NONE) {
300 modDumpReport->isDumped = module_buf.dumpPeToFile(modDumpReport->dumpFileName, curr_dump_mode);
301 }
302 else {
303 modDumpReport->isDumped = module_buf.dumpPeToFile(modDumpReport->dumpFileName, curr_dump_mode, exportsMap, &notCovered);
304 }
305
306
307 if (!modDumpReport->isDumped) {
308 modDumpReport->isDumped = module_buf.dumpToFile(modDumpReport->dumpFileName);
309 curr_dump_mode = peconv::PE_DUMP_VIRTUAL;
310 }
314 std::string imports_file = modDumpReport->dumpFileName + ".imports.txt";
315 if (impRec.printFoundIATs(imports_file)) {
316 modDumpReport->impListFileName = imports_file;
317 }
318 }
319 std::string imports_not_rec_file = modDumpReport->dumpFileName + ".not_fixed_imports.txt";
320 if (IATScanReport::saveNotRecovered(imports_not_rec_file, processHandle, nullptr, notCovered, modulesInfo, exportsMap)) {
321 modDumpReport->notRecoveredFileName = imports_not_rec_file;
322 }
323 }
324
325 if (!modDumpReport->isDumped || dump_shellcode)
326 {
327 if (dump_shellcode) {
328 payload_ext = "shc";
329 }
330
331 fillModuleCopy(mod, module_buf);
332
333 modDumpReport = new ModuleDumpReport(module_buf.getModuleBase(), module_buf.getBufferSize());
334 dumpReport.appendReport(modDumpReport);
335
336 modDumpReport->is_shellcode = dump_shellcode;
337 modDumpReport->dumpFileName = makeModuleDumpPath(module_buf.getModuleBase(), module_name, payload_ext);
338 modDumpReport->isDumped = module_buf.dumpToFile(modDumpReport->dumpFileName);
339 curr_dump_mode = peconv::PE_DUMP_VIRTUAL;
341 }
342 if (modDumpReport->isDumped) {
343 is_dumped = true;
344 if (!this->quiet) {
345 std::string mode_info = modDumpReport->mode_info;
346 if (mode_info.length() > 0) mode_info = " as " + mode_info;
347 std::cout << "[*] Dumped module to: " + modDumpReport->dumpFileName + mode_info << "\n";
348 }
349 }
350 else {
351 if (!this->quiet) {
352 std::cerr << "[-] Failed dumping module!" << std::endl;
353 }
354 is_dumped = false;
355 }
356
358 if (codeScanReport) {
359 std::string tags_file = modDumpReport->dumpFileName + ".tag";
360
361 if (codeScanReport->generateTags(tags_file)) {
362 modDumpReport->hooksTagFileName = tags_file;
363 modDumpReport->isReportDumped = true;
364 }
365 }
366
368 if (wsScanReport) {
369 std::string tags_file = modDumpReport->dumpFileName + ".pattern.tag";
370
371 if (wsScanReport->generateTags(tags_file)) {
372 modDumpReport->patternsTagFileName = tags_file;
373 modDumpReport->isReportDumped = true;
374 }
375 }
376
377
379 if (iatHooksReport) {
380 std::string imports_not_rec_file = modDumpReport->dumpFileName + ".iat_hooks.txt";
381
382 if (iatHooksReport->generateList(imports_not_rec_file, processHandle, modulesInfo, exportsMap)) {
383 modDumpReport->iatHooksFileName = imports_not_rec_file;
384 modDumpReport->isReportDumped = true;
385 }
386 }
387 return is_dumped;
388}
389
391{
392 if (!make_dump_dir(this->baseDir)) {
393 this->baseDir = ""; // reset path
394 }
395 std::string inner_dir = this->dumpDir;
396 if (baseDir.length() > 0) {
397 inner_dir = this->baseDir + DIR_SEPARATOR + this->dumpDir;
398 }
399 if (!make_dump_dir(inner_dir)) {
400 this->dumpDir = ""; // reset path
401 }
402 if (baseDir.length() > 0) {
403 stream << baseDir;
405 }
406 if (this->dumpDir.length() > 0) {
407 stream << this->dumpDir;
409 }
410}
411
412std::string pesieve::ResultsDumper::makeModuleDumpPath(ULONGLONG modBaseAddr, const std::string &fname, const std::string &default_extension)
413{
414 std::stringstream stream;
415 makeAndJoinDirectories(stream);
416 stream << std::hex << modBaseAddr;
417 if (fname.length() > 0) {
418 stream << ".";
419 stream << fname;
420 } else {
421 stream << "." << default_extension;
422 }
423 return stream.str();
424}
425
426std::string pesieve::ResultsDumper::makeOutPath(const std::string &fname, const std::string& default_extension)
427{
428 std::stringstream stream;
429 makeAndJoinDirectories(stream);
430
431 if (fname.length() > 0) {
432 stream << fname;
433 }
434 else {
435 stream << std::dec << time(nullptr);
437 }
438 return stream.str();
439}
440
441std::string pesieve::ResultsDumper::makeDirName(const DWORD process_id)
442{
443 std::stringstream stream;
444 stream << "process_";
445 stream << process_id;
446 return stream.str();
447}
A report from the artefacts scan, generated by ArtefactScanner.
A report from the code scan, generated by CodeScanner.
A report from an IAT scan, generated by IATScanner.
Definition iat_scanner.h:12
static bool saveNotRecovered(IN std::string fileName, IN HANDLE hProcess, IN peconv::ImportsCollection *storedFunc, IN peconv::ImpsNotCovered &notCovered, IN const ModulesInfo &modulesInfo, IN const peconv::ExportsMapper *exportsMap)
enum pesieve::ImpReconstructor::imprec_res t_imprec_res
A base class of all the reports detailing on the output of the performed module's scan.
A container of all the process modules that were scanned.
The report aggregating the results of the performed dumps.
Definition dump_report.h:48
The report aggregating the results of the performed scan.
Definition scan_report.h:19
std::string makeModuleDumpPath(ULONGLONG modBaseAddr, const std::string &fname, const std::string &defaultExtension)
std::string makeDirName(const DWORD process_id)
bool fillModuleCopy(IN ModuleScanReport *mod, IN OUT PeBuffer &module_buf)
void makeAndJoinDirectories(std::stringstream &name_stream)
std::string makeOutPath(const std::string &fname, const std::string &defaultExtension="")
ProcessDumpReport * dumpDetectedModules(HANDLE hProcess, bool isRefl, ProcessScanReport &process_report, const pesieve::t_dump_mode dump_mode, const pesieve::t_imprec_mode imprec_mode)
bool dumpModule(IN HANDLE processHandle, IN bool isRefl, IN const ModulesInfo &modulesInfo, IN ModuleScanReport *modReport, IN const peconv::ExportsMapper *exportsMap, IN const pesieve::t_dump_mode dump_mode, IN const pesieve::t_imprec_mode imprec_mode, OUT ProcessDumpReport &dumpReport)
bool dumpJsonReport(ProcessScanReport &process_report, const ProcessScanReport::t_report_filter &filter, const pesieve::t_json_level &jdetails)
A report from the working set scan, generated by WorkingSetScanner.
bool create_dir_recursively(const std::string &path)
Definition path_util.cpp:73
int MAX_PATH
Definition pesieve.py:10
std::string get_imprec_res_name(const ImpReconstructor::t_imprec_res &res)
std::string get_payload_ext(const ArtefactScanReport &artefactRepot)
size_t fill_iat(BYTE *vBuf, size_t vBufSize, IN const peconv::ExportsMapper *exportsMap, IN OUT IATBlock &iat, IN ThunkFoundCallback *callback)
Definition iat_finder.h:31
peconv::t_pe_dump_mode convert_to_peconv_dump_mode(const pesieve::t_dump_mode dump_mode)
std::string get_module_file_name(HANDLE processHandle, const ModuleScanReport &mod)
bool make_dump_dir(const std::string &directory)
std::string get_dump_mode_name(peconv::t_pe_dump_mode dump_mode)
#define DIR_SEPARATOR