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#define DEFAULT_BASE 0x10000000
17//---
18namespace pesieve {
19
20 std::string get_payload_ext(const ArtefactScanReport& artefactRepot)
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 }
94 return util::create_dir_recursively(directory);
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");
136 json_report.open(report_path);
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.empty()) {
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");
165 json_report.open(report_path);
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{
179 std::stringstream stream;
180 size_t level = 1;
181
182 const std::string err_content = err_report_to_json(error_report, filter, level);
183 if (err_content.empty()) {
184 return false;
185 }
186
187 //ensure that the directory is created:
188 this->dumpDir = pesieve::ResultsDumper::makeDirName(error_report.pid);
189
190 std::ofstream json_report;
191 std::string report_path = makeOutPath("error_report.json");
192 json_report.open(report_path);
193 if (json_report.is_open() == false) {
194 return false;
195 }
196 json_report << err_content << std::endl;
197 if (json_report.is_open()) {
198 json_report.close();
199 return true;
200 }
201 return false;
202}
203
205 HANDLE processHandle,
206 bool isRefl,
207 ProcessScanReport &process_report,
208 const pesieve::t_dump_mode dump_mode,
209 const t_imprec_mode imprec_mode,
210 const bool rebase)
211{
212 if (processHandle == nullptr) {
213 return nullptr;
214 }
215 ProcessDumpReport *dumpReport = new ProcessDumpReport(process_report.getPid());
216 this->dumpDir = pesieve::ResultsDumper::makeDirName(process_report.getPid());
217
218 for (auto itr = process_report.moduleReports.begin();
219 itr != process_report.moduleReports.end();
220 ++itr)
221 {
222 ModuleScanReport* mod = *itr;
223 if (mod->status != SCAN_SUSPICIOUS) {
224 continue;
225 }
226 // skip already dumped:
227 if (dumpReport->hasModule((ULONGLONG)mod->module, mod->moduleSize)) {
228 continue;
229 }
230 dumpModule(processHandle,
231 isRefl,
232 process_report.modulesInfo,
233 mod,
234 process_report.exportsMap,
235 dump_mode,
236 imprec_mode,
237 rebase,
238 *dumpReport
239 );
240 }
241 return dumpReport;
242}
243
245{
246 if (!mod) return false;
247
248 bool filled = false;
249
250 // first try to use cache:
251 WorkingSetScanReport* wsReport = dynamic_cast<WorkingSetScanReport*>(mod);
252 if (wsReport && wsReport->data_cache.isFilled()) {
253 filled = module_buf.fillFromBuffer((ULONGLONG)mod->module, wsReport->data_cache);
254 }
255 // if no cache, or loading from cache failed, read from the process memory:
256 if (!filled) {
257 filled = module_buf.readRemote((ULONGLONG)mod->module, mod->moduleSize);
258 }
259 return filled;
260}
261
262bool pesieve::ResultsDumper::dumpModule(IN HANDLE processHandle,
263 IN bool isRefl,
264 IN const ModulesInfo &modulesInfo,
265 IN ModuleScanReport* mod,
266 IN const peconv::ExportsMapper *exportsMap,
267 IN const pesieve::t_dump_mode dump_mode,
268 IN const t_imprec_mode imprec_mode,
269 IN bool rebase,
270 OUT ProcessDumpReport &dumpReport
271)
272{
273 if (!mod) return false;
274
275 const bool save_imp_report = true;
276 bool is_dumped = false;
277
278 peconv::t_pe_dump_mode curr_dump_mode = convert_to_peconv_dump_mode(dump_mode);
279
280 bool dump_shellcode = false;
281 std::string payload_ext = "";
282
283 PeBuffer module_buf(processHandle, isRefl);
284 bool is_corrupt_pe = false;
285 ArtefactScanReport* artefactReport = dynamic_cast<ArtefactScanReport*>(mod);
286 if (artefactReport) {
287 payload_ext = get_payload_ext(*artefactReport);
288 // whenever the artefactReport is available, use it to reconstruct a PE
289 if (artefactReport->has_shellcode) {
290 dump_shellcode = true;
291 }
292 if (artefactReport->has_pe) {
293 ULONGLONG found_pe_base = artefactReport->artefacts.peImageBase();
294 PeReconstructor peRec(artefactReport->artefacts, module_buf);
295 if (!peRec.reconstruct()) {
296 is_corrupt_pe = true;
297 payload_ext = "corrupt_" + payload_ext;
298 if (!this->quiet) {
299 std::cout << "[-] Reconstructing PE at: " << std::hex << (ULONGLONG)found_pe_base << " failed." << std::endl;
300 }
301 }
302 }
303 }
304 // if it is not an artefact report, or reconstructing by artefacts failed, read it from the memory:
305 if (!artefactReport || is_corrupt_pe) {
306 fillModuleCopy(mod, module_buf);
307 }
308 //if no extension selected yet, do it now:
309 if (payload_ext.length() == 0) {
310 payload_ext = module_buf.isValidPe() ? "dll" : "shc";
311 }
312 const std::string module_name = get_module_file_name(processHandle, *mod);
313
314 ModuleDumpReport *modDumpReport = new ModuleDumpReport(module_buf.getModuleBase(), module_buf.getBufferSize());
315 dumpReport.appendReport(modDumpReport);
316
317 modDumpReport->dumpFileName = makeModuleDumpPath(module_buf.getModuleBase(), module_name, payload_ext);
318 modDumpReport->is_corrupt_pe = is_corrupt_pe;
319 modDumpReport->is_shellcode = !module_buf.isValidPe() && module_buf.isCode();
320
321 peconv::ImpsNotCovered notCovered;
322
323 if (module_buf.isFilled()) {
324
325 // Try to fix imports:
326 ImpReconstructor impRec(module_buf);
327 ImpReconstructor::t_imprec_res imprec_res = impRec.rebuildImportTable(exportsMap, imprec_mode);
328 modDumpReport->impRecMode = get_imprec_res_name(imprec_res);
329
330 // Define a base the module should be rebased to:
331 module_buf.setRelocBase(mod->getRelocBase());
332 ULONGLONG out_base = 0;
333 if (rebase) {
334 out_base = mod->origBase;
335 if (!out_base) {
336 out_base = DEFAULT_BASE;
337 }
338 module_buf.setRelocBase(out_base);
339 }
340 if (imprec_mode == pesieve::PE_IMPREC_NONE) {
341 modDumpReport->isDumped = module_buf.dumpPeToFile(modDumpReport->dumpFileName, curr_dump_mode);
342 }
343 else {
344 modDumpReport->isDumped = module_buf.dumpPeToFile(modDumpReport->dumpFileName, curr_dump_mode, exportsMap, &notCovered);
345 }
346
347 if (!modDumpReport->isDumped) {
348 modDumpReport->isDumped = module_buf.dumpToFile(modDumpReport->dumpFileName);
349 curr_dump_mode = peconv::PE_DUMP_VIRTUAL;
350 }
351 if (curr_dump_mode != peconv::PE_DUMP_VIRTUAL && out_base) {
352 modDumpReport->rebasedTo = out_base;
353 }
354 modDumpReport->mode_info = get_dump_mode_name(curr_dump_mode);
355 bool iat_not_rebuilt = (imprec_res == ImpReconstructor::IMP_RECOVERY_ERROR) || (imprec_res == ImpReconstructor::IMP_RECOVERY_NOT_APPLICABLE);
356 if (iat_not_rebuilt || save_imp_report) {
357 std::string imports_file = modDumpReport->dumpFileName + ".imports.txt";
358 if (impRec.printFoundIATs(imports_file)) {
359 modDumpReport->impListFileName = imports_file;
360 }
361 }
362 std::string imports_not_rec_file = modDumpReport->dumpFileName + ".not_fixed_imports.txt";
363 if (IATScanReport::saveNotRecovered(imports_not_rec_file, processHandle, nullptr, notCovered, modulesInfo, exportsMap)) {
364 modDumpReport->notRecoveredFileName = imports_not_rec_file;
365 }
366 }
367
368 if (!modDumpReport->isDumped || dump_shellcode)
369 {
370 if (dump_shellcode) {
371 payload_ext = "shc";
372 }
373
374 fillModuleCopy(mod, module_buf);
375
376 modDumpReport = new ModuleDumpReport(module_buf.getModuleBase(), module_buf.getBufferSize());
377 dumpReport.appendReport(modDumpReport);
378
379 modDumpReport->is_shellcode = dump_shellcode;
380 modDumpReport->dumpFileName = makeModuleDumpPath(module_buf.getModuleBase(), module_name, payload_ext);
381 modDumpReport->isDumped = module_buf.dumpToFile(modDumpReport->dumpFileName);
382 curr_dump_mode = peconv::PE_DUMP_VIRTUAL;
383 modDumpReport->mode_info = get_dump_mode_name(curr_dump_mode);
384 }
385 if (modDumpReport->isDumped) {
386 is_dumped = true;
387 if (!this->quiet) {
388 std::string mode_info = modDumpReport->mode_info;
389 if (mode_info.length() > 0) mode_info = " as " + mode_info;
390 std::cout << "[*] Dumped module to: " + modDumpReport->dumpFileName + mode_info << "\n";
391 }
392 }
393 else {
394 if (!this->quiet) {
395 std::cerr << "[-] Failed dumping module!" << std::endl;
396 }
397 is_dumped = false;
398 }
399
400 pesieve::CodeScanReport *codeScanReport = dynamic_cast<pesieve::CodeScanReport*>(mod);
401 if (codeScanReport) {
402 std::string tags_file = modDumpReport->dumpFileName + ".tag";
403
404 if (codeScanReport->generateTags(tags_file)) {
405 modDumpReport->hooksTagFileName = tags_file;
406 modDumpReport->isReportDumped = true;
407 }
408 }
409
410 pesieve::WorkingSetScanReport* wsScanReport = dynamic_cast<pesieve::WorkingSetScanReport*>(mod);
411 if (wsScanReport) {
412 std::string tags_file = modDumpReport->dumpFileName + ".pattern.tag";
413
414 if (wsScanReport->generateTags(tags_file)) {
415 modDumpReport->patternsTagFileName = tags_file;
416 modDumpReport->isReportDumped = true;
417 }
418 }
419
420
421 IATScanReport* iatHooksReport = dynamic_cast<IATScanReport*>(mod);
422 if (iatHooksReport) {
423 std::string imports_not_rec_file = modDumpReport->dumpFileName + ".iat_hooks.txt";
424
425 if (iatHooksReport->generateList(imports_not_rec_file, processHandle, modulesInfo, exportsMap)) {
426 modDumpReport->iatHooksFileName = imports_not_rec_file;
427 modDumpReport->isReportDumped = true;
428 }
429 }
430 return is_dumped;
431}
432
434{
435 if (!make_dump_dir(this->baseDir)) {
436 this->baseDir = ""; // reset path
437 }
438 std::string inner_dir = this->dumpDir;
439 if (baseDir.length() > 0) {
440 inner_dir = this->baseDir + DIR_SEPARATOR + this->dumpDir;
441 }
442 if (!make_dump_dir(inner_dir)) {
443 this->dumpDir = ""; // reset path
444 }
445 if (baseDir.length() > 0) {
446 stream << baseDir;
447 stream << DIR_SEPARATOR;
448 }
449 if (this->dumpDir.length() > 0) {
450 stream << this->dumpDir;
451 stream << DIR_SEPARATOR;
452 }
453}
454
455std::string pesieve::ResultsDumper::makeModuleDumpPath(ULONGLONG modBaseAddr, const std::string &fname, const std::string &default_extension)
456{
457 std::stringstream stream;
458 makeAndJoinDirectories(stream);
459 stream << std::hex << modBaseAddr;
460 if (fname.length() > 0) {
461 stream << ".";
462 stream << fname;
463 } else {
464 stream << "." << default_extension;
465 }
466 return stream.str();
467}
468
469std::string pesieve::ResultsDumper::makeOutPath(const std::string &fname, const std::string& default_extension)
470{
471 std::stringstream stream;
472 makeAndJoinDirectories(stream);
473
474 if (fname.length() > 0) {
475 stream << fname;
476 }
477 else {
478 stream << std::dec << time(nullptr);
479 stream << default_extension;
480 }
481 return stream.str();
482}
483
484std::string pesieve::ResultsDumper::makeDirName(const DWORD process_id)
485{
486 std::stringstream stream;
487 stream << "process_";
488 stream << process_id;
489 return stream.str();
490}
A report from the artefacts scan, generated by ArtefactScanner.
A report from the code scan, generated by CodeScanner.
size_t generateTags(const std::string &reportPath)
A report from an IAT scan, generated by IATScanner.
Definition iat_scanner.h:12
bool generateList(IN const std::string &fileName, IN HANDLE hProcess, IN const ModulesInfo &modulesInfo, IN const peconv::ExportsMapper *exportsMap)
static bool saveNotRecovered(IN const std::string &fileName, IN HANDLE hProcess, IN peconv::ImportsCollection *storedFunc, IN peconv::ImpsNotCovered &notCovered, IN const ModulesInfo &modulesInfo, IN const peconv::ExportsMapper *exportsMap)
t_imprec_res rebuildImportTable(const IN peconv::ExportsMapper *exportsMap, IN const pesieve::t_imprec_mode &imprec_mode)
bool printFoundIATs(const std::string &reportPath)
enum pesieve::ImpReconstructor::imprec_res t_imprec_res
std::string notRecoveredFileName
Definition dump_report.h:43
std::string patternsTagFileName
Definition dump_report.h:41
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.
void setRelocBase(ULONGLONG reloc_base)
Definition pe_buffer.h:78
size_t getBufferSize() const
Definition pe_buffer.h:39
bool dumpPeToFile(IN std::string dumpFileName, IN OUT peconv::t_pe_dump_mode &dumpMode, IN OPTIONAL const peconv::ExportsMapper *exportsMap=NULL, OUT OPTIONAL peconv::ImpsNotCovered *notCovered=NULL)
ULONGLONG getModuleBase() const
Definition pe_buffer.h:68
bool dumpToFile(IN std::string dumpFileName)
The report aggregating the results of the performed dumps.
Definition dump_report.h:49
virtual bool toJSON(std::stringstream &stream, size_t level) const
bool hasModule(const ULONGLONG modBase, const size_t modSize) const
Definition dump_report.h:92
The report aggregating the results of the performed scan.
Definition scan_report.h:19
virtual const bool toJSON(std::stringstream &stream, size_t level, const t_results_filter &filter, const pesieve::t_json_level &jdetails) const
peconv::ExportsMapper * exportsMap
Definition scan_report.h:98
std::vector< ModuleScanReport * > moduleReports
Definition scan_report.h:97
bool hasAnyShownType(const t_results_filter &filter)
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="")
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, IN bool rebase, OUT ProcessDumpReport &dumpReport)
ProcessDumpReport * dumpDetectedModules(HANDLE hProcess, bool isRefl, ProcessScanReport &process_report, const pesieve::t_dump_mode dump_mode, const t_imprec_mode imprec_mode, const bool rebase)
bool dumpJsonReport(ProcessScanReport &process_report, const t_results_filter &filter, const pesieve::t_json_level &jdetails)
A report from the working set scan, generated by WorkingSetScanner.
size_t generateTags(const std::string &reportPath)
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)
std::string err_report_to_json(const ErrorReport &err_report, t_results_filter filter, size_t start_level=0)
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)
t_results_filter
#define DIR_SEPARATOR
#define DEFAULT_BASE