本项目(主要)是一个调用栈扫描器,旨在识别指示未打包或注入的C2代理的IOC。
所有检查基于以下观察:C2代理在回调之间等待,导致信标线程空闲,而此工具旨在分析可能导致线程空闲的原因。
这包括传统的IOC,如未备份的内存或被篡改的模块,但也尝试检测使用APC或定时器实现的多种睡眠掩码。后者通过两种方式完成:分析调用栈,以及从用户态枚举定时器及其确切回调。
(几乎)没有哪个IOC可以被认为是100%的真正例,例如模块篡改检测极易产生误报。尽管如此,结果可能会引起对进程行为的怀疑。
DotNet和32位二进制文件被忽略。

调用栈中的私有R(W)X页面可能表示信标在运行时被解包或注入。
多种睡眠掩码会将信标页面的页权限更改为不可执行。这导致调用栈中出现可疑的不可执行页面。
通常,信标通过加载并覆盖磁盘上的合法模块来避免私有内存页面。
借助“写时复制”机制,可以通过检查MEMORY_WORKING_SET_EX_INFORMATION结构体的VirtualAttributes.SharedOriginal字段来识别被篡改的镜像。如果调用栈中的任何页面不是私有的且SharedOriginal == 0,则被视为IOC。
这可能是最容易产生误报的检测。:'(
多种睡眠掩码实现会向Ntdll!NtContinue排队一系列APC,其中有一个APC触发Ntdll!WaitForSingleObject的执行。因此,如果在阻塞函数的调用栈中发现Ntdll!KiUserApcDispatcher,此工具将其视为IOC。
与APC的可疑使用类似,此工具还会检查阻塞函数调用栈中是否存在ntdll!RtlpTpTimerCallback,以检测基于定时器的睡眠掩码。
据我所知,定时器是基于线程池实现的。正如Alon Leviev所演示的,可以使用NtQueryInformationWorkerFactory并传入WorkerFactoryBasicInformation来枚举它们。
WORKER_FACTORY_BASIC_INFORMATION结构体包含一个FULL_TP_POOL,该池又链接到一个TimerQueue双向链表。遍历该PFULL_TP_TIMER列表可以访问每个已注册的回调。如果发现任何回调指向一组可疑的API调用,例如ntdll!ntcontinue,则可以视为强IOC。

最初,模块代理被引入作为一种绕过可疑调用栈的方法。 虽然绕过有效,但它引入了另一种强IOC,因为NTAPI被用来调用WINAPI。这很奇怪,因为WINAPI是NTAPI的抽象。因此,如果观察到调用栈中存在ntdll.dll->kernel32.dll->ntdll.dll的序列,并最终调用一个阻塞函数,则可以视为IOC。
据我所知,大多数返回地址欺骗实现使用一种技术,即被调用函数返回到一个jmp [非易失性寄存器] gadget。本项目简单地遍历调用栈中的每个返回地址,并搜索指示返回到jmp gadget的模式。

_ _ _____ ______
| | | | / ___| | ___ \
| |_| | \ `--. | |_/ /
| _ | `--. \ | ___ \
| | | | /\__/ / | |_/ /
\_| |_/ \____/ \____/
Hunt-Sleeping-Beacons | @thefLinkk
-p / --pid {PID}
--dotnet | 设置为包含dotnet进程。(容易产生误报)
--commandline | 启用可疑进程的命令行输出
-h / --help | 打印此消息?