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 {
22 return true;
23 }
25 if (status == SCAN_ERROR) return true;
26 }
28 if (status == SCAN_SUSPICIOUS) return true;
29 }
31 if (status == SCAN_NOT_SUSPICIOUS) return true;
32 }
33 return false;
34 }
35
36}; //namespace pesieve
37
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) {
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) {
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
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) {
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,
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.
static t_scan_status get_scan_status(const ModuleScanReport *report)
bool hasAnyShownType(const ProcessScanReport::t_report_filter &filter)
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_report_filter &filter, const pesieve::t_json_level &jdetails) const
bool isModuleReplaced(HMODULE module_base)
pesieve::t_report generateSummary() const
static t_report_type getReportType(ModuleScanReport *report)
size_t countHdrsReplaced() const
void appendToType(ModuleScanReport *report)
std::string listModules(size_t level, const ProcessScanReport::t_report_filter &filter, const t_json_level &jdetails) 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
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
bool is_shown_type(t_scan_status status, ProcessScanReport::t_report_filter filter)
enum pesieve::module_scan_status t_scan_status
#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