[Olly]Getting the D3D9 device pointer in MW2

Posts 17 of 7 · Page 1 of 1
[Olly]Getting the D3D9 device pointer in MW2
Since melodia had a question about d3d hooking I figured i'd write this up real quick(copied over from her forums):

---
A quick tut on getting the D3D device pointer for MW2

First load up iw4mp.exe into olly, and search for -> all intermodular calls.
Sort them by name by clicking on the 'Destination' button above the calls.

Start typing: Direct, you should now see two calls to Direct3DCreate9.
Follow the first one:
Code:
004FC856  |. E8 05D21800    CALL <JMP.&d3d9.Direct3DCreate9>
004FC85B  |. 8BF8           MOV EDI,EAX
004FC85D  |. 85FF           TEST EDI,EDI
004FC85F  |. 74 34          JE SHORT iw4mp.004FC895
004FC861  |. 8B07           MOV EAX,DWORD PTR DS:[EDI]
004FC863  |. 8B50 14        MOV EDX,DWORD PTR DS:[EAX+14]
004FC866  |. 8D4C24 08      LEA ECX,DWORD PTR SS:[ESP+8]
004FC86A  |. 51             PUSH ECX
004FC86B  |. 6A 00          PUSH 0
004FC86D  |. 6A 00          PUSH 0
004FC86F  |. 57             PUSH EDI
004FC870  |. FFD2           CALL EDX
004FC872  |. 85C0           TEST EAX,EAX
004FC874  |. 7C 17          JL SHORT iw4mp.004FC88D
Aww... no absolute addies for us to use(we could still hook Direct3DCreate9 tho, but they will detect that)

Let's try the other one:
Code:
0050DCAB  |. E8 B0BD1700    CALL <JMP.&d3d9.Direct3DCreate9>
0050DCB0  |. 85C0           TEST EAX,EAX ; test against itself
0050DCB2  |. A3 E4727306    MOV DWORD PTR DS:[67372E4],EAX ; BINGO! 0x67372E4 is where they store the device pointer!
0050DCB7  |. 75 13          JNZ SHORT iw4mp.0050DCCC ; jump if D3DCreate9 didnt return a null pointer
0050DCB9  |. 68 745B7800    PUSH iw4mp.00785B74                      ;  ASCII "Direct3D 9 failed to initialize
"
0050DCBE  |. 6A 08          PUSH 8
0050DCC0  |. E8 0B27FFFF    CALL iw4mp.005003D0
0x67372E4 is a pointer to the games IDirect3D9, from which they will call CreateDevice to have the actual device created.

CreateDevice is the 16th virtual function in IDirect3D9, 0x4 is the size of a pointer, 16 = 0x10, 0x4*0x10 = 0x40, so we want to look at where IW4MP
does something like the following:
mov ECX, DWORD PTR DS:[67372E4];
mov EAX, DWORD PTR DS:[ECX+40];
call EAX;

