DLL 劫持初探
前几周做了一个小工具 HijackGen,反响还不错,今天就来简单介绍一下 DLL 劫持的原理和实现,具体的实践也可以参考文档。
1 基本概念
DLL 劫持 (DLL Hijacking) 是一种常见的攻击手段,通过劫持系统 DLL 或自定义 DLL 实现恶意代码的注入。
2 原理
2.1 劫持系统 DLL
当程序加载系统 DLL 时,若未指定 DLL 的完整路径 (这种情况相当常见),Windows 会大致按照以下顺序搜索 DLL (随 Windows 版本不同可能会有差异):
- 当前目录
- 系统目录
- Windows 目录
- 环境变量 PATH 中指定的目录
可以看到,当前目录优先级最高,因此可以将恶意 DLL 重命名为程序需要的系统 DLL 并放置在当前目录,从而实现劫持。
注意:在注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs 中列出的 DLL 不会被劫持。
当然,被掉包的 DLL 应该实现原 DLL 的所有导出函数,否则可能会导致程序崩溃。
实现方式见下文。
2.2 劫持自定义 DLL
这一部分就很简单了,我们通过创建一个新的 DLL,重命名为原 DLL 的名称,并在其中实现原 DLL 的所有导出函数。这样,当程序加载这个新的 DLL 时,实际上是加载了我们自己实现的代码。

这个方法也叫做 “DLL 代理”(DLL Proxy)。
3 实现
3.1 劫持系统 DLL
在代码中,我们需要找到目标 DLL 的路径,通过 LoadLibrary 加载该 DLL,之后通过 GetProcAddress 获取真正的函数地址。随后,我们需要导出获取到的真正函数,并且导出的名称必须与原 DLL 对应函数一致。
关键代码如下:(以 version.dll 的 GetFileVersionInfoA 函数为例)
#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=_Redirect_GetFileVersionInfoA,@1")
PVOID Real_GetFileVersionInfoA = NULL;
HMODULE Real_Module = NULL;
FARPROC WINAPI GetAddress(LPCSTR lpProcName)
{
FARPROC pAddress = GetProcAddress(Real_Module, lpProcName);
if (pAddress == NULL)
{
MessageBoxW(NULL, L"Get address failed", L"version", MB_OK);
ExitProcess(1);
}
return pAddress;
}
EXTERN_C __declspec(naked) void __cdecl Redirect_GetFileVersionInfoA(void)
{
__asm jmp Real_GetFileVersionInfoA;
}
VOID WINAPI InitHijack()
{
WCHAR real_dll_path[MAX_PATH];
GetSystemDirectoryW(real_dll_path, MAX_PATH);
lstrcatW(real_dll_path, L"\\version.dll");
Real_Module = LoadLibraryW(real_dll_path);
if (Real_Module == NULL)
{
MessageBoxW(NULL, L"Load original dll failed", L"version", MB_OK);
ExitProcess(1);
}
Real_GetFileVersionInfoA = GetAddress("GetFileVersionInfoA");
}
最后在 DLL 的 DllMain 函数中调用 InitHijack 函数即可。
3.2 劫持自定义 DLL
劫持自定义 DLL 时利用转发函数的方式实现,将调用转发到原 DLL 的对应函数。
如果使用 .h 头文件,那么我们需要使用类似 #pragma comment(linker, "/EXPORT:...") 的语句来导出函数。
#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=version.GetFileVersionInfoA,@1")
如果使用 .def 文件,那么我们需要在 .def 文件中添加 EXPORTS 语句。这种方式仅限 gcc/g++,msvc 不支持。
EXPORTS
GetFileVersionInfoA=version.GetFileVersionInfoA @1