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
38bool pesieve::ProcessScanReport::hasAnyShownType(const pesieve::t_results_filter &filter)
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;
135 const t_report_type type = REPORT_HEADERS_SCAN;
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 }
170 summary.errors++;
171 }
172 }
173 summary.replaced = MASK_TO_DWORD(countHdrsReplaced());
174 summary.patched = MASK_TO_DWORD(countSuspiciousPerType(REPORT_CODE_SCAN));
175 summary.iat_hooked = MASK_TO_DWORD(countSuspiciousPerType(REPORT_IAT_SCAN));
176 summary.implanted_shc = MASK_TO_DWORD(countSuspiciousPerType(REPORT_MEMPAGE_SCAN) + countSuspiciousPerType(REPORT_THREADS_SCAN));
177 summary.implanted_pe = MASK_TO_DWORD(countSuspiciousPerType(REPORT_ARTEFACT_SCAN));
178 summary.implanted = MASK_TO_DWORD(summary.implanted_shc + summary.implanted_pe);
179 summary.hdr_mod = MASK_TO_DWORD(countSuspiciousPerType(REPORT_HEADERS_SCAN) - summary.replaced);
180 summary.unreachable_file = MASK_TO_DWORD(countSuspiciousPerType(REPORT_UNREACHABLE_SCAN) + countResultsPerType(REPORT_UNREACHABLE_SCAN, pesieve::SCAN_ERROR));
181 summary.other = MASK_TO_DWORD(summary.suspicious - (summary.patched + summary.replaced + summary.implanted + summary.hdr_mod + summary.iat_hooked));
182 return summary;
183}
184
185std::string pesieve::ProcessScanReport::listModules(size_t level, const pesieve::t_results_filter &filter, const t_json_level &jdetails) const
186{
187 std::stringstream stream;
188 //summary:
189 OUT_PADDED(stream, level, "\"scans\" : [\n");
190 bool is_first = true;
191 std::vector<ModuleScanReport*>::const_iterator itr;
192 for (itr = this->moduleReports.begin(); itr != this->moduleReports.end(); ++itr) {
193 ModuleScanReport *mod = *itr;
194 if (is_shown_type(mod->status, filter)) {
195 if (!is_first) {
196 stream << ",\n";
197 }
198 OUT_PADDED(stream, level + 1, "{\n");
199 mod->toJSON(stream, level + 2, jdetails);
200 stream << "\n";
201 OUT_PADDED(stream, level + 1, "}");
202 is_first = false;
203 }
204 }
205 if (moduleReports.size()) {
206 stream << "\n";
207 }
208 OUT_PADDED(stream, level, "]\n");
209 return stream.str();
210}
211
213 std::stringstream &stream, size_t start_level,
214 const pesieve::t_results_filter &filter,
215 const pesieve::t_json_level &jdetails) const
216{
217 const t_report report = this->generateSummary();
218 //summary:
219 size_t other = report.other;
220 size_t level = start_level + 1;
221 OUT_PADDED(stream, start_level, "{\n"); // beginning of the report
222
223 OUT_PADDED(stream, level, "\"pid\" : ");
224 stream << std::dec << report.pid << ",\n";
225 OUT_PADDED(stream, level, "\"is_64_bit\" : ");
226 stream << std::dec << report.is_64bit << ",\n";
227 OUT_PADDED(stream, level, "\"is_managed\" : ");
228 stream << std::dec << report.is_managed << ",\n";
229 OUT_PADDED(stream, level, "\"main_image_path\" : \"");
230 stream << escape_path_separators(this->mainImagePath) << "\",\n";
231 OUT_PADDED(stream, level, "\"used_reflection\" : ");
232 stream << std::dec << report.is_reflection << ",\n";
233 OUT_PADDED(stream, level, "\"scanner_version\" : ");
234 stream << "\"" << PESIEVE_VERSION_STR << "\",\n";
235 OUT_PADDED(stream, level, "\"scanned\" : \n");
236 OUT_PADDED(stream, level, "{\n");
237 //stream << " {\n";
238 OUT_PADDED(stream, level + 1, "\"total\" : ");
239 stream << std::dec << report.scanned << ",\n";
240 OUT_PADDED(stream, level + 1, "\"skipped\" : ");
241 stream << std::dec << report.skipped << ",\n";
242 OUT_PADDED(stream, level + 1, "\"modified\" : \n");
243 OUT_PADDED(stream, level + 1, "{\n");
244 //stream << " {\n";
245 OUT_PADDED(stream, level + 2, "\"total\" : ");
246 stream << std::dec << report.suspicious << ",\n";
247 OUT_PADDED(stream, level + 2, "\"patched\" : ");
248 stream << std::dec << report.patched << ",\n";
249 OUT_PADDED(stream, level + 2, "\"iat_hooked\" : ");
250 stream << std::dec << report.iat_hooked << ",\n";
251 OUT_PADDED(stream, level + 2, "\"replaced\" : ");
252 stream << std::dec << report.replaced << ",\n";
253 OUT_PADDED(stream, level + 2, "\"hdr_modified\" : ");
254 stream << std::dec << report.hdr_mod << ",\n";
255 OUT_PADDED(stream, level + 2, "\"implanted_pe\" : ");
256 stream << std::dec << report.implanted_pe << ",\n";
257 OUT_PADDED(stream, level + 2, "\"implanted_shc\" : ");
258 stream << std::dec << report.implanted_shc << ",\n";
259 OUT_PADDED(stream, level + 2, "\"unreachable_file\" : ");
260 stream << std::dec << report.unreachable_file << ",\n";
261 OUT_PADDED(stream, level + 2, "\"other\" : ");
262 stream << std::dec << other << "\n";
263 OUT_PADDED(stream, level + 1, "},\n"); // modified
264 OUT_PADDED(stream, level + 1, "\"errors\" : ");
265 stream << std::dec << report.errors << "\n";
266 OUT_PADDED(stream, level, "},\n"); // scanned
267 stream << listModules(level, filter, jdetails);
268
269 OUT_PADDED(stream, start_level, "}"); // end of the report
270 return true;
271}
272
A report from the artefacts scan, generated by ArtefactScanner.
A report from the code scan, generated by CodeScanner.
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
static t_scan_status get_scan_status(const ModuleScanReport *report)
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
static t_report_type getReportType(ModuleScanReport *report)
size_t countHdrsReplaced() const
bool hasAnyShownType(const t_results_filter &filter)
void appendToType(ModuleScanReport *report)
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)
t_results_filter
@ SHOW_ERRORS
@ SHOW_SUSPICIOUS
@ SHOW_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