[原创]CoolCmd_通用D3D降压透视插件(附源码)
两个用了多年的老方法,善加利用,依然可用于市面上所有DX网游(有各式保护系统的也是可以的)
降压用第18个函数
IDirect3DDevice9::Present
DWORD g_Oft_Present = (18 - 1)*4;
函数原型:
typedef HRESULT (__stdcall *TPresent)(LPDIRECT3DDEVICE9 pDevice,const RECT *pSourceRect,const RECT *pDestRect,HWND hDestWindowOverride,const RGNDATA *pDirtyRegion);
此方式降压效果还是不错的,把百分之几十的CPU占用降低到1%左右,稳定挂个几天是没问题的
透视用第83个函数
IDirect3DDevice9::DrawIndexedPrimitive
DWORD g_Oft_DrawIndexedPrimitive = (83 - 1)*4;
函数原型:
typedef HRESULT (__stdcall *TDrawIndexedPrimitive)(LPDIRECT3DDEVICE9 pDevice,D3DPRIMITIVETYPE type,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount);
之前网上的透视之类都是硬编码,在这里我们为了多系统多版本通用,可以写个函数获取DX接口
bool GetD3DAdr(){
bool bRet = false;
LPDIRECT3D9 Tmp_pD3D = NULL;
LPDIRECT3DDEVICE9 Tmp_pd3dDevice = NULL;
if (NULL == ::GetModuleHandleA(D3D9DLLName)){
goto Exit0;
}
if (NULL == (Tmp_pD3D = Direct3DCreate9(D3D_SDK_VERSION))){
goto Exit0;
}
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof( d3dpp ) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
if (S_OK == Tmp_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,GetDesktopWindow(),
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,&Tmp_pd3dDevice)){
try{
g_Adr_DrawIndexedPrimitive = *(PDWORD)(*(PDWORD)Tmp_pd3dDevice + g_Oft_DrawIndexedPrimitive);
g_Adr_Present = *(PDWORD)(*(PDWORD)Tmp_pd3dDevice + g_Oft_Present);
LogDbg("g_Adr_DrawIndexedPrimitive:%.8X g_Adr_Present:%.8X",g_Adr_DrawIndexedPrimitive,g_Adr_Present);
bRet = true;
}
catch (...){
}
}
Exit0:
if( Tmp_pd3dDevice != NULL ){
Tmp_pd3dDevice->Release();
}
if( Tmp_pD3D != NULL ){
Tmp_pD3D->Release();
}
return bRet;
}
之前网上发此类HOOK实现都是直接修改代码JMP实现,不稳定又易被Crc检测
这里我们灵活处理下,写指针对应的内存,不修改任何代码即实现稳定Hook
对应处理如下:
//透视_Hook控制函数
void __stdcall HookD3D9_DrawIndexedPrimitive_Mem(bool bHook)
{
if (bHook){
g_Stride = GetPrivateProfileInt(Ini_App,Ini_Key_Stride,CN_D3D_Stride,g_Cfg);
g_KeyBoardSwitch = GetPrivateProfileInt(Ini_App,Ini_Key_KeyBoardSwitch,0,g_Cfg) > 0 ? true:false;
OldDrawIndexedPrimitive = (TDrawIndexedPrimitive)g_Adr_DrawIndexedPrimitive;
//此处写内存即可实现Hook
*(PDWORD)(*(PDWORD)(g_Param_DrawIndexedPrimitive) + g_Oft_DrawIndexedPrimitive) = DWORD(NewDrawIndexedPrimitive);
}
else{
OldDrawIndexedPrimitive = NULL;
//此处写内存恢复Hook
*(PDWORD)(*(PDWORD)(g_Param_DrawIndexedPrimitive) + g_Oft_DrawIndexedPrimitive) = g_Adr_DrawIndexedPrimitive;
}
HookKeyBoard(bHook);
}//透视_Hook实际处理函数
HRESULT __stdcall NewDrawIndexedPrimitive(LPDIRECT3DDEVICE9 pDevice,D3DPRIMITIVETYPE type,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount)
{
HRESULT hRet = S_OK;
IDirect3DVertexBuffer9* pStreamData = NULL;
UINT iOffsetInBytes,iStride;
if(D3D_OK == pDevice->GetStreamSource(0,&pStreamData,&iOffsetInBytes,&iStride)){
pStreamData->Release();
}
if(iStride == g_Stride){
pDevice->SetRenderState(D3DRS_ZENABLE,FALSE);//禁用Z轴缓冲
hRet = OldDrawIndexedPrimitive(pDevice,type,BaseVertexIndex,MinVertexIndex,NumVertices,startIndex,primCount);
pDevice->SetRenderState(D3DRS_ZENABLE,TRUE );//恢复Z轴缓冲
}
else{
hRet = OldDrawIndexedPrimitive(pDevice,type,BaseVertexIndex,MinVertexIndex,NumVertices,startIndex,primCount);
}
return hRet;
}//降压_Hook控制函数
void __stdcall HookD3D9_Present_Mem(bool bHook)
{
if (bHook){
g_CpuSleep = GetPrivateProfileIntA(Ini_App,Ini_Key_CpuSleep,0,g_Cfg);
LogDbg("%d",g_CpuSleep);
OldPresent = (TPresent)g_Adr_Present;
*(PDWORD)(*(PDWORD)(g_Param_DrawIndexedPrimitive) + g_Oft_Present) = DWORD(NewPresent);
}
else{
OldPresent = NULL;
*(PDWORD)(*(PDWORD)(g_Param_DrawIndexedPrimitive) + g_Oft_Present) = g_Adr_Present;
}
}//降压_实际处理函数
HRESULT __stdcall NewPresent(LPDIRECT3DDEVICE9 pDevice,const RECT *pSourceRect,const RECT *pDestRect,HWND hDestWindowOverride,const RGNDATA *pDirtyRegion)
{
HRESULT hRet = S_OK;
if (-1 == g_CpuSleep){ //黑屏,直接返回
Sleep(168);
return hRet;
};
//执行原函数
hRet = OldPresent(pDevice,pSourceRect,pDestRect,hDestWindowOverride,pDirtyRegion);
//Sleep降压
if (g_CpuSleep > 0){
Sleep(g_CpuSleep);
}
return hRet;
}至此,显然会发现,写指针对应的内存实现的Hook获取到g_Param_DrawIndexedPrimitive的地址,也就是对应函数原型中的Device才是关键所在
在此提供3个方法
1.直接任意Hook一个涉及到Device且频繁调用的函数,比如Hook DrawIndexedPrimitive来获取参数(速度快,但是有一定概率被Crc校验检测到)
void __stdcall GetD3DParam_Hook()
{
while (0 == g_Param_DrawIndexedPrimitive){
//这边如此操作,是为了降低被针对性Crc检测到概率
HookDrawIndexedPrimitiveParam(true);
Sleep(2);
HookDrawIndexedPrimitiveParam(false);
Sleep(168);
}
}具体Hook,这里我们可用用CmdApi的快速Hook接口实现
//获取参数_Hook控制函数
void __stdcall HookDrawIndexedPrimitiveParam(bool bHook)
{
if (bHook){
OldDrawIndexedPrimitiveParam = g_InitIO.Api.HookCodeEx((void*)g_Adr_DrawIndexedPrimitive, &NewDrawIndexedPrimitiveParam);
}
else{
g_InitIO.Api.UnHookCodeEx(OldDrawIndexedPrimitiveParam);
}
}
//获取参数_Hook实际处理函数
void __stdcall NewDrawIndexedPrimitiveParam(PHookCodeExParam32 pParam)
{
try{
if (0 == g_Param_DrawIndexedPrimitive){
g_Param_DrawIndexedPrimitive = *(PDWORD)(pParam->dwEsp + 4);
LogDbg("NewDrawIndexedPrimitiveParam %.8X %.8X %.8X",*(PDWORD)(pParam->dwEsp),g_Param_DrawIndexedPrimitive,*(PDWORD)(*(PDWORD)(g_Param_DrawIndexedPrimitive) + g_Oft_DrawIndexedPrimitive));
}
}
catch (...){
}
}2.全进程暴力搜索(速度慢,但是不用修改任何代码)
DWORD __stdcall GetD3DParam_Search()
{
DWORD dwRet = 0;
DWORD dwTmp;
DWORD dwCurrent = DWORD(GetModuleHandleA(NULL));
DWORD dwMax = 0x7F000000;
while (dwCurrent < dwMax){
dwTmp = dwCurrent;
if (!IsBadReadPtr((void*)(dwTmp),4)){
dwTmp = *(PDWORD)dwTmp + g_Oft_DrawIndexedPrimitive;
if (!IsBadReadPtr((void*)(dwTmp),4)){
if (*PDWORD(dwTmp) == g_Adr_DrawIndexedPrimitive){
dwRet = dwCurrent;
LogDbg("GetD3DParam_Search %.8X %.8X",dwRet,g_Adr_DrawIndexedPrimitive);
break;
}
}
}
dwCurrent += 4;
}
return dwRet;
}3.游戏中Device一般都是存在一级基址中的,目前还未遇到不是的网游
于是针对每个具体的游戏,我们又有了速度最快,又不用修改任何代码的获取方式
在主模块搜索一级基址
void __stdcall SearchBaseAdr(DWORD dwParam)
{
bool bFound = false;
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32 ={0};
if (0 == dwParam){
goto Exit0;
}
//创建进程快照
hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId());
if(INVALID_HANDLE_VALUE == hModuleSnap){
goto Exit0;
}
//递归枚举模块
me32.dwSize = sizeof(MODULEENTRY32);
if(::Module32First(hModuleSnap,&me32))
{
do{
DWORD dwCurrent = DWORD(me32.hModule);
LogDbg("Module %.8X %.8X %s",dwCurrent,me32.modBaseSize,me32.szExePath);
DWORD dwOft = 0;
while (dwOft <= me32.modBaseSize){
if (!IsBadReadPtr((void*)(dwCurrent + dwOft),4)){
if (*(PDWORD)(dwCurrent + dwOft) == dwParam){
LogDbg("BaseAdr %.8X %.8X",dwCurrent,dwOft);
}
}
dwOft += 4;
}
}while(::Module32Next(hModuleSnap,&me32));
}
Exit0:
if(INVALID_HANDLE_VALUE != hModuleSnap){
CloseHandle(hModuleSnap);
}
LogDbg("SearchBaseAdr End");
return;
} 根据搜索结果,定位提取相关的特征码,在程序中用CmdApi的特征码扫描接口处理下,这样对于具体的游戏,就可以无障碍获取到Device,写指针实现Hook既然也就不存在问题
关于透视,看上面的Hook对应处理函数就知道,还有个关键点即要过滤Stride值,
针对不同的D3D游戏还要针对性处理下,于是就需要写个通用的调试函数便于获取这个值
此处实现方式就是Hook游戏窗口处理函数,处理上下按键消息,在桌面DC绘制以及Dbg输出调试值
对于本插件,只需要把游戏的类名,填写到配置文件中即可
游戏的类名可通过Spy++类工具获取,也可自己写个窗口遍历把属于本进程的打印出来
//输出调试的Stride值
void DrawDC()
{
HDC TmpDc = GetDC(NULL);
char aBuff[26] = {0};
sprintf_s(aBuff,20,"Stride:%d ",g_Stride);
TextOut(TmpDc,0,0,aBuff,strlen(aBuff));
ReleaseDC(NULL, TmpDc);
LogDbg("%s",aBuff);
}//新窗口处理函数
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if(WM_KEYDOWN == uMsg){
if(VK_UP == wParam){
g_Stride += 1;
DrawDC();
}
if(VK_DOWN == wParam){
g_Stride -= 1;
DrawDC();
}
}
return CallWindowProc(g_OldlWindowProc,hwnd,uMsg,wParam,lParam);
}对于逆战、枪神纪等针对性检测较严的游戏,现有的插件还不足以胜任透视,可根据此思路再往下面挖一挖,运用DX的一些自有特性,还是可以实现的.

[D3D9]
Stride=44
GameClass=CrossFire

[D3D9]
Stride=12
GameClass=LaunchUnrealUWindowsClient

[D3D9]
Stride=12
GameClass=LaunchUnrealUWindowsClient

[D3D9]
Stride=72
GameClass=Gamebryo Application

[D3D9]
Stride=64
GameClass=NETEASE-XHEROES-APP
CoolCmd程序下载以及交流QQ群:101564837
最后,附上全部插件源码
CoolCmd_通用D3D降压透视插件源码_VC.rar
