欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]CoolCmd_通用D3D降压透视插件(附源码)

[原创]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

上传的附件 CoolCmd_通用D3D降压透视插件源码_VC.rar
穿越火线.jpg
逆战.jpg
枪神纪.jpg
终极火力.jpg
突击英雄.jpg

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功