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