d3d透视逆向篇:第5课:D3D9游戏黑屏优化2 DrawIndexedPrimitive
程序员文章站
2022-07-02 11:37:51
...
本逆向安全章节是我业余之间编写的,也借鉴了前辈的经验来给大家做一个简单的文字逆向安全教程,欢迎大家讨论和指正,共同学习。禁止非法用途。教程我从最简单的开始给大家做一个简单的讲解d3d 类的3d游戏和fps游戏黑屏、透视等功能。后面有更多的经验分享给大家。
这节课还是用老掉牙的方法,对上节课完善。这节课目的是:
1.你想你实现的一些功能不想给别人看到
2.优化cpu和GPU
3.这个知识后期做透视做准备
技术讨论QQ:2273545181 Q群:550839408
DrawPrimitive渲染 虚函数,不能根据函数名称获取函数地址 HOOK?
DrawIndexedPrimitive渲染
SetRenderState() 渲染状态
BeginScene开始渲染
EndScene结束渲染
Present显示后置缓冲的动画
HRESULT DrawIndexedPrimitive (
D3DPRIMITIVETYPE Type,
INT BaseVertexIndex, UINT MinIndex,
UINT NumVertices, UINT StartIndex,
UINT PrimitiveCounts);
偏移计算=5F097740-5f040000 = 57740 Loadlibrary
公式: DrawIndexPrimitive函数地址=模块D3D9基地址+偏移
//HMODULE hm = LoadLibrary("d3d9.dll"); //575F0
DWORD addr_real_drawPrimitive = (DWORD)hm + 0x575F0; //挂钩 addr_real_drawPrimitive变量的值就是函数地址
HookDetours(&(PVOID&)addr_real_drawPrimitive, (PVOID)NewDrawPrimitive, GetCurrentThread()); //挂钩 & 引用意思 & 取地址
DWORD addr_real_drawIndexedPrimitive = (DWORD)hm + 0x57740; //挂钩 addr_real_drawIndexedPrimitive变量的值就是函数地址
HookDetours(&(PVOID&)addr_real_drawIndexedPrimitive, (PVOID)NewDrawIndexedPrimitive, GetCurrentThread());
MessageBox(NULL, "暂停", NULL);
UnHookDetours(&(PVOID&)addr_real_drawIndexedPrimitive, (PVOID)NewDrawIndexedPrimitive, GetCurrentThread());//卸载挂钩
UnHookDetours(&(PVOID&)addr_real_drawPrimitive, (PVOID)NewDrawPrimitive, GetCurrentThread()); //卸载挂钩
汇编分析
5F097740 mov edi,edi
5F097742 push ebp
5F097743 mov ebp,esp
5F097745 push 0FFFFFFFFh
5F097747 push 5F0B8BB8h
5F09774C mov eax,dword ptr fs:[00000000h]
5F097752 push eax
5F097753 sub esp,24h
5F097756 mov eax,dword ptr ds:[5F191264h]
5F09775B xor eax,ebp
5F09775D mov dword ptr [ebp-14h],eax
DrawIndexedPrimitive参考函数说明资料:
参数1:D3DPRIMITIVETYPE type-图元类型
使用的图元类型,这个比较好理解,D3D只能绘制三种图元-点、线和三角形,其中线和三角形又细分为线列表(LINELIST)、线条带(LINESTRIP)、三角列表(TRIANGLELIST)、三角条带(TRIANGLESTRIP),根据实际需要选择使用某一种图元,这不会影响最终的绘制效果,但理论上讲,同样多边形数目的物体,使用三角形要比使用线绘制效率高(一个三角形=3条线),而条带比列表要节省顶点数量,同样n个顶点,线列表能绘制n/2条线,线条带能绘制n-1条线,同样,三角列表能绘制n/3个三角形,而三角条带能绘制n-2个三角形,不过经过D3D最优化处理后,绘制条带或列表的性能差别并不大。
参数2:INT BaseVertexIndex-起始顶点索引
MSDN中的原话是这样:Offset from the start of the vertex buffer to the first vertex.
“从顶点缓冲的起始位置到第一个顶点的偏移量”,第一个顶点不就是起始位置么?不一定是,DrawIndexedPrimitive这个函数接受从顶点缓冲的任何一个位置开始读入顶点数据,你可以从index0的位置开始读取顶点数据,那这时BaseVertexIndex就是0,也可以从index2的位置开始读取数据,那这时BaseVertexIndex就是2,如此类推。
参数3:UINT MinVertexIndex-最小顶点索引(这里MSDN应该是个笔误,写成MinIndex了,应该是MinVertexIndex)
MSDN:Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex.
本次函数调用中,顶点缓冲中最小的顶点索引。MSDN解释中我认为最关键的一句是“relative to BaseVertexIndex”-“相对与起始顶点”,即MinVertexIndex实际上也是个相对偏移量,例如,起始顶点是index2,MinVertexIndex是0,那么实际是从index2读入数据,起始顶点是index1,MinVertexIndex是1,那么实际也是从index2读入数据,即实际上的第一个顶点位置在BaseVertexIndex+MinVertexIndex处,最终偏移量=BaseVertexIndex+MinVertexIndex,这里很容易产生误解,就是认为MinVertexIndex是指顶点索引的最小值,即总是0,如果这样那MinVertexIndex这个参数就没有意义了。当初MS如果把这形参声明为OffsetRelativeToBaseVertexIndex应该是更明确一点,不会让人产生误解。
另一个问题就是,理论上这个偏移量如果是负值也可能是有意义的,例如BaseVertexIndex是1,而MinVertexIndex是-1就是指的顶点缓冲中的第一个顶点,这是有意义的,但实际中如果给MinVertexIndex赋负值我没有试验过。一般情况下MinVertexIndex都是0。
参数4:UINT NumVertices-顶点数量
MSDN:Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex.
本次调用所使用的顶点数量。MSDN的解释依然(依旧)模糊,第一个顶点位于BaseVertexIndex + MinIndex这个我已经知道啦,还写这个干什么?这里的顶点数量不是指的顶点缓冲中的顶点总数量,如vb有6个顶点,“The first vertex is located at index: BaseVertexIndex + MinIndex”这句话是要告诉我们,计算顶点数量是要从BaseVertexIndex + MinIndex开始的,例如,我要画一条从顶点index4到顶点index5的线段,那么参数应该是这样的:
BaseVertexIndex=4
MinVertexIndex=0
NumVertices=2
NumVertices是2而不是6,即NumVertices不是顶点总数,而总是你绘制图元实际需要的顶点数,注意这个“实际需要”,指的是你用到了几个顶点,这取决于索引缓冲的绘制这个图形所使用的不同的顶点个数(可能不太好理解,看下面的例子就明白了)。1条线段需要2个顶点,所以就是2,也许这形参声明为NumVerticesUsedForDrawing更明确一些。
参数5:UINT StartIndex-起始索引
又是一个Index,如果把这个形参声明为StartIndexOfIndexBuffer,我想就不会头晕了,这个起始索引指的是索引缓冲中的起始索引,也就是指索引缓冲数组的下标(顶点缓冲和索引缓冲都是用数组表示)-ib[StartIndex]。
参数6:UINT PrimitiveCount-图元数量
这个没什么难度,有一点要注意,对于绘制的几何图形而言,n-2和n/3指的都是索引的数量而不是顶点的数量,也就是说n是索引缓冲中索引的数量。
下面就是黑屏优化效果。左边是黑屏,右边是某f测试。(仅仅测试)
技术测试,禁止非法用途。