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
pe_sieve.cpp
Go to the documentation of this file.
1#include "pe_sieve.h"
2#include <peconv.h>
3
4#include <windows.h>
5#include "scanners/scanner.h"
6
7#include "utils/format_util.h"
14#include "utils/console_color.h"
15#include "color_scheme.h"
16
18
19using namespace pesieve;
20using namespace pesieve::util;
21
22namespace pesieve {
23 void check_access_denied(DWORD processID)
24 {
25 HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID);
26 if (!hProcess) {
27 std::cerr << "-> Access denied. Try to run the scanner as Administrator." << std::endl;
28 return;
29 }
31 switch (level) {
33 std::cerr << "-> Access denied. Could not query the process token." << std::endl;
34 break;
36 std::cerr << "-> Access denied. Could not access the system process." << std::endl;
37 break;
38 default:
39 break;
40 }
41 CloseHandle(hProcess);
42 hProcess = NULL;
43 }
44
45 bool is_scanner_compatible(IN HANDLE hProcess)
46 {
47 BOOL isCurrWow64 = FALSE;
48 is_process_wow64(GetCurrentProcess(), &isCurrWow64);
49
50 BOOL isRemoteWow64 = FALSE;
51 is_process_wow64(hProcess, &isRemoteWow64);
52
53 if (isCurrWow64 && !isRemoteWow64) {
54 return false;
55 }
56 return true;
57 }
58
59 // throws std::runtime_error if opening the process failed
60 HANDLE open_process(DWORD processID, bool reflection, bool quiet)
61 {
62 const DWORD basic_access = SYNCHRONIZE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION;
63 DWORD access = basic_access;
64 if (reflection) {
65 access |= pesieve::util::reflection_access | PROCESS_VM_OPERATION;
66 }
67
68 HANDLE hProcess = OpenProcess(access, FALSE, processID);
69
70 // if failed, try to open with the lower reflection access
71 if (!hProcess && access != basic_access && access != pesieve::util::reflection_access1) {
72 hProcess = OpenProcess(pesieve::util::reflection_access1, FALSE, processID);
73 }
74
75 // if failed, try to open with basic rights
76 if (!hProcess && access != basic_access) {
77 hProcess = OpenProcess( basic_access, FALSE, processID);
78 }
79
80 // check process compatibility
81 if (hProcess && !is_scanner_compatible(hProcess) && !quiet) {
82 util::print_in_color(WARNING_COLOR, "[!] Scanner mismatch! Try to use the 64bit version of the scanner!\n", true);
83 }
84
85 // opening succeeded, return the handle:
86 if (hProcess) {
87 return hProcess;
88 }
89
90 const DWORD last_err = GetLastError();
91
92 if (last_err == ERROR_ACCESS_DENIED) {
93 if (!quiet) {
94 std::cerr << "[-][" << processID << "] Could not open the process Error: " << last_err << std::endl;
95 //print more info:
96 check_access_denied(processID);
97 }
98
99 SetLastError(ERROR_ACCESS_DENIED);
100 throw std::runtime_error("Could not open the process: Access Denied");
101 return nullptr;
102 }
103 if (last_err == ERROR_INVALID_PARAMETER) {
104 if (!quiet) {
105 std::cerr << "-> Is this process still running?" << std::endl;
106 }
107 SetLastError(ERROR_INVALID_PARAMETER);
108 throw std::runtime_error("Could not open the process: Invalid Parameter");
109 }
110 return hProcess;
111 }
112
113 pesieve::ProcessDumpReport* make_dump(IN HANDLE hProcess, IN bool isRefl, IN const pesieve::t_params &args, IN ProcessScanReport &process_report)
114 {
115 if (!hProcess) {
116 return nullptr;
117 }
118 if (args.out_filter == OUT_NO_DIR) {
119 // dumping disabled
120 return nullptr;
121 }
122 ProcessDumpReport* dumpReport = nullptr;
123 ResultsDumper dumper(expand_path(args.output_dir), args.quiet);
124
125 if (dumper.dumpJsonReport(process_report, ProcessScanReport::REPORT_SUSPICIOUS_AND_ERRORS, args.json_lvl) && !args.quiet) {
126 std::cout << "[+] Report dumped to: " << dumper.getOutputDir() << std::endl;
127 }
128
129 if (args.out_filter != OUT_NO_DUMPS) {
130 pesieve::t_dump_mode dump_mode = pesieve::PE_DUMP_AUTO;
131 if (args.dump_mode < peconv::PE_DUMP_MODES_COUNT) {
132 dump_mode = pesieve::t_dump_mode(args.dump_mode);
133 }
134 size_t dumped_modules = 0;
135 dumpReport = dumper.dumpDetectedModules(hProcess, isRefl, process_report, dump_mode, args.imprec_mode);
136 if (dumpReport && dumpReport->countDumped()) {
137 dumped_modules = dumpReport->countDumped();
138 }
139 if (!args.quiet && dumped_modules) {
140 std::cout << "[+] Dumped modified to: " << dumper.getOutputDir() << std::endl;
141 }
142 }
143 if (args.minidump) {
144 pesieve::t_report report = process_report.generateSummary();
145 if (report.suspicious > 0) {
146 if (!args.quiet) {
147 std::cout << "[*] Creating minidump..." << std::endl;
148 }
149 std::string original_path = process_report.mainImagePath;
150 std::string file_name = peconv::get_file_name(original_path);
151 std::string dump_file = dumper.makeOutPath(file_name + ".dmp");
152 if (make_minidump(process_report.getPid(), dump_file)) {
153 if (!dumpReport) {
154 dumpReport = new ProcessDumpReport(process_report.getPid());
155 }
156 dumpReport->minidumpPath = dump_file;
157 if (!args.quiet) {
158 std::cout << "[+] Minidump saved to: " << dumpReport->minidumpPath << std::endl;
159 }
160 }
161 else if (!args.quiet) {
162 std::cout << "[-] Creating minidump failed! " << std::endl;
163 }
164 }
165 }
166 if (dumpReport) {
167 dumpReport->outputDir = dumper.getOutputDir();
168 if (dumper.dumpJsonReport(*dumpReport) && !args.quiet) {
169 std::cout << "[+] Report dumped to: " << dumper.getOutputDir() << std::endl;
170 }
171 }
172 return dumpReport;
173 }
174
175}; //namespace pesieve
176
177
178namespace pesieve {
179
180 inline bool is_by_patterns(const t_shellc_mode& shellc_mode)
181 {
182 switch (shellc_mode) {
183 case SHELLC_PATTERNS:
186 return true;
187 }
188 return false;
189 }
190
191}; // namespace pesieve
192
194{
195 ReportEx *report = new(std::nothrow) ReportEx();
196 if (!report) {
197 // should not happen
198 return nullptr;
199 }
200 HANDLE orig_proc = nullptr; // original process handle
201 HANDLE cloned_proc = nullptr; // process reflection handle
202
203 if (!set_debug_privilege()) {
204 if (!args.quiet) std::cerr << "[-] Could not set debug privilege" << std::endl;
205 }
206
207 if (args.pattern_file.length) {
208 size_t loaded = matcher::load_pattern_file(args.pattern_file.buffer);
209 if (!args.quiet) {
210 if (loaded) std::cout << "[+] Pattern file loaded: " << args.pattern_file.buffer << ", Signs: " << loaded << std::endl;
211 else std::cerr << "[-] Failed to load pattern file: " << args.pattern_file.buffer << std::endl;
212 }
213 }
214 if (is_by_patterns(args.shellcode)) {
216 }
217
218 try {
219 orig_proc = open_process(args.pid, args.make_reflection, args.quiet);
220 HANDLE target_proc = orig_proc;
221
222 if (args.make_reflection) {
223 cloned_proc = make_process_reflection(orig_proc);
224 if (cloned_proc) {
225 target_proc = cloned_proc;
226 }
227 else {
228 if (!args.quiet) std::cerr << "[-] Failed to create the process reflection" << std::endl;
229 }
230 }
231
232 if (!args.quiet) {
233 if (cloned_proc) {
234 std::cout << "[*] Using process reflection!\n";
235 }
236 else {
237 std::cout << "[*] Using raw process!\n";
238 if (args.data == pesieve::PE_DATA_SCAN_INACCESSIBLE || args.data == pesieve::PE_DATA_SCAN_INACCESSIBLE_ONLY) {
239 print_in_color(WARNING_COLOR, "[WARNING] Scanning of inaccessible pages is possible only in reflection mode!\n");
240 }
241 }
242 }
243
244 const bool is_reflection = (cloned_proc) ? true : false;
245 ProcessScanner scanner(target_proc, is_reflection, args);
246 report->scan_report = scanner.scanRemote();
247
248 if (report->scan_report) {
249 // dump elements from the process:
250 report->dump_report = make_dump(target_proc, is_reflection, args, *report->scan_report);
251 }
252 }
253 catch (std::exception &e) {
254 delete report;
255 report = nullptr;
256
257 if (!args.quiet) {
258 util::print_in_color(ERROR_COLOR, std::string("[ERROR] ") + e.what() + "\n", true);
259 }
260 }
261 if (cloned_proc) {
262 release_process_reflection(&cloned_proc);
263 }
264 CloseHandle(orig_proc);
265 return report;
266}
267
268std::string pesieve::info()
269{
270 std::stringstream stream;
271 stream << "Version: " << PESIEVE_VERSION_STR;
272#ifdef _WIN64
273 stream << " (x64)" << "\n";
274#else
275 stream << " (x86)" << "\n";
276#endif
277 stream << "Built on: " << __DATE__ << "\n\n";
278 stream << "~ from hasherezade with love ~\n";
279 stream << "Scans a given process, recognizes and dumps a variety of in-memory implants:\nreplaced/injected PEs, shellcodes, inline hooks, patches etc.\n";
280 stream << "URL: " << PESIEVE_URL << "\n";
281 return stream.str();
282}
The report aggregating the results of the performed dumps.
Definition dump_report.h:48
size_t countDumped() const
Definition dump_report.h:78
The report aggregating the results of the performed scan.
Definition scan_report.h:19
The root scanner, responsible for enumerating all the elements to be scanned within a given process,...
Definition scanner.h:14
ProcessScanReport * scanRemote()
The main function of ProcessScanner, deploying the scan. Throws exceptions in case of a failure.
Definition scanner.cpp:216
The final report about the actions performed on the process: scanning and dumping.
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)
std::string getOutputDir()
bool dumpJsonReport(ProcessScanReport &process_report, const ProcessScanReport::t_report_filter &filter, const pesieve::t_json_level &jdetails)
size_t load_pattern_file(const char *filename)
bool make_minidump(DWORD pid, const std::string &out_file)
process_integrity_t get_integrity_level(HANDLE hProcess)
BOOL is_process_wow64(IN HANDLE processHandle, OUT BOOL *isProcWow64)
std::string expand_path(std::string path)
const DWORD reflection_access1
void print_in_color(int color, const std::string &text, bool is_error=false)
BOOL(CALLBACK *_MiniDumpWriteDump)(HANDLE hProcess
const DWORD reflection_access
bool release_process_reflection(HANDLE *reflection_hndl)
DWORD(__stdcall *_PssCaptureSnapshot)(HANDLE ProcessHandle
HANDLE make_process_reflection(HANDLE orig_hndl)
const char PESIEVE_URL[]
Definition pe_sieve.h:21
void check_access_denied(DWORD processID)
Definition pe_sieve.cpp:23
bool is_by_patterns(const t_shellc_mode &shellc_mode)
Definition pe_sieve.cpp:180
const WORD ERROR_COLOR
Definition color_scheme.h:5
HANDLE open_process(DWORD processID, bool reflection, bool quiet)
Definition pe_sieve.cpp:60
const WORD WARNING_COLOR
Definition color_scheme.h:6
bool is_scanner_compatible(IN HANDLE hProcess)
Definition pe_sieve.cpp:45
pesieve::ProcessDumpReport * make_dump(IN HANDLE hProcess, IN bool isRefl, IN const pesieve::t_params &args, IN ProcessScanReport &process_report)
Definition pe_sieve.cpp:113
std::string info()
The string with the basic information about the scanner.
Definition pe_sieve.cpp:268
ReportEx * scan_and_dump(IN const pesieve::t_params args)
The main action performed by PE-sieve: scanning the process and dumping the detected material.
Definition pe_sieve.cpp:193
The root of the PE-sieve scanner.
@ SHELLC_PATTERNS_OR_STATS
detect shellcodes by patterns or stats (any match)
@ SHELLC_PATTERNS_AND_STATS
detect shellcodes by patterns and stats (both match)
@ SHELLC_PATTERNS
detect shellcodes by patterns
@ OUT_NO_DUMPS
don't dump the modified PEs, but save the report
@ OUT_NO_DIR
don't dump any files
#define PESIEVE_VERSION_STR
Final summary about the scanned process.
DWORD suspicious
general summary of suspicious