[原创]TX某游戏内存加载d3d9的一个HOOK思路
首先声明:本帖内容仅供技术交流,不涉及任何与过保护、作弊功能等任何违法行为有关的内容。
玩儿过TX某F的同学应该知道,某F在vista以上系统会加载d3d9.dll,而xp环境下就不加载d3d9.dll了,而且模块列表里也找不到d3d9.dll,这是为什么呢?
(因为没有xp系统的真机,所以就不放测试截图了)
有些同学以为*F是加载了D3D\\d3d9xpsp3.dll这个文件,其实不是,这个文件只是*f用来做内存校验的

真正的d3d9.dll被打包进了cross****Base.dll中,在运行时解密出来当做内存dll加载,所以你只能在模块列表里看到一个d3d8thk.dll(因为这是d3d9.dll的依赖库)。
那碰到这种内存dll的情况该如何做d3d hook呢?ida打开d3d9xpsp3.dll看一看,找到我们最亲切的D3DCreateDevice9同学

加载符号之后基本就能看个大概了
IDirect3D9 *__stdcall Direct3DCreate9(UINT SDKVersion)
{
HMODULE v1; // eax@3
FARPROC v2; // eax@4
UINT v4; // eax@6
BYTE Data[4]; // [sp+0h] [bp-108h]@1
CHAR OutputString; // [sp+4h] [bp-104h]@10
if ( GetD3DRegValue(4, "LoadDebugRuntime", Data, 4) )
{
if ( *(_DWORD *)Data )
{
v1 = LoadLibraryA("d3d9d.dll");
if ( v1 )
{
v2 = GetProcAddress(v1, "Direct3DCreate9");
if ( v2 )
return (IDirect3D9 *)((int (__stdcall *)(_DWORD))v2)(SDKVersion);
}
}
}
v4 = SDKVersion & 0x7FFFFFFF;
if ( (SDKVersion & 0x7FFFFFFF) != 31 && (v4 < 0x20 || v4 > 0x20 && v4 < 0x84) )
{
StringCbPrintfA(
&OutputString,
0x100u,
"\nD3D ERROR: D3D header version mismatch.\nThe application was compiled against and will only work with D3D_SDK_VERSION (%d), but the currently installed runtime is version (%d).\nRecompile the application against the appropriate SDK for the installed runtime.\n\n",
SDKVersion & 0x7FFFFFFF,
32);
OutputDebugStringA(&OutputString);
return 0;
}
if ( !operator new(0x47B8u) )
return 0;
return (IDirect3D9 *)CEnum::CEnum(SDKVersion);
}那么我们需要的IDirect3D9 *指针就由CEnum::CEnum里返回得到
在CEnum::CEnum靠近return的地方可以看到:

v2就是我们要的IDirect3D9 *指针了。
if ( !RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectDraw\\GammaCalibrator", 0, 1u, &phkResult) )
这不就是一个很棒的hook点么,在这里返回值v2的值已经确定了,后面没有对v2的赋值操作了。
但是怎么获得这个返回值v2呢?看下汇编:
*((_DWORD *)v2 + 4520) = 0; *((_DWORD *)v2 + 4521) = 0; *((_DWORD *)v2 + 4522) = 0; *((_DWORD *)v2 + 4523) = 0;
刚好对应
mov [ebx+46A0h], esi .text:4B66ADA7 mov [ebx+46A4h], esi .text:4B66ADAD mov [ebx+46A8h], esi .text:4B66ADB3 mov [ebx+46ACh], esi .text:4B66ADB9 call ds:__imp__RegOpenKeyExA@20
那么在RegOpenKeyExA的detour函数中取ebx不就可以了?
赶紧写个demo
先HOOK RegOpenKeyExA,这里必须inline hook因为内存dll的IAT你是hook不到的
LONG(WINAPI *m_pfnRegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) = NULL; IDirect3D9 *(WINAPI *m_pfnDirect3DCreate9)(UINT SDKVersion) = NULL;
//开始hook,最好在dllmain里做
m_pfnRegOpenKeyExA = (LONG(WINAPI *)(HKEY , LPCSTR , DWORD , REGSAM , PHKEY ))
GetProcAddress(GetModuleHandle(L"Advapi32.dll"), "RegOpenKeyExA");
if (m_pfnRegOpenKeyExA)
{
DetourTransactionBegin();
DetourAttach(&(void *&)m_pfnRegOpenKeyExA, NewRegOpenKeyExAProxyXPSP3);
DetourTransactionCommit();
}
//取返回值IDirect3D9 *
DWORD m_Direct3DCreate9_RetVal = NULL;
LONG WINAPI NewRegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
{
if (0 == strcmp("Software\\Microsoft\\DirectDraw\\GammaCalibrator", lpSubKey))
{
DWORD *pVTable = *(DWORD **)m_Direct3DCreate9_RetVal;
if (!m_pfnIDirect3D9_CreateDevice)
{
m_pfnIDirect3D9_CreateDevice = (HRESULT(WINAPI *)(IDirect3D9 *, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *, IDirect3DDevice9 **))pVTable[16];
//Direct3DCreate9返回值是IDirect3D9 *指针,它的虚函数表第17个就是->CreateDevice啦,替换虚函数表或者inline都可以随便你了~
}
}
return m_pfnRegOpenKeyExA(hKey, lpSubKey, ulOptions, samDesired, phkResult);
}
//由于ebx是易失寄存器,我们需要在每一个调用中首先保存ebx再进真正的detour函数,防止在detour函数中ebx被编译器生成的代码改掉
__declspec(naked) void NewRegOpenKeyExAProxyXPSP3()
{
__asm {
mov dword ptr[m_Direct3DCreate9_RetVal], ebx;
jmp NewRegOpenKeyExA;
}
}
将该dll用系统dll劫持、lsp劫持、内核注入或者别的手段注入游戏,保证该dll先于d3d9初始化,即可成功hook到Direct3DCreate9。
那么就又有人要问了:万一换别的系统呢?又不是每个系统的d3d9.dll都有这玩意
于是我用ida看过一遍从xpsp3开始到win10 14393的所有d3d9.dll,都有
if ( !RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectDraw\\GammaCalibrator", 0, 1u, &phkResult) )
也就是说这种方法是全系统支持的。
ps:直接hook d3d9里的东西是必然会被检测的,但这并不在本帖的讨论范围内,本帖仅提供一种另类(火星?)的内存dll的hook思路,仅此而已。
