HollowsHunter
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
etw_listener.cpp
Go to the documentation of this file.
1#include "etw_listener.h"
2#ifdef __USE_ETW__
3
4#include "hh_scanner.h"
5
6#include <string>
7#include <thread>
8#include <mutex>
9
10#include "util/process_util.h"
11#include "term_util.h"
12
13#define EXECUTABLE_FLAGS (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)
14#define MAX_PROCESSES 65536
15
16
17// Global var for ETW thread
18
19struct ProceesStat
20{
21 time_t startTime;
22 time_t cooldown;
23 time_t lastScanStart;
24 time_t lastScanEnd;
25 std::thread* thread;
26
27 void init()
28 {
29 startTime = 0;
30 cooldown = 0;
31 lastScanStart = lastScanEnd = 0;
32 thread = nullptr;
33 }
34
35 void setProcessStart()
36 {
37 time_t now = 0;
38 time(&now);
39 startTime = now;
40 }
41
42 void resetCooldown()
43 {
44 cooldown = 0;
45 }
46
47 void cleanupThread()
48 {
49 if (thread) {
50#ifdef _DEBUG
51 std::cout << std::dec << "Deleting thread: " << thread->get_id() << std::endl;
52#endif
53 if (thread->joinable()) {
54 thread->join();
55 }
56 delete thread;
57 thread = nullptr;
58 }
59 }
60
61};
62
63ProceesStat procStats[MAX_PROCESSES] = { 0 };
64time_t g_initTime = 0;
65
66// ETW Handler
67// To filter our events, we want to compare against the
68// event opcode. For kernel traces, you can consult this page
69// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364083(v=vs.85).aspx
70//
71// The documentation specific to the image load provider is here:
72// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364068(v=vs.85).aspx
73
74
75bool isDelayedLoad(std::uint32_t pid)
76{
77 if (!procStats[pid].startTime) true;
78
79 time_t now = 0;
80 time(&now);
81 if (now - procStats[pid].startTime > 1) {
82 return true;
83 }
84 return false;
85}
86
87
88bool isCooldown(std::uint32_t pid)
89{
90 if (procStats[pid].cooldown)
91 {
92 time_t now = 0;
93 time(&now);
94
95 if (now - procStats[pid].cooldown > 1)
96 procStats[pid].resetCooldown();
97 else {
98 //std::cout << "Skipping scan for: " << pid << "is in cooldown" << std::endl;
99 return false;
100 }
101 }
102 return true;
103}
104
105void updateCooldown(std::uint32_t pid)
106{
107 if (procStats[pid].cooldown)
108 {
109 time(&procStats[pid].cooldown);
110 }
111}
112
113
114bool isAllocationExecutable(std::uint32_t pid, LPVOID baseAddress)
115{
116 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION, FALSE, pid);
117 if (!hProcess) return false;
118
119 bool isExec = false;
120
121 bool stop = false;
122 PVOID base = 0;
123 LPVOID addr = baseAddress;
124 MEMORY_BASIC_INFORMATION mbi = { 0 };
125
126 do
127 {
128 if (VirtualQueryEx(hProcess, addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) && mbi.AllocationBase)
129 {
130 // Start of the allocation
131 if (!base)
132 {
133 base = mbi.AllocationBase;
134 }
135
136 // Still within?
137 if (base == mbi.AllocationBase)
138 {
139 if (mbi.AllocationProtect & EXECUTABLE_FLAGS || mbi.Protect & EXECUTABLE_FLAGS)
140 {
141 if (!g_hh_args.quiet) {
142 const std::lock_guard<std::mutex> lock(g_stdOutMutex);
143 std::cout << "New Executable Section: " << " (" << pid << ") 0x" << std::hex << addr << " Flags=[Alloc: " << mbi.AllocationProtect << " | Now: " << mbi.Protect << "] " << std::dec << std::endl;
144 }
145 isExec = true;
146 }
147
148 // Move to next block
149 addr = static_cast<char*>(addr) + mbi.RegionSize;
150 }
151 else
152 stop = true;
153 }
154 else
155 stop = true;
156
157 } while (!stop && !isExec);
158
159 CloseHandle(hProcess);
160 return isExec;
161}
162
163
164inline std::wstring getProcessName(const DWORD pid)
165{
166 WCHAR processName[MAX_PATH] = { 0 };
167 if (!process_util::get_process_path(pid, processName, MAX_PATH * 2)) {
168 return L"";
169 }
170 std::wstring pName(processName);
171 std::transform(pName.begin(), pName.end(), pName.begin(), tolower);
172 std::size_t found = pName.find_last_of(L"/\\");
173 if (found == (-1) || found >= pName.length()) {
174 return pName;
175 }
176 return pName.substr(found + 1);
177}
178
179
180bool isWatchedPid(const DWORD pid)
181{
182 // get process name:
183 const std::wstring wImgFileName = getProcessName(pid);
184 const t_single_scan_status res = HHScanner::shouldScanProcess(g_hh_args, g_initTime, pid, wImgFileName.c_str());
185 if (res == SSCAN_READY) {
186 return true;
187 }
188 return false;
189}
190
191bool isWatchedName(const std::string& imgFileName)
192{
193 const std::wstring wImgFileName(imgFileName.begin(), imgFileName.end());
194 const t_single_scan_status res = HHScanner::shouldScanProcess(g_hh_args, g_initTime, 0, wImgFileName.c_str());
195 if (res == SSCAN_READY) {
196 return true;
197 }
198 return false;
199}
200
201// The function we want to execute on the new thread.
202void runHHinNewThread(t_hh_params args)
203{
204 if (!args.pids_list.size()) {
205 return;
206 }
207 long pid = *(args.pids_list.begin());
208 HHScanner hhunter(args, g_initTime);
209 HHScanReport* report = hhunter.scan();
210 if (report)
211 {
212 // in this mode only suspicious will be reported
213 if (!g_hh_args.quiet || report->countReports(pesieve::SHOW_SUSPICIOUS)) {
214 const std::lock_guard<std::mutex> lock(g_stdOutMutex);
215 hhunter.summarizeScan(report, pesieve::SHOW_SUSPICIOUS);
216 }
217 else {
218 hhunter.writeToLog(report);
219 }
220 delete report;
221 }
222 time_t now = 0;
223 time(&now);
224 procStats[pid].lastScanEnd = now;
225}
226
227
228void runHHScan(std::uint32_t pid)
229{
230 static std::mutex mutx;
231 const std::lock_guard<std::mutex> lock(mutx);
232
233 time_t now = 0;
234 time(&now);
235
236 bool shouldScan = false;
237 if (procStats[pid].lastScanStart == 0 ||
238 (procStats[pid].lastScanEnd != 0 && (now - procStats[pid].lastScanEnd) > 1)) {
239 shouldScan = true;
240 }
241 if (!shouldScan) {
242#ifdef _DEBUG
243 const std::lock_guard<std::mutex> stdOutLock(g_stdOutMutex);
244 std::cout << std::dec << pid << " : " << now << ": Skipping the scan...\n";
245#endif
246 return;
247 }
248 procStats[pid].lastScanStart = now;
249 procStats[pid].lastScanEnd = 0;
250
251 t_hh_params args = g_hh_args;
252 // during the current scan use only a single PID
253 args.pids_list.clear();
254 args.names_list.clear();
255 args.pids_list.insert(pid);
256
257 procStats[pid].cleanupThread();
258 procStats[pid].thread = new std::thread(runHHinNewThread, args);
259#ifdef _DEBUG
260 {
261 const std::lock_guard<std::mutex> stdOutLock(g_stdOutMutex);
262 std::cout << std::dec << pid << " : Running a new thread: " << procStats[pid].thread->get_id() << std::endl;
263 }
264#endif
265}
266
267void printAllProperties(krabs::parser &parser)
268{
269 for (krabs::property& prop : parser.properties()) {
270 std::wcout << prop.name() << "\n";
271 }
272}
273
274std::string ipv4FromDword(DWORD ip_dword)
275{
276 std::ostringstream oss;
277 BYTE* ip_bytes = (BYTE*)&ip_dword;
278 const size_t chunks = sizeof(DWORD);
279 for (int i = 0; i < chunks; i++) {
280 oss << std::dec << (unsigned int)ip_bytes[i];
281 if (i < (chunks - 1))
282 oss << ".";
283 }
284 return oss.str();
285}
286
287bool ETWstart(ETWProfile& settings)
288{
289 krabs::kernel_trace trace(L"HollowsHunter");
290 g_initTime = time(NULL);
291
292 krabs::kernel::process_provider processProvider;
293 krabs::kernel::image_load_provider imageLoadProvider;
294 krabs::kernel::virtual_alloc_provider virtualAllocProvider;
295 krabs::kernel::network_tcpip_provider tcpIpProvider;
296 krabs::kernel::object_manager_provider objectMgrProvider;
297
298 // Process Start Trigger
299 processProvider.add_on_event_callback([](const EVENT_RECORD& record, const krabs::trace_context& trace_context)
300 {
301 const int OPCODE_START = 0x1;
302 const int OPCODE_STOP = 0x2;
303 krabs::schema schema(record, trace_context.schema_locator);
304 if (schema.event_opcode() == OPCODE_STOP) {
305 krabs::parser parser(schema);
306 std::uint32_t pid = parser.parse<std::uint32_t>(L"ProcessId");
307 procStats[pid].cleanupThread();
308 }
309 if (schema.event_opcode() == OPCODE_START)
310 {
311 krabs::parser parser(schema);
312 std::uint32_t parentPid = parser.parse<std::uint32_t>(L"ParentId");
313
314 std::string filename = parser.parse<std::string>(L"ImageFileName");
315 if (isWatchedName(filename)) {
316 std::uint32_t pid = parser.parse<std::uint32_t>(L"ProcessId");
317 // New process, reset stats
318 procStats[pid].cleanupThread();
319 procStats[pid].init();
320 procStats[pid].setProcessStart();
321 if (!g_hh_args.quiet) {
322 const std::lock_guard<std::mutex> stdOutLock(g_stdOutMutex);
323 std::cout << std::dec << time(NULL) << " : New Process: " << filename << " (" << pid << ") Parent: " << parentPid << std::endl;
324 }
325 runHHScan(pid);
326 }
327
328 if (isWatchedPid(parentPid)) {
329 runHHScan(parentPid);
330 }
331 }
332 });
333
334 imageLoadProvider.add_on_event_callback([](const EVENT_RECORD& record, const krabs::trace_context& trace_context)
335 {
336 krabs::schema schema(record, trace_context.schema_locator);
337 //std::wcout << schema.task_name() << " : Opcode : " << schema.opcode_name() << " : " << std::dec << schema.event_opcode() << "\n";
338 if (schema.event_opcode() == 10) { // Load
339 krabs::parser parser(schema);
340 std::uint32_t pid = parser.parse<std::uint32_t>(L"ProcessId");
341 if (!isWatchedPid(pid)) return;
342
343 std::wstring filename = parser.parse<std::wstring>(L"FileName");
344 if (!isDelayedLoad(pid)) {
345#ifdef _DEBUG
346 const std::lock_guard<std::mutex> stdOutLock(g_stdOutMutex);
347 std::wcout << " LOADING " << std::dec << pid << " : " << time(NULL) << " : IMAGE:" << filename << std::endl;
348#endif
349 return;
350 }
351 if (!g_hh_args.quiet) {
352 const std::lock_guard<std::mutex> stdOutLock(g_stdOutMutex);
353 std::wcout << std::dec << pid << " : " << time(NULL) << " : IMAGE:" << filename << std::endl;
354 }
355 runHHScan(pid);
356 }
357 });
358
359 tcpIpProvider.add_on_event_callback([](const EVENT_RECORD& record, const krabs::trace_context& trace_context)
360 {
361 krabs::schema schema(record, trace_context.schema_locator);
362 krabs::parser parser(schema);
363 std::uint32_t pid = parser.parse<std::uint32_t>(L"PID");
364 if (!isWatchedPid(pid)) return;
365
366 krabs::ip_address daddr = parser.parse<krabs::ip_address>(L"daddr");
367
368
369 if (!g_hh_args.quiet) {
370 const std::lock_guard<std::mutex> stdOutLock(g_stdOutMutex);
371 std::wcout << std::dec << pid << " : " << schema.task_name() << " : " << schema.opcode_name();
372 if (!daddr.is_ipv6) {
373 long ipv4 = daddr.v4;
374 std::cout << " -> " << ipv4FromDword(ipv4);
375 }
376 std::wcout <<"\n";
377 }
378 runHHScan(pid);
379 });
380
381 objectMgrProvider.add_on_event_callback([](const EVENT_RECORD& record, const krabs::trace_context& trace_context)
382 {
383 krabs::schema schema(record, trace_context.schema_locator);
384 if (schema.event_opcode() == 34) // DuplicateHandle
385 {
386 krabs::parser parser(schema);
387 std::uint32_t pid = parser.parse<std::uint32_t>(L"TargetProcessId");
388 if (!isWatchedPid(pid)) return;
389
390 if (!g_hh_args.quiet) {
391 const std::lock_guard<std::mutex> stdOutLock(g_stdOutMutex);
392 std::wcout << std::dec << pid << " : " << schema.task_name() << " : " << schema.opcode_name() << "\n";
393 }
394 runHHScan(pid);
395 }
396 });
397 // Process VirtualAlloc Trigger
398 virtualAllocProvider.add_on_event_callback([](const EVENT_RECORD& record, const krabs::trace_context& trace_context)
399 {
400 krabs::schema schema(record, trace_context.schema_locator);
401 if (schema.event_opcode() == 98) // VirtualAlloc
402 {
403 krabs::parser parser(schema);
404 std::uint32_t targetPid = parser.parse<std::uint32_t>(L"ProcessId");
405 if (!isWatchedPid(targetPid)) return;
406
407 if (!isCooldown(targetPid)) return;
408
409 bool doScan = false;
410 LPVOID baseAddress = parser.parse<LPVOID>(L"BaseAddress");
411
412 if (targetPid)
413 {
414 doScan = isAllocationExecutable(targetPid, baseAddress);
415 }
416
417 if (doScan)
418 {
419 updateCooldown(targetPid);
420 runHHScan(targetPid);
421 }
422 }
423 });
424
425 bool isOk = true;
426 if (settings.tcpip) trace.enable(tcpIpProvider);
427 if (settings.obj_mgr) trace.enable(objectMgrProvider);
428 if (settings.process_start) trace.enable(processProvider);
429 if (settings.img_load) trace.enable(imageLoadProvider);
430 if (settings.allocation) trace.enable(virtualAllocProvider);
431 try {
432 std::cout << "Starting listener..." << std::endl;
433 trace.start();
434 }
435 catch (std::runtime_error& err) {
436 std::cerr << "[ERROR] " << err.what() << std::endl;
437 isOk = false;
438 }
439 return isOk;
440}
441
442#endif // __USE_ETW__
size_t countReports(const pesieve::t_results_filter rfilter) const
Definition hh_report.h:39
static t_single_scan_status shouldScanProcess(const hh_params &hh_args, const time_t hh_initTime, const DWORD pid, const WCHAR *exe_file)
t_hh_params g_hh_args
Definition main.cpp:29
@ SSCAN_READY
Definition hh_scanner.h:21
enum single_status t_single_scan_status
bool get_process_path(DWORD processID, WCHAR *szProcessName, size_t processNameSize)
bool allocation
Definition etw_settings.h:9
bool process_start
Definition etw_settings.h:7
bool img_load
Definition etw_settings.h:8
std::set< long > pids_list
Definition hh_params.h:34
bool quiet
Definition hh_params.h:28
std::set< std::wstring > names_list
Definition hh_params.h:33
std::mutex g_stdOutMutex
Definition term_util.cpp:9