Now this is what I found:
Code:
0050DAB0   $ 83EC 10        SUB ESP,10
0050DAB3   . 53             PUSH EBX
0050DAB4   . 55             PUSH EBP
0050DAB5   . 8B6C24 24      MOV EBP,DWORD PTR SS:[ESP+24]
0050DAB9   . 56             PUSH ESI
0050DABA   . 57             PUSH EDI
0050DABB   . EB 03          JMP SHORT iw4mp.0050DAC0
0050DABD     8D49 00        LEA ECX,DWORD PTR DS:[ECX]
0050DAC0   > 68 085B7800    PUSH iw4mp.00785B08                      ;  ASCII "Creating Direct3D device...
"
0050DAC5   . 6A 08          PUSH 8
0050DAC7   . E8 0429FFFF    CALL iw4mp.005003D0
0050DACC   . 83C4 08        ADD ESP,8
0050DACF   . 33DB           XOR EBX,EBX
0050DAD1   . EB 0D          JMP SHORT iw4mp.0050DAE0
0050DAD3   . 8DA424 0000000>LEA ESP,DWORD PTR SS:[ESP]
0050DADA   . 8D9B 00000000  LEA EBX,DWORD PTR DS:[EBX]
0050DAE0   > 8B3D EC727306  MOV EDI,DWORD PTR DS:[67372EC]
0050DAE6   . 68 F8727306    PUSH iw4mp.067372F8
0050DAEB   . BE F4727306    MOV ESI,iw4mp.067372F4
0050DAF0   . E8 7BF0FFFF    CALL iw4mp.0050CB70
0050DAF5   . 8B5424 2C      MOV EDX,DWORD PTR SS:[ESP+2C]
0050DAF9   . 83C4 04        ADD ESP,4
0050DAFC   . 68 E8727306    PUSH iw4mp.067372E8
0050DB01   . 55             PUSH EBP
0050DB02   . 52             PUSH EDX
0050DB03   . 8B5424 30      MOV EDX,DWORD PTR SS:[ESP+30]
0050DB07   . 52             PUSH EDX
0050DB08   . A2 F0727306    MOV BYTE PTR DS:[67372F0],AL
0050DB0D   . A1 E4727306    MOV EAX,DWORD PTR DS:[67372E4]
0050DB12   . 8B08           MOV ECX,DWORD PTR DS:[EAX]
0050DB14   . 6A 01          PUSH 1
0050DB16   . 57             PUSH EDI
0050DB17   . 50             PUSH EAX
0050DB18   . 8B41 40        MOV EAX,DWORD PTR DS:[ECX+40]
0050DB1B   . FFD0           CALL EAX
0050DB1D   . 8BF0           MOV ESI,EAX
0050DB1F   . 85F6           TEST ESI,ESI
0050DB21   . 7D 28          JGE SHORT iw4mp.0050DB4B
0050DB23   . 6A 64          PUSH 64                                  ; /Timeout = 100. ms
0050DB25   . FF15 A4616D00  CALL DWORD PTR DS:[<&KERNEL32.Sleep>]    ; \Sleep
0050DB2B   . 83C3 01        ADD EBX,1
0050DB2E   . 83FB 14        CMP EBX,14
0050DB31   .^75 AD          JNZ SHORT iw4mp.0050DAE0
0050DB33   . 833D EC727306 >CMP DWORD PTR DS:[67372EC],0
0050DB3A   . 74 5B          JE SHORT iw4mp.0050DB97
0050DB3C   . C705 EC727306 >MOV DWORD PTR DS:[67372EC],0
0050DB46   .^E9 75FFFFFF    JMP iw4mp.0050DAC0
0050DB4B   > A1 E4727306    MOV EAX,DWORD PTR DS:[67372E4]
0050DB50   . 8B08           MOV ECX,DWORD PTR DS:[EAX]
0050DB52   . 8D5424 10      LEA EDX,DWORD PTR SS:[ESP+10]
0050DB56   . 52             PUSH EDX
0050DB57   . 8B15 EC727306  MOV EDX,DWORD PTR DS:[67372EC]
0050DB5D   . 52             PUSH EDX
0050DB5E   . 50             PUSH EAX
0050DB5F   . 8B41 20        MOV EAX,DWORD PTR DS:[ECX+20]
0050DB62   . FFD0           CALL EAX
0050DB64   . 85C0           TEST EAX,EAX
0050DB66   . 7C 1E          JL SHORT iw4mp.0050DB86
0050DB68   . 8B4C24 10      MOV ECX,DWORD PTR SS:[ESP+10]
0050DB6C   . 8B5424 14      MOV EDX,DWORD PTR SS:[ESP+14]
0050DB70   . 890D FC727306  MOV DWORD PTR DS:[67372FC],ECX
0050DB76   . 8915 00737306  MOV DWORD PTR DS:[6737300],EDX
0050DB7C   . 8BC6           MOV EAX,ESI
0050DB7E   . 5F             POP EDI
0050DB7F   . 5E             POP ESI
0050DB80   . 5D             POP EBP
0050DB81   . 5B             POP EBX
0050DB82   . 83C4 10        ADD ESP,10
0050DB85   . C3             RETN
0050DB86   > 8B45 00        MOV EAX,DWORD PTR SS:[EBP]
0050DB89   . A3 FC727306    MOV DWORD PTR DS:[67372FC],EAX
0050DB8E   . 8B4D 04        MOV ECX,DWORD PTR SS:[EBP+4]
0050DB91   . 890D 00737306  MOV DWORD PTR DS:[6737300],ECX
0050DB97   > 5F             POP EDI
0050DB98   . 8BC6           MOV EAX,ESI
0050DB9A   . 5E             POP ESI
0050DB9B   . 5D             POP EBP
0050DB9C   . 5B             POP EBX
0050DB9D   . 83C4 10        ADD ESP,10
0050DBA0   . C3             RETN
now take a closer look at the following, this is what CreateDevice looks like:
Code:
HRESULT CreateDevice(
  [in]           UINT Adapter,
  [in]           D3DDEVTYPE DeviceType,
  [in]           HWND hFocusWindow,
  [in]           DWORD BehaviorFlags,
  [in, out]      D3DPRESENT_PARAMETERS *pPresentationParameters,
  [out, retval]  IDirect3DDevice9 **ppReturnedDeviceInterface
);
And this:

Code:
0050DAFC   . 68 E8727306    PUSH iw4mp.067372E8 ; push parameter
0050DB01   . 55             PUSH EBP ; push parameter
0050DB02   . 52             PUSH EDX ; push parameter
0050DB03   . 8B5424 30      MOV EDX,DWORD PTR SS:[ESP+30] ; store one of the arguments to the function in EDX
0050DB07   . 52             PUSH EDX ; Push parameter
0050DB08   . A2 F0727306    MOV BYTE PTR DS:[67372F0],AL ; No idea
0050DB0D   . A1 E4727306    MOV EAX,DWORD PTR DS:[67372E4] ; Store the pointer to the IDirect3D9 pointer in EAX
0050DB12   . 8B08           MOV ECX,DWORD PTR DS:[EAX] ; Store the address of the actual IDirect3D9 in ECX
0050DB14   . 6A 01          PUSH 1 ; push parameter
0050DB16   . 57             PUSH EDI ; push parameter
0050DB17   . 50             PUSH EAX ; push parameter
0050DB18   . 8B41 40        MOV EAX,DWORD PTR DS:[ECX+40] ; Store the pointer to CreateDevice in EAX
0050DB1B   . FFD0           CALL EAX ; Call EAX, which points to CreateDevice
Since EDX is pushed twice, I just discard one since I have no idea what i'm doing.
That leaves with 6 pushed parameters, since they're pushed in reverse order(last in first out), it would mean the last parameter of CreateDevice is pushed first.
Code:
PUSH iw4mp.067372E8
OH SHIT! COULD THAT BE THE POINTER TO THE DEVICE POINTER? let's hope so, since I am unable to test

IDirect3DDevice9 *myDevicePointer = *(IDirect3DDevice9**)0x067372E8;

I hope this helped!

Have fun,
Hell_Demon
The device class is IDirect3DDevice9, not IDirect3D9. Though, your method still works. You can replace the CreateDevice function with your own and log the last parameter ( returned device pointer ) then start from step 1 using that pointer but for the device class.

Geewd job HD.
Quote Originally Posted by Void View Post
The device class is IDirect3DDevice9, not IDirect3D9. Though, your method still works. You can replace the CreateDevice function with your own and log the last parameter ( returned device pointer ) then start from step 1 using that pointer but for the device class.

Geewd job HD.
I realised that like right after I posted it, but had to go so was unable to edit my post.
I'll go back to olly see where they store the device pointer(since I wan't to do it without changing anything in the game itself)

edited main post to report my new finding
To find it, just use the virtual method to get CreateDevice function, follow it and look in the stack. Last parameter is the device. In my "Hooking members of IDirect3DDevice9" tutorial, the inline assembly method I showed is what you should do in olly to find the function using the virtual table.
Quote Originally Posted by Void View Post
To find it, just use the virtual method to get CreateDevice function, follow it and look in the stack. Last parameter is the device. In my "Hooking members of IDirect3DDevice9" tutorial, the inline assembly method I showed is what you should do in olly to find the function using the virtual table.
wouldnt that require the game to be running?

I doubt this is going to work

Code:
IDirect3DDevice9 *myDevicePointer;

HRESULT (*oDrawIndexedPrimitive)(D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount);
HRESULT xDrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType,INT BaseVertexIndex,UINT MinVertexIndex,UINT NumVertices,UINT startIndex,UINT primCount)
{
	if(primCount == 1337)
	{
		myDevicePointer->SetRenderState(D3DRS_ZENABLE, FALSE);
	}
	HRESULT ret = oDrawIndexedPrimitive(PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
	if(primCount == 1337)
	{
		myDevicePointer->SetRenderState(D3DRS_ZENABLE, TRUE);
	}
	return ret;
}

unsigned long HookVTableOffset(unsigned long address, int offset, unsigned long newFunc)
{
	unsigned long retval = *(unsigned long*)(address+(0x4*offset));
	DWORD dwOld;
	VirtualProtect((LPVOID)(address+(0x4*offset)), 4, PAGE_EXECUTE_READWRITE, &dwOld);
	*(unsigned long*)(address+(0x4*offset)) = newFunc;
	VirtualProtect((LPVOID)(address+(0x4*offset)), 4, dwOld, &dwOld);
	return retval;
}

void SetupHook()
{
	myDevicePointer = *(IDirect3DDevice9**)0x067372E8;

	oDrawIndexedPrimitive = (HRESULT (__cdecl *)(D3DPRIMITIVETYPE,INT,UINT,UINT,UINT,UINT))HookVTableOffset(*(unsigned long*)myDevicePointer, 82, (unsigned long)xDrawIndexedPrimitive);
}
82 is the offset in the vtable for DIP
Hmm, I meant the inline assembly part. You have address for the created IDirect3D9 instance, using that method you can find CreateDevice, log the last parameter ( look in the stack window ) then use the method again to get the IDirect3DDevice9 address.

I think your code will work, type definitions for those long annoying casts
I don't like typedefs, never learned how to use those :P
Posts 17 of 7 · Page 1 of 1
This thread is closed for replies.

Similar Threads

Tags for this Thread

None

Need help?