### Example Usage of Process::add_detection Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Illustrates how to create and add a `thread_detection` to a `process` object using its thread-safe `add_detection` method. This is typically done within a scan function when an indicator of compromise (IOC) is found. ```cpp thread_detection detection; detection.name = L"Suspicious Activity"; detection.description = L"Description of the IOC"; detection.tid = thread->tid; detection.severity = hsb::containers::detections::HIGH; process->add_detection(detection); ``` -------------------------------- ### Display Help Message Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Display the help message for available command-line arguments. ```bash Hunt-Sleeping-Beacons.exe -h ``` ```bash Hunt-Sleeping-Beacons.exe --help ``` -------------------------------- ### Scan All Processes Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Run a scan on all processes on the system. Requires Administrator privileges. ```bash Hunt-Sleeping-Beacons.exe ``` -------------------------------- ### Display Command Line Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Display the command line of suspicious processes identified during the scan. ```bash Hunt-Sleeping-Beacons.exe --commandline ``` -------------------------------- ### Enumerate and Detect Suspicious Timer Callbacks Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Initializes a list of suspicious API targets and scans process worker factories to identify malicious timer callbacks. ```cpp // Initialize list of suspicious callback targets static void initialize_suspicious_callbacks(void) { HMODULE hNtdll = GetModuleHandleA("ntdll.dll"); HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); suspicious_callbacks.push_back(make_callback(L"ntdll!NtContinue", GetProcAddress(hNtdll, "NtContinue"))); suspicious_callbacks.push_back(make_callback(L"ntdll!NtContinueEx", GetProcAddress(hNtdll, "NtContinueEx"))); suspicious_callbacks.push_back(make_callback(L"ntdll!KiUserApcDispatcher", GetProcAddress(hNtdll, "KiUserApcDispatcher"))); suspicious_callbacks.push_back(make_callback(L"ntdll!RtlCaptureContext", GetProcAddress(hNtdll, "RtlCaptureContext"))); suspicious_callbacks.push_back(make_callback(L"ntdll!NtAllocateVirtualMemory", GetProcAddress(hNtdll, "NtAllocateVirtualMemory"))); suspicious_callbacks.push_back(make_callback(L"kernel32!ResumeThread", GetProcAddress(hKernel32, "ResumeThread"))); // ... additional suspicious targets } process_scan process_scans::suspicious_timer = [](process* process) { std::vector workerFactories; WORKER_FACTORY_BASIC_INFORMATION wfbi = {0}; // Get all TpWorkerFactory handles for the process get_workerfactory_handles(process, WORKER_FACTORY_ALL_ACCESS, workerFactories); for (HANDLE hWorkerFactory : workerFactories) { if (NtQueryInformationWorkerFactory(hWorkerFactory, WorkerFactoryBasicInformation, &wfbi, sizeof(wfbi), NULL) == STATUS_SUCCESS) { FULL_TP_POOL full_tp_pool = {0}; ReadProcessMemory(process->handle, wfbi.StartParameter, &full_tp_pool, sizeof(FULL_TP_POOL), NULL); // Traverse timer queue linked list PFULL_TP_TIMER p_tp_timer = CONTAINING_RECORD(full_tp_pool.TimerQueue.RelativeQueue.WindowStart.Root, FULL_TP_TIMER, WindowStartLinks); // Read timer callback context and check against suspicious targets TPP_CLEANUP_GROUP_MEMBER ctx = {0}; ReadProcessMemory(process->handle, tp_timer.Work.CleanupGroupMember.Context, &ctx, sizeof(ctx), NULL); for (SUSPICIOUS_CALLBACK suspiciousCallback : suspicious_callbacks) { if (ctx.FinalizationCallback >= (PBYTE)suspiciousCallback.addr - 8 && ctx.FinalizationCallback <= suspiciousCallback.addr) { process_detection p; p.name = L"Suspicious Timer"; p.description = std::format(L"A suspicious timer callback was identified pointing to {}", suspiciousCallback.name); p.severity = hsb::containers::detections::CRITICAL; process->add_detection(p); } } } CloseHandle(hWorkerFactory); } }; ``` -------------------------------- ### Hunt Sleeping Beacons CLI Usage Source: https://github.com/theflink/hunt-sleeping-beacons/blob/main/Readme.md Command-line interface options for the Hunt Sleeping Beacons tool. Use -p or --pid to specify the process ID to scan. The --dotnet flag includes .NET processes, and --commandline shows command lines for suspicious processes. ```bash _ _ _____ ______ | | | | / ___| | ___ \ | |_| | \ `--. | |_/ / | _ | `--. \ | ___ \ | | | | /\__/ / | |_/ / \_| |_/ \____/ \____/ Hunt-Sleeping-Beacons | @thefLinkk -p / --pid {PID} --dotnet | Set to also include dotnet processes. ( Prone to false positivies ) --commandline | Enables output of cmdline for suspicious processes -h / --help | Prints this message? ``` -------------------------------- ### Include .NET Processes Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Include .NET processes in the scan. This may lead to a higher false positive rate. ```bash Hunt-Sleeping-Beacons.exe --dotnet ``` -------------------------------- ### Combined Scan Options Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Combine options to scan a specific PID and display command line output. ```bash Hunt-Sleeping-Beacons.exe -p 4567 --commandline ``` -------------------------------- ### Detecting Blocking Timer via Thread Stack Scanning Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Resolves the RtlpTpTimerCallback address and scans thread stacks to identify blocking states. Requires appropriate process and thread handles with sufficient permissions. ```cpp // Dynamically resolves RtlpTpTimerCallback by creating a timer and capturing the callback address static DWORD64 callback_dispatcher = 0; static void my_callback(void) { CONTEXT context = {0}; STACKFRAME64 stackframe = {0}; RtlCaptureContext(&context); stackframe.AddrPC.Offset = context.Rip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrStack.Mode = AddrModeFlat; SymInitialize(GetCurrentProcess(), NULL, TRUE); StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL); StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL); SymCleanup(GetCurrentProcess()); callback_dispatcher = stackframe.AddrPC.Offset; // Captures RtlpTpTimerCallback address SetEvent(hEvent); } thread_scan thread_scans::blocking_timer = [](process* process, thread* thread) { std::call_once(dispatcher_resolved, resolve_callback_dispatcher); if (callback_dispatcher == 0) return; CONTEXT context = {0}; context.ContextFlags = CONTEXT_ALL; if (GetThreadContext(thread->handle, &context) == FALSE) return; for (DWORD64 i = context.Rsp; i <= (DWORD64)thread->stackbase; i += 8) { DWORD64 stackAddr = 0; if (ReadProcessMemory(process->handle, (LPCVOID)i, &stackAddr, sizeof(DWORD64), NULL) == FALSE) break; if (stackAddr == callback_dispatcher) { thread_detection detection; detection.name = L"Blocking Timer detected"; detection.description = L"Thread's blocking state triggered by ntdll!RtlpTpTimerCallback"; detection.tid = thread->tid; detection.severity = hsb::containers::detections::HIGH; process->add_detection(detection); } } }; ``` -------------------------------- ### Process Scanner Class Definition and Initialization Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Defines the `process_scanner` class within the `hsb::scanning` namespace. The constructor registers various process-level and thread-level scans. This class orchestrates detection scans using a thread pool for parallel execution. ```cpp namespace hsb::scanning { class process_scanner { public: process_scanner() { // Register process-level scans process_scans_.push_back(process_scans::suspicious_timer); // Register thread-level scans thread_scans_.push_back(thread_scans::blocking_apc); thread_scans_.push_back(thread_scans::blocking_timer); thread_scans_.push_back(thread_scans::abnormal_intermodular_call); thread_scans_.push_back(thread_scans::return_address_spoofing); thread_scans_.push_back(thread_scans::private_memory); thread_scans_.push_back(thread_scans::stomped_module); thread_scans_.push_back(thread_scans::hardware_breakpoints); thread_scans_.push_back(thread_scans::non_executable_memory); } // Returns (processes_scanned, threads_scanned) std::pair scan_processes(std::vector>& processes) { for (auto& process : processes) { // Submit process-level scans to thread pool for (auto& process_scan : process_scans_) { multi_future_.push_back(thread_pool_.submit_task([&]() { process_scan(process.get()); })); } // Submit thread-level scans for each thread for (auto& thread : process->threads) { for (auto& thread_scan : thread_scans_) { multi_future_.push_back(thread_pool_.submit_task([&]() { thread_scan(process.get(), thread.get()); })); } } } multi_future_.get(); // Wait for all scans to complete return std::make_pair(n_scanned_processes_.load(), n_scanned_threads_.load()); } }; } ``` -------------------------------- ### Process Container Class Definition Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Defines the `process` class within the `hsb::containers` namespace. It holds process information such as handle, PID, image name, command line, threads, and accumulated detections. The `add_detection` method is thread-safe, ensuring safe accumulation of detections from concurrent scans. ```cpp namespace hsb::containers { class process { public: HANDLE handle; // Process handle with PROCESS_ALL_ACCESS DWORD pid; // Process ID std::wstring imagename; // Process executable name std::wstring cmdline; // Command line arguments std::vector> threads; // All threads in the process std::vector> detections; // Accumulated detections // Thread-safe method to add detections from concurrent scans template void add_detection(const T& d) { static_assert(std::is_base_of::value, "T must inherit from detection"); std::lock_guard lock(mutex); detections.push_back(std::make_unique(d)); } private: std::mutex mutex; }; } ``` -------------------------------- ### Define Severity Levels and Console Formatting Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Defines the severity enumeration and maps each level to a specific ANSI color code for console output. ```cpp namespace hsb::containers::detections { enum severity { LOW, // High false positive rate (e.g., module stomping) MID, // Moderate confidence (e.g., private memory, non-executable pages) HIGH, // Strong indicator (e.g., blocking APC/timer) CRITICAL // Very strong indicator (e.g., suspicious timer callbacks, return address spoofing) }; // Color-coded console output static constexpr std::array, 4> severity_info = {{ {L"\033[32m", L"LOW"}, // Green {L"\033[33m", L"MID"}, // Yellow {L"\033[31m", L"HIGH"}, // Red {L"\033[35m", L"CRITICAL"} // Magenta }}; } ``` -------------------------------- ### Scan Specific Process by PID Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Scan a specific process using its Process ID (PID). ```bash Hunt-Sleeping-Beacons.exe -p 1234 ``` ```bash Hunt-Sleeping-Beacons.exe --pid 1234 ``` -------------------------------- ### Detect Module Stomping in C++ Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Identifies beacons that overwrite legitimate DLL code sections by checking if memory pages are still shared with the original on-disk image. ```cpp // Checks if module pages are still shared with the original on-disk image static BOOL ModuleIsSharedOriginal(HANDLE hProcess, PVOID pAddr) { MEMORY_BASIC_INFORMATION mbi = {0}; MEMORY_WORKING_SET_EX_INFORMATION mwsi = {0}; if (VirtualQueryEx(hProcess, (LPCVOID)pAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) == 0) return TRUE; if (mbi.Type != MEM_IMAGE) return TRUE; mwsi.VirtualAddress = mbi.BaseAddress; if (NtQueryVirtualMemory(hProcess, NULL, MemoryWorkingSetExInformation, &mwsi, sizeof(MEMORY_WORKING_SET_EX_INFORMATION), 0) != STATUS_SUCCESS) return TRUE; // SharedOriginal == 0 indicates the page has been modified (copy-on-write triggered) return mwsi.u1.VirtualAttributes.SharedOriginal != 0; } thread_scan thread_scans::stomped_module = [](process* process, thread* thread) { for (int i = 0; i < thread->calltrace->raw_addresses.size(); i++) { DWORD64 savedRet = thread->calltrace->raw_addresses.at(i); std::string moduleName = thread->calltrace->modules.at(i); // Skip private memory and known system modules if (ModuleIsSharedOriginal(process->handle, (PVOID)savedRet) || moduleName == "unknown") continue; if (!_strcmpi(moduleName.c_str(), "ntdll") || !_strcmpi(moduleName.c_str(), "kernel32") || !_strcmpi(moduleName.c_str(), "kernelbase") || !_strcmpi(moduleName.c_str(), "user32")) continue; // Non-system module with modified pages = potential stomping thread_detection detection; detection.name = L"Module Stomping"; detection.description = std::format(L"Callstack contains stomped module: {}", misc::string_to_wstring(moduleName)); detection.severity = hsb::containers::detections::LOW; process->add_detection(detection); break; } }; ``` -------------------------------- ### Detect Private Memory Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Detects private read-write-execute (RWX) memory pages in thread callstacks, indicating unpacked or injected beacon shellcode. It checks if any module in the calltrace is marked as 'unknown'. ```cpp // The scanner iterates through each module in the callstack // If "unknown" is found, it indicates private memory (not backed by a module) thread_scan thread_scans::private_memory = [](process* process, thread* thread) { BOOL bSuspicious = FALSE; for (std::string module : thread->calltrace->modules) { if (module == "unknown") { bSuspicious = TRUE; break; } } if (bSuspicious) { thread_detection detection; detection.name = L"Abnormal Page in Callstack"; detection.description = L"Callstack contains private memory regions"; detection.tid = thread->tid; detection.severity = hsb::containers::detections::MID; process->add_detection(detection); } }; // Example detection output: // * Detections for: suspicious.exe (1234) // ! Thread 5678 | Abnormal Page in Callstack | Callstack contains private memory regions | Severity: MID ``` -------------------------------- ### Detect Return Address Spoofing Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt This C++ code snippet detects return address spoofing by scanning for JMP register instructions at return addresses in the call stack. It checks for specific byte patterns corresponding to JMP [register] gadgets. ```cpp // JMP gadget patterns for various non-volatile registers static constexpr std::array patternJmpDerefRbx = {0xFF, 0x23}; // jmp [rbx] static constexpr std::array patternJmpDerefRbp = {0xFF, 0x65, 0x00}; // jmp [rbp] static constexpr std::array patternJmpDerefRdi = {0xFF, 0x27}; // jmp [rdi] static constexpr std::array patternJmpDerefRsi = {0xFF, 0x26}; // jmp [rsi] static constexpr std::array patternJmpDerefR12 = {0x41, 0xff, 0x24, 0x24}; // jmp [r12] static constexpr std::array patternJmpDerefR13 = {0x41, 0xff, 0x65, 0x00}; // jmp [r13] static constexpr std::array patternJmpDerefR14 = {0x41, 0xff, 0x26}; // jmp [r14] static constexpr std::array patternJmpDerefR15 = {0x41, 0xff, 0x27}; // jmp [r15] thread_scan thread_scans::return_address_spoofing = [](process* process, thread* thread) { std::array instructions = {}; for (int i = 0; i < thread->calltrace->raw_addresses.size(); i++) { // Check bytes at and after return address for JMP gadget patterns for (int j = 0; j < 4; j++) { ReadProcessMemory(process->handle, (PVOID)(thread->calltrace->raw_addresses.at(i) + j), instructions.data(), sizeof(instructions), NULL); BOOL bSuspicious = FALSE; if (memcmp(instructions.data(), patternJmpDerefRbx.data(), sizeof(patternJmpDerefRbx)) == 0) bSuspicious = TRUE; else if (memcmp(instructions.data(), patternJmpDerefRbp.data(), sizeof(patternJmpDerefRbp)) == 0) bSuspicious = TRUE; // ... check other patterns if (bSuspicious) { thread_detection detection; detection.name = L"Return Address Spoofing"; detection.description = std::format(L"Thread {} returns to JMP gadget. Gadget in: {}", thread->tid, misc::string_to_wstring(thread->calltrace->syms.at(i))); detection.tid = thread->tid; detection.severity = hsb::containers::detections::CRITICAL; process->add_detection(detection); break; } } } }; // Example detection output: // * Detections for: spoofer.exe (4567) // ! Thread 8901 | Return Address Spoofing | Thread 8901 returns to JMP gadget. Gadget in: ntdll!RtlUserThreadStart+0x21 | Severity: CRITICAL ``` -------------------------------- ### Detect Blocking APC in C++ Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Detects APC-based sleepmask implementations by scanning thread stacks for the presence of ntdll!KiUserApcDispatcher. ```cpp // Resolves KiUserApcDispatcher address once at startup static DWORD64 pKiUserAPCDispatcher = 0; static bool resolve_apc_dispatcher(void) { HMODULE hNtdll = GetModuleHandleA("ntdll.dll"); if (hNtdll == NULL) return false; pKiUserAPCDispatcher = (DWORD64)GetProcAddress(hNtdll, "KiUserApcDispatcher"); return pKiUserAPCDispatcher != 0; } thread_scan thread_scans::blocking_apc = [](process* process, thread* thread) { CONTEXT context = {0}; context.ContextFlags = CONTEXT_ALL; std::call_once(apc_resolved, resolve_apc_dispatcher); if (pKiUserAPCDispatcher == 0) return; if (GetThreadContext(thread->handle, &context) == FALSE) return; // Walk the stack looking for KiUserApcDispatcher return addresses for (DWORD64 i = context.Rsp; i <= (DWORD64)thread->stackbase; i += 8) { DWORD64 stackAddr = 0; if (ReadProcessMemory(process->handle, (LPCVOID)i, &stackAddr, sizeof(DWORD64), NULL) == FALSE) break; // Check if address falls within KiUserApcDispatcher function if (stackAddr >= pKiUserAPCDispatcher && (pKiUserAPCDispatcher + 120) > stackAddr) { thread_detection detection; detection.name = L"Blocking APC detected"; detection.description = L"Thread's state triggered by ntdll!kiuserapcdispatcher"; detection.tid = thread->tid; detection.severity = hsb::containers::detections::HIGH; process->add_detection(detection); } } }; ``` -------------------------------- ### Detect Non-Executable Memory Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt Identifies sleepmask implementations by checking if return addresses in the callstack point to memory pages that lack execute permissions. This can indicate code hiding techniques. ```cpp // Checks memory protection flags for each return address in the callstack thread_scan thread_scans::non_executable_memory = [](process* process, thread* thread) { BOOL bSuspicious = FALSE; MEMORY_BASIC_INFORMATION mbi = {0}; for (uint64_t ret : thread->calltrace->raw_addresses) { SIZE_T s = VirtualQueryEx(process->handle, (LPCVOID)ret, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); if (s == 0) continue; // Check if memory lacks any execute permission if ((mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) == 0) { bSuspicious = TRUE; break; } } if (bSuspicious) { thread_detection detection; detection.name = L"Non-Executable Page in Callstack"; detection.description = L"Callstack contains memory regions marked as non-executable"; detection.tid = thread->tid; detection.severity = hsb::containers::detections::MID; process->add_detection(detection); } }; // Example detection output: // * Detections for: beacon.exe (9012) // ! Thread 3456 | Non-Executable Page in Callstack | Callstack contains memory regions marked as non-executable | Severity: MID ``` -------------------------------- ### Detect Abnormal Intermodular Calls Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt This C++ code snippet detects module proxying by identifying abnormal call flows where NTAPI functions indirectly call WINAPI functions. It checks call stacks for sequences like ntdll.dll -> kernel32.dll -> ntdll.dll. ```cpp thread_scan thread_scans::abnormal_intermodular_call = [](process* process, thread* thread) { if (thread->calltrace->raw_addresses.size() <= 2) return; for (int i = 0; i < thread->calltrace->raw_addresses.size(); i++) { std::string module_tmp = thread->calltrace->modules.at(i); // Look for kernel32/kernelbase in callstack if (!_stricmp(module_tmp.c_str(), "kernelbase") || !_stricmp(module_tmp.c_str(), "kernel32")) { if (i == thread->calltrace->raw_addresses.size() - 2) break; // Check if the caller was ntdll (abnormal: ntdll called kernel32) module_tmp = thread->calltrace->modules.at(i + 1); if (!_stricmp(module_tmp.c_str(), "ntdll")) { thread_detection detection; detection.name = L"Abnormal Intermodular Call"; detection.description = std::format(L"{} called {}. This indicates module-proxying.", misc::string_to_wstring(thread->calltrace->syms.at(i + 1)), misc::string_to_wstring(thread->calltrace->syms.at(i))); detection.tid = thread->tid; detection.severity = hsb::containers::detections::CRITICAL; process->add_detection(detection); } } } }; // Example detection output: // * Detections for: proxy_beacon.exe (6789) // ! Thread 1234 | Abnormal Intermodular Call | ntdll!NtWaitForSingleObject called kernel32!Sleep. This indicates module-proxying. | Severity: CRITICAL ``` -------------------------------- ### Detect Hardware Breakpoints in Thread Source: https://context7.com/theflink/hunt-sleeping-beacons/llms.txt This C++ code snippet defines a thread scan function to detect active hardware breakpoints (DR0-DR3, DR7). It checks the thread's context for set debug registers and adds a critical detection if found. This is useful for identifying patchless API hooking and bypass techniques. ```cpp thread_scan thread_scans::hardware_breakpoints = [](process* process, thread* thread) { CONTEXT context = {0}; context.ContextFlags = CONTEXT_ALL; if (GetThreadContext(thread->handle, &context)) { // Check if any debug registers are set bool bFound = (context.Dr0 != 0) || (context.Dr1 != 0) || (context.Dr2 != 0) || (context.Dr3 != 0) || (context.Dr7 != 0); if (bFound) { thread_detection detection; detection.name = L"Identified enabled hardware_breakpoints"; detection.description = L"Often used as part of patchless-modifications of code"; detection.tid = thread->tid; detection.severity = hsb::containers::detections::CRITICAL; process->add_detection(detection); } } }; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.