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
scan_report.cpp
Go to the documentation of this file.
1#include "scan_report.h"
3#include "headers_scanner.h"
4#include "code_scanner.h"
5#include "iat_scanner.h"
7#include "artefact_scanner.h"
8#include "mapping_scanner.h"
9#include "thread_scanner.h"
10
13
14using namespace pesieve;
15using namespace pesieve::util;
16
17namespace pesieve {
18
20 {
21 if (filter == SHOW_ALL) {
22 return true;
23 }
24 if (filter & SHOW_ERRORS) {
25 if (status == SCAN_ERROR) return true;
26 }
27 if (filter & SHOW_SUSPICIOUS) {
28 if (status == SCAN_SUSPICIOUS) return true;
29 }
30 if (filter & SHOW_NOT_SUSPICIOUS) {
31 if (status == SCAN_NOT_SUSPICIOUS) return true;
32 }
33 return false;
34 }
35
36}; //namespace pesieve
37
39{
40 t_report summary = this->generateSummary();
41 t_scan_status aggregated_status = (summary.suspicious > 0) ? SCAN_SUSPICIOUS : SCAN_NOT_SUSPICIOUS;
42 if (is_shown_type(aggregated_status, filter)) {
43 return true;
44 }
45 aggregated_status = (summary.errors > 0) ? SCAN_ERROR : SCAN_NOT_SUSPICIOUS;
46 if (is_shown_type(aggregated_status, filter)) {
47 return true;
48 }
49 return false;
50}
51//----
52
87
89{
90 if (type >= REPORT_TYPES_COUNT) {
91 return 0; //invalid type
92 }
93 size_t counter = 0;
94 std::set<ModuleScanReport*>::iterator itr;
95 for (itr = this->reportsByType[type].begin(); itr != this->reportsByType[type].end(); ++itr) {
98 counter++;
99 }
100 }
101 return counter;
102}
103
105{
106 if (report == nullptr) return;
107
109 if (type >= REPORT_TYPES_COUNT) {
110 return;
111 }
112 this->reportsByType[type].insert(report);
113}
114
116{
117 if (!hasModule((ULONGLONG)module_base)) {
118 return false;
119 }
120 std::set<ModuleScanReport*>::const_iterator itr;
121 for (itr = reportsByType[REPORT_HEADERS_SCAN].begin(); itr != reportsByType[REPORT_HEADERS_SCAN].end(); ++itr) {
122 HeadersScanReport* report = dynamic_cast<HeadersScanReport*>(*itr);
123 if (report && report->module == module_base) {
124 if (report->isHdrReplaced()) {
125 return true;
126 }
127 }
128 }
129 return false;
130}
131
133{
134 size_t replaced = 0;
136
137 std::set<ModuleScanReport*>::iterator itr;
138 for (itr = this->reportsByType[type].begin(); itr != this->reportsByType[type].end(); ++itr) {
139 ModuleScanReport* report = *itr;
141 HeadersScanReport *hdrRep = dynamic_cast<HeadersScanReport*>(report);
142 if (!hdrRep) continue; //it should not happen
143
144 if (hdrRep->isHdrReplaced()) {
145 replaced++;
146 }
147 }
148 }
149 return replaced;
150}
151
153{
154 t_report summary = { 0 };
155 summary.pid = this->pid;
156 summary.is_64bit = this->is64bit;
157 summary.is_managed = this->isManaged;
158 summary.is_reflection = this->isReflection;
159 summary.errors = static_cast<DWORD>(this->errorsCount);
160 summary.skipped = static_cast<DWORD>(this->reportsByType[REPORT_SKIPPED_SCAN].size());
161 summary.scanned = static_cast<DWORD>(this->reportsByType[REPORT_HEADERS_SCAN].size());
162
163 std::vector<ModuleScanReport*>::const_iterator itr = moduleReports.begin();
164 for (; itr != moduleReports.end(); ++itr) {
165 ModuleScanReport* report = *itr;
167 summary.suspicious++;
168 }
169 }
170 summary.replaced = MASK_TO_DWORD(countHdrsReplaced());
175 summary.implanted = MASK_TO_DWORD(summary.implanted_shc + summary.implanted_pe);
176 summary.hdr_mod = MASK_TO_DWORD(countSuspiciousPerType(REPORT_HEADERS_SCAN) - summary.replaced);
178 summary.other = MASK_TO_DWORD(summary.suspicious - (summary.patched + summary.replaced + summary.implanted + summary.hdr_mod + summary.iat_hooked));
179 return summary;
180}
181
182std::string pesieve::ProcessScanReport::listModules(size_t level, const pesieve::t_results_filter &filter, const t_json_level &jdetails) const
183{
184 std::stringstream stream;
185 //summary:
186 OUT_PADDED(stream, level, "\"scans\" : [\n");
187 bool is_first = true;
188 std::vector<ModuleScanReport*>::const_iterator itr;
189 for (itr = this->moduleReports.begin(); itr != this->moduleReports.end(); ++itr) {
190 ModuleScanReport *mod = *itr;
191 if (is_shown_type(mod->status, filter)) {
192 if (!is_first) {
193 stream << ",\n";
194 }
195 OUT_PADDED(stream, level + 1, "{\n");
196 mod->toJSON(stream, level + 2, jdetails);
197 stream << "\n";
198 OUT_PADDED(stream, level + 1, "}");
199 is_first = false;
200 }
201 }
202 if (moduleReports.size()) {
203 stream << "\n";
204 }
205 OUT_PADDED(stream, level, "]\n");
206 return stream.str();
207}
208
210 std::stringstream &stream, size_t start_level,
211 const pesieve::t_results_filter &filter,
212 const pesieve::t_json_level &jdetails) const
213{
214 const t_report report = this->generateSummary();
215 //summary:
216 size_t other = report.other;
217 size_t level = start_level + 1;
218 OUT_PADDED(stream, start_level, "{\n"); // beginning of the report
219
220 OUT_PADDED(stream, level, "\"pid\" : ");
221 stream << std::dec << report.pid << ",\n";
222 OUT_PADDED(stream, level, "\"is_64_bit\" : ");
223 stream << std::dec << report.is_64bit << ",\n";
224 OUT_PADDED(stream, level, "\"is_managed\" : ");
225 stream << std::dec << report.is_managed << ",\n";
226 OUT_PADDED(stream, level, "\"main_image_path\" : \"");
227 stream << escape_path_separators(this->mainImagePath) << "\",\n";
228 OUT_PADDED(stream, level, "\"used_reflection\" : ");
229 stream << std::dec << report.is_reflection << ",\n";
230 OUT_PADDED(stream, level, "\"scanner_version\" : ");
231 stream << "\"" << PESIEVE_VERSION_STR << "\",\n";
232 OUT_PADDED(stream, level, "\"scanned\" : \n");
233 OUT_PADDED(stream, level, "{\n");
234 //stream << " {\n";
235 OUT_PADDED(stream, level + 1, "\"total\" : ");
236 stream << std::dec << report.scanned << ",\n";
237 OUT_PADDED(stream, level + 1, "\"skipped\" : ");
238 stream << std::dec << report.skipped << ",\n";
239 OUT_PADDED(stream, level + 1, "\"modified\" : \n");
240 OUT_PADDED(stream, level + 1, "{\n");
241 //stream << " {\n";
242 OUT_PADDED(stream, level + 2, "\"total\" : ");
243 stream << std::dec << report.suspicious << ",\n";
244 OUT_PADDED(stream, level + 2, "\"patched\" : ");
245 stream << std::dec << report.patched << ",\n";
246 OUT_PADDED(stream, level + 2, "\"iat_hooked\" : ");
247 stream << std::dec << report.iat_hooked << ",\n";
248 OUT_PADDED(stream, level + 2, "\"replaced\" : ");
249 stream << std::dec << report.replaced << ",\n";
250 OUT_PADDED(stream, level + 2, "\"hdr_modified\" : ");
251 stream << std::dec << report.hdr_mod << ",\n";
252 OUT_PADDED(stream, level + 2, "\"implanted_pe\" : ");
253 stream << std::dec << report.implanted_pe << ",\n";
254 OUT_PADDED(stream, level + 2, "\"implanted_shc\" : ");
255 stream << std::dec << report.implanted_shc << ",\n";
256 OUT_PADDED(stream, level + 2, "\"unreachable_file\" : ");
257 stream << std::dec << report.unreachable_file << ",\n";
258 OUT_PADDED(stream, level + 2, "\"other\" : ");
259 stream << std::dec << other << "\n";
260 OUT_PADDED(stream, level + 1, "},\n"); // modified
261 OUT_PADDED(stream, level + 1, "\"errors\" : ");
262 stream << std::dec << report.errors << "\n";
263 OUT_PADDED(stream, level, "},\n"); // scanned
264 stream << listModules(level, filter, jdetails);
265
266 OUT_PADDED(stream, start_level, "}"); // end of the report
267 return true;
268}
269
A report from the artefacts scan, generated by ArtefactScanner.
A report from the code scan, generated by CodeScanner.
static t_scan_status get_scan_status(const ElementScanReport *report)
A report from the headers scan, generated by HeadersScanner.
A report from an IAT scan, generated by IATScanner.
Definition iat_scanner.h:12
A base class of all the reports detailing on the output of the performed module's scan.
virtual const bool toJSON(std::stringstream &outs, size_t level=JSON_LEVEL, const pesieve::t_json_level &jdetails=JSON_BASIC)=0
size_t countResultsPerType(const t_report_type type, const t_scan_status result) const
virtual const bool toJSON(std::stringstream &stream, size_t level, const t_results_filter &filter, const pesieve::t_json_level &jdetails) const
bool isModuleReplaced(HMODULE module_base)
pesieve::t_report generateSummary() const
std::string listModules(size_t level, const t_results_filter &filter, const t_json_level &jdetails) const
std::set< ModuleScanReport * > reportsByType[REPORT_TYPES_COUNT]
std::vector< ModuleScanReport * > moduleReports
Definition scan_report.h:97
bool hasModule(ULONGLONG page_addr)
Definition scan_report.h:67
static t_report_type getReportType(ModuleScanReport *report)
size_t countHdrsReplaced() const
bool hasAnyShownType(const t_results_filter &filter)
void appendToType(ModuleScanReport *report)
size_t countSuspiciousPerType(const t_report_type type) const
A report from the thread scan, generated by ThreadScanner.
A report from the working set scan, generated by WorkingSetScanner.
#define OUT_PADDED(stream, field_size, str)
Definition format_util.h:12
#define MASK_TO_DWORD(val)
Definition iat_finder.h:9
DWORD(__stdcall *_PssCaptureSnapshot)(HANDLE ProcessHandle
std::string escape_path_separators(std::string path)
Definition path_util.cpp:27
enum pesieve::module_scan_status t_scan_status
bool is_shown_type(t_scan_status status, t_results_filter filter)
@ SHOW_ERRORS
report only scan errors
@ SHOW_SUSPICIOUS
report only suspicious
@ SHOW_NOT_SUSPICIOUS
report only not suspicious
@ SHOW_ALL
#define PESIEVE_VERSION_STR
Final summary about the scanned process.
DWORD errors
the number of elements that could not be scanned because of errors. If errors == ERROR_SCAN_FAILURE,...
DWORD implanted_shc
implanted shellcodes
bool is_reflection
was the scan performed on process reflection
DWORD scanned
number of all scanned modules
DWORD patched
detected modifications in the code
DWORD suspicious
general summary of suspicious
bool is_64bit
is process 64 bit
DWORD iat_hooked
detected IAT hooks
DWORD hdr_mod
PE header is modified (but not replaced).
DWORD unreachable_file
cannot read the file corresponding to the module in memory
DWORD implanted_pe
the full PE was probably loaded manually
DWORD skipped
some of the modules must be skipped (i.e. dotNET managed code have different characteristics and this...
DWORD replaced
PE file replaced in memory (probably hollowed).
bool is_managed
is process managed (.NET)
DWORD other
other indicators
DWORD pid
pid of the process that was scanned