Windows应急响应/22-DLL劫持与侧加载 tags:: #Windows应急响应 #持久化 #DLL劫持 #DLLHijacking #SideLoading category:: 持久化与后门检测 difficulty:: 高级 platform:: Windows 7/10/11, Server 2012-2022
概述 DLL Hijacking(DLL劫持)和DLL Side-Loading(DLL侧加载)利用Windows DLL搜索机制的弱点实现代码执行
攻击者将恶意DLL放置在特定位置,使合法程序加载执行恶意代码
MITRE ATT&CK:
T1574.001 - Hijack Execution Flow: DLL Search Order Hijacking
T1574.002 - Hijack Execution Flow: DLL Side-Loading
优势: 恶意代码在合法签名进程上下文中执行,绕过白名单和EDR
Linux对比: 19-LD_PRELOAD劫持 是Linux下的类似机制
一、Windows DLL搜索顺序 1.1 标准搜索顺序(SafeDllSearchMode启用时) Windows加载DLL时按以下顺序搜索(6步):
1 2 3 4 5 6 7 8 1. DLL重定向 / API Set / SxS Manifest(.local / manifest)2. Known DLLs(HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs)3. 应用程序所在目录(EXE同目录)4. 系统目录 C:\Windows\System325. 16位系统目录 C:\Windows\System6. Windows目录 C:\Windows7. 当前工作目录 (CWD)8. PATH环境变量中的目录
SafeDllSearchMode = 1(默认启用) : CWD在System32之后搜索
SafeDllSearchMode = 0 : CWD在System32之前搜索(更危险)
注册表控制:
1 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
1.2 Known DLLs保护机制 Windows维护一个”已知DLL”列表,这些DLL只从System32加载:
1 2 reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs"
常见Known DLLs包括: kernel32.dll, user32.dll, ntdll.dll, advapi32.dll等
Known DLLs不受搜索顺序影响,始终从System32加载
但 : 不在Known DLLs列表中的DLL仍然可以被劫持
1.3 DLL加载API差异 不同的加载API使用不同的搜索策略:
1 2 3 4 5 6 7 8 9 10 LoadLibrary("helper.dll" ); LoadLibraryEx("C:\\App\\helper.dll" , NULL , LOAD_WITH_ALTERED_SEARCH_PATH); SetDllDirectory("C:\\custom\\path" ); LoadLibrary("helper.dll" );
二、DLL劫持攻击手法 2.1 应用程序目录劫持(Application Directory Hijacking) 原理: 将恶意DLL放在目标EXE的同目录下
如果该DLL不在Known DLLs中,且EXE在Step 3时找到,则加载恶意版本
常见可劫持目标:
1 2 3 4 5 6 7 8 version .dll — 几乎所有程序都会加载dbghelp.dll — 很多程序导入 wtsapi32.dll — Terminal Service API dwmapi.dll — Desktop Window Manager uxtheme.dll — Theme related winmm.dll — Multimedia API crypt32.dll — 不在所有版本的KnownDLLs中
示例: 劫持某应用的version.dll:
1 2 3 4 5 6 7 8 9 Copy-Item evil.dll "C:\Program Files\TargetApp\version.dll"
2.2 DLL侧加载(Side-Loading) DLL Side-Loading是DLL劫持的一种特殊形式:
攻击者将签名的合法程序 和恶意DLL 一起部署
合法程序启动时自动加载同目录的恶意DLL
因为宿主进程有合法签名,所以能绕过白名单
经典案例:
1 2 3 4 C :\Users\Public\Staging\ MsMpEng.exe ← 合法的Windows Defender EXE(已签名) mpsvc .dll ← 恶意DLL(MsMpEng.exe会加载这个DLL)
常被APT使用的合法程序+DLL对:
1 2 3 4 5 6 7 8 9 程序 劫持的DLL ────────────────────────────────────────── MsMpEng.exe mpsvc.dll vmtoolsd.exe vmtools.dll Notepad++\updater.exe libcurl.dll GoogleUpdate.exe goopdate.dll WinSCP.exe DragExt.dll 7 zFM.exe 7 z.dll python.exe python3.dll
2.3 Phantom DLL劫持 利用程序尝试加载不存在的DLL 的行为
程序设计时引用了某个DLL,但该DLL在当前系统上不存在
Windows按搜索顺序逐一查找,最终失败(NAME NOT FOUND)
攻击者在搜索路径的某个位置放置同名DLL
发现Phantom DLL的方法:
常见的Phantom DLL:
1 2 3 4 5 6 # Windows系统中常见的不存在但会被搜索的DLL WptsExtensions.dll — Explorer.exe IKEEXT.dll — svchost.exe (IKEEXT服务) wlbsctrl.dll — svchost.exe (某些网络服务) Tsmsisrv.dll — SessionEnv服务 TSVIPSrv.dll — SessionEnv服务
2.4 DLL代理(DLL Proxying) 恶意DLL将合法DLL的导出函数转发(proxy),同时执行恶意代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #pragma comment(linker, "/export:GetFileVersionInfoA=C:\\Windows\\System32\\version.GetFileVersionInfoA" ) #pragma comment(linker, "/export:GetFileVersionInfoByHandle=C:\\Windows\\System32\\version.GetFileVersionInfoByHandle" ) #pragma comment(linker, "/export:GetFileVersionInfoExA=C:\\Windows\\System32\\version.GetFileVersionInfoExA" ) #pragma comment(linker, "/export:GetFileVersionInfoExW=C:\\Windows\\System32\\version.GetFileVersionInfoExW" ) #pragma comment(linker, "/export:GetFileVersionInfoSizeA=C:\\Windows\\System32\\version.GetFileVersionInfoSizeA" ) #pragma comment(linker, "/export:GetFileVersionInfoSizeExA=C:\\Windows\\System32\\version.GetFileVersionInfoSizeExA" ) #pragma comment(linker, "/export:GetFileVersionInfoSizeExW=C:\\Windows\\System32\\version.GetFileVersionInfoSizeExW" ) #pragma comment(linker, "/export:GetFileVersionInfoSizeW=C:\\Windows\\System32\\version.GetFileVersionInfoSizeW" ) #pragma comment(linker, "/export:GetFileVersionInfoW=C:\\Windows\\System32\\version.GetFileVersionInfoW" ) #pragma comment(linker, "/export:VerFindFileA=C:\\Windows\\System32\\version.VerFindFileA" ) #pragma comment(linker, "/export:VerFindFileW=C:\\Windows\\System32\\version.VerFindFileW" ) #pragma comment(linker, "/export:VerInstallFileA=C:\\Windows\\System32\\version.VerInstallFileA" ) #pragma comment(linker, "/export:VerInstallFileW=C:\\Windows\\System32\\version.VerInstallFileW" ) #pragma comment(linker, "/export:VerLanguageNameA=C:\\Windows\\System32\\version.VerLanguageNameA" ) #pragma comment(linker, "/export:VerLanguageNameW=C:\\Windows\\System32\\version.VerLanguageNameW" ) #pragma comment(linker, "/export:VerQueryValueA=C:\\Windows\\System32\\version.VerQueryValueA" ) #pragma comment(linker, "/export:VerQueryValueW=C:\\Windows\\System32\\version.VerQueryValueW" ) #include <windows.h> BOOL APIENTRY DllMain (HMODULE hModule, DWORD reason, LPVOID reserved) { if (reason == DLL_PROCESS_ATTACH) { CreateThread(NULL , 0 , (LPTHREAD_START_ROUTINE)MaliciousFunction, NULL , 0 , NULL ); } return TRUE; }
这样原始程序功能不受影响,不会崩溃,更加隐蔽
三、检测方法 3.1 Process Monitor实时监控 Process Monitor是发现DLL劫持的最佳工具:
1 2 3 4 5 6 7 8 9 设置过滤器 : Operation = CreateFile Path ends with .dll Result = SUCCESS 关注 : - 从非标准路径加载的系统DLL - 从用户可写目录加载的DLL - 加载失败(NAME NOT FOUND)后从其他位置成功加载
命令行使用Procmon:
1 2 3 4 5 Procmon.exe /AcceptEula /Quiet /Minimized /BackingFile C:\procmon.pml :: 运行一段时间后 Procmon.exe /Terminate :: 转换为CSV分析 Procmon.exe /OpenLog C:\procmon.pml /SaveAs C:\procmon.csv
3.2 sigcheck验证DLL签名 检查特定目录下所有DLL的签名:
1 2 3 4 5 :: 检查Program Files下所有未签名DLL sigcheck.exe -u -e -s "C:\Program Files" > unsigned_dlls.txt :: 检查特定应用目录 sigcheck.exe -u -e "C:\Program Files\TargetApp\*.dll"
PowerShell批量检查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $paths = @ ( "C:\Program Files" , "C:\Program Files (x86)" , "C:\ProgramData" ) foreach ($basePath in $paths ) { Get-ChildItem $basePath -Recurse -Filter "*.dll" -ErrorAction SilentlyContinue | ForEach-Object { $sig = Get-AuthenticodeSignature $_ .FullName -ErrorAction SilentlyContinue if ($sig .Status -ne 'Valid' ) { [PSCustomObject ]@ { Path = $_ .FullName SigStatus = $sig .Status LastWriteTime = $_ .LastWriteTime Size = $_ .Length } } } } | Sort-Object LastWriteTime -Descending | Format-Table -AutoSize -Wrap
3.3 对比分析 将DLL与已知正常版本进行Hash对比:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $appDir = "C:\Program Files\TargetApp" Get-ChildItem $appDir -Filter "*.dll" | ForEach-Object { $sysDll = "C:\Windows\System32\$ ($_ .Name)" if (Test-Path $sysDll ) { $appHash = (Get-FileHash $_ .FullName -Algorithm SHA256).Hash $sysHash = (Get-FileHash $sysDll -Algorithm SHA256).Hash if ($appHash -ne $sysHash ) { Write-Host "[!] MISMATCH: $ ($_ .Name)" -ForegroundColor Red Write-Host " App: $appHash " Write-Host " Sys: $sysHash " $appVer = (Get-ItemProperty $_ .FullName).VersionInfo $sysVer = (Get-ItemProperty $sysDll ).VersionInfo Write-Host " App Version: $ ($appVer .FileVersion)" Write-Host " Sys Version: $ ($sysVer .FileVersion)" } } }
3.4 Sysmon Image Load监控 Sysmon Event ID 7可以监控DLL加载:
1 2 3 4 5 6 7 8 9 <ImageLoad onmatch ="include" > <ImageLoaded condition ="contains" > C:\Users\</ImageLoaded > <ImageLoaded condition ="contains" > C:\ProgramData\</ImageLoaded > <ImageLoaded condition ="contains" > \Temp\</ImageLoaded > <Signed condition ="is" > false</Signed > </ImageLoad >
查询Sysmon DLL加载事件:
1 2 3 4 5 6 7 8 9 10 Get-WinEvent -FilterHashtable @ { LogName = 'Microsoft-Windows-Sysmon/Operational' Id = 7 } -MaxEvents 100 | Where-Object { $_ .Message -match "Signed:\s+false" -or $_ .Message -match "C:\\Users\\" } | Select-Object TimeCreated, @ {N='Process' ;E={($_ .Message -split "`n" | Select-String "Image:" ).ToString().Trim()}}, @ {N='LoadedDLL' ;E={($_ .Message -split "`n" | Select-String "ImageLoaded:" ).ToString().Trim()}} | Format-Table -AutoSize -Wrap
3.5 自动化DLL劫持扫描脚本 检查正在运行的进程加载的可疑DLL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Get-Process | ForEach-Object { $proc = $_ try { $modules = $proc .Modules | Where-Object { $_ .FileName -and $_ .FileName -notmatch "^C:\\Windows\\" -and $_ .FileName -notmatch "^C:\\Program Files.*\\Microsoft" -and $_ .FileName -match "\.dll$ " } foreach ($mod in $modules ) { $sig = Get-AuthenticodeSignature $mod .FileName -ErrorAction SilentlyContinue if ($sig .Status -ne 'Valid' ) { [PSCustomObject ]@ { Process = $proc .ProcessName PID = $proc .Id DLL = $mod .FileName SigStatus = $sig .Status DLLSize = $mod .ModuleMemorySize } } } } catch {} } | Sort-Object Process | Format-Table -AutoSize -Wrap
四、常见目标与案例 4.1 APT41 — ShadowPad ShadowPad后门常使用DLL Side-Loading:
1 2 合法程序: AppLaunch.exe (Microsoft ClickOnce) 恶意DLL: mscoree.dll
部署在: C:\ProgramData\<random_folder>\
通过计划任务或服务启动合法EXE
4.2 PlugX恶意软件 PlugX是使用DLL Side-Loading最多的恶意软件家族之一:
1 2 3 4 # 常见的PlugX Side-Loading组合 CamMute.exe + DivXVSMULTICodec.dll Mc.exe (McAfee) + McUtil.dll hkcmd.exe (Intel) + hkcmd.dll
三件套: 合法EXE + 恶意DLL + 加密payload文件
4.3 Windows系统DLL劫持提权 某些Windows系统服务存在DLL劫持漏洞:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $env:PATH -split ';' | ForEach-Object { if ($_ -and (Test-Path $_ )) { $acl = Get-Acl $_ $userAccess = $acl .Access | Where-Object { $_ .IdentityReference -match "Users|Everyone|Authenticated" -and $_ .FileSystemRights -match "Write|FullControl|Modify" } if ($userAccess ) { Write-Host "[!] Writable PATH dir: $_ " -ForegroundColor Red $userAccess | ForEach-Object { Write-Host " $ ($_ .IdentityReference): $ ($_ .FileSystemRights)" -ForegroundColor Yellow } } } }
五、清除与修复 5.1 识别并删除恶意DLL 确认恶意DLL后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $malDll = "C:\Program Files\TargetApp\version.dll" Get-Process | Where-Object { $_ .Modules.FileName -contains $malDll } | Select-Object ProcessName, Id, Path Get-Process | Where-Object { $_ .Modules.FileName -contains $malDll } | Stop-Process -Force Copy-Item $malDll "C:\IR_Evidence\" -Force Get-FileHash $malDll -Algorithm SHA256 | Format-List Remove-Item $malDll -Force
5.2 防御措施 启用SafeDllSearchMode(确认已启用):
1 reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager" /v SafeDllSearchMode
应用开发层面:
使用绝对路径调用LoadLibrary
使用SetDefaultDllDirectories限制搜索范围
启用DLL重定向(.local文件)
系统层面:
监控非标准路径的DLL加载(Sysmon Event 7)
定期扫描应用目录中的未签名DLL
限制用户可写目录
六、关联检查 DLL劫持/侧加载通常与以下内容关联:
20-Windows服务后门 — 服务DLL是劫持重点目标
25-IFEO与AppInit-DLLs后门 — AppInit_DLLs是另一种DLL注入
23-COM劫持 — COM劫持也涉及DLL替换
18-Registry-Run后门 — 可能通过Run Key启动宿主EXE
19-LD_PRELOAD劫持 — Linux等效机制
七、应急响应Checklist [ ] 使用Process Monitor监控DLL搜索行为
[ ] 使用sigcheck扫描应用目录中的未签名DLL
[ ] 检查System32目录外的系统DLL副本
[ ] 对比可疑DLL与已知正常版本的Hash
[ ] 检查Sysmon Event ID 7中的异常DLL加载
[ ] 检查PATH环境变量中的用户可写目录
[ ] 检查Known DLLs列表是否被篡改
[ ] 扫描ProgramData/Users目录下的EXE+DLL组合
[ ] 验证SafeDllSearchMode注册表值
[ ] 收集证据后删除恶意DLL并终止相关进程