Register | Login | |||||
Main
| Memberlist
| Active users
| Calendar
| Chat
| Online users Ranks | FAQ | ACS | Stats | Color Chart | Search | Photo album |
| |
0 users currently in The Pit of Despair. |
Acmlm's Board - I3 Archive - The Pit of Despair - DirectX API calls in assembler, step-by-step. | New poll | | Thread closed |
Add to favorites | Next newer thread | Next older thread |
User | Post | |||
blackhole89 Moronic Thread Bodycount: 17 (since 2006-08-21 09:50 EST) F5 F5 F5 F5 F5 Since: 12-31-69 From: Dresden/SN/DE Last post: 6297 days Last view: 6295 days |
| |||
What would one need this for?
The particular goal I had in mind when working this out was looking up the call of a specific Direct3D call in the disassembly of a Ragnarok Online client (I wanted to hack it to change the scene's background colour, so it was a call to IDirect3DDevice::Clear). But of course, you can ultimately do all you want with it, up to writing your own DirectX applications in assembler. What tools does one need? - A C/C++ header of whatever DirectX API you are working on. In the example I give here, it is d3d.h. - The disassembler of your choice. I recommend IDA or SoftICE (google for that one). - A program that can search using either wildcards or regular exp So how would this be done? First off, disassemble your binary to a text file or, if you use an integrated environment like IDA, just disassemble it. Next, let's assume you are, like me, looking for a call of IDirect3DDevice::Clear. Let's assume our application uses the IDirect3DDevice7 interface. There are multiple ways to guess the version of the interface used (like looking up DLL dependencies), but version 7 seems to be the most common nowadays anyway. Open up your header - in our case, it's d3d.h - and look up your interface definition; find the function you are looking for in it.
As you see, Clear is the 11th method from above. Memorize this number. It takes 6 parameters. Memorize that too. At this point, you should know Microsoft's COM interfaces all work in a similar way: when you initialize them using a DLL's API function, your small container structure is given a pointer to a virtual function table ("void *vf_ptr" in most implementations, but that doesn't matter). It points at an array of function pointers to the single interface functions somewhere in memory. x86 pointers are, natively, 32 bits - 4 bytes - long, so you can expect the pointer to Clear - remember it was the 11th method in the interface definition? - to be at (11-1)*4 = 40d = 28h. (We subtract 1 from 11 because the first method is at (vf_ptr+0).) Sadly, the way vf_ptr takes throughout the application is very different from one binary to another, so we can't realistically trace it. Nevertheless, the only way in x86 architecture to call an offset relative to a pointer is the opcode FF xx yy - call dword ptr [register xx + yy]. The next problem is the fact the compiler could have used any register to store vf_ptr in for the call. This is where the wildcard function of your disassembly viewer comes in handy: Search for call dword ptr [*+28h] (where * is the wildcard). You will get a number of hits, of which some might be not what you are looking for. A relatively safe way to figure out whether you have actually stumbled upon a DirectX API call is checking the number of push operations done before the call. Note that - push operations for a function call can go quite far back in the code flow. - It is also worth checking whether the type of arguments plus one matches (in this example, we expect 7 push operations). The additional argument is from the this pointer which is always added as a first argument to DirectX interface calls. - The order of the pushed arguments is reversed, so a call to f(a,b,c) looks like push c push b push a call f In the beforementioned example, we will stumble upon the following piece of code at some point.
As you see, the number of preceding push operations matches 7. Furthermore, the first argument obviously is a this pointer, as we know edx, which stores our vf_ptr here, is [ecx]; in x86 assembly, [ecx] means nothing else than "the contents of memory at the address in ecx". The two DWORDs are pushed as immediates, 1 and 3; the LPD3DRECT is passed from eax. Let's trace back eax's way.
As everybody knows, XORing something with itself effectively sets it to zero; this seemingly exotic procedure is very common among x86 compilers. For some reason, it is stored to var_10 on the stack while other arguments are being processed; before being pushed, it is retrieved from it again. Hence, we are passing 0 (NULL) as the LPD3DRECT parameter, thus clearing the entire viewport. For the D3DCOLOR value, we pass something passed as an argument to the surrounding subroutine. The D3DVALUE, which is just Direct3D API alias for IEEE 32-bit single-precision floating-point numbers, is passed as 3F800000h; if you have the free time and will to, you can get a hex editor and verify it is the DWORD representation of 1.0f, but as it is, you just will have to believe me on this one. As the last DWORD, our previously nulled eax is pushed. With this information, we can reconstruct the call that happened there to something like the following:
Based on this information, you could place hooks in that code and modify virtually anything about the call; in my modification of the client, I hooked in a piece of code that sets the D3DCOLOR to the value of the location in the binary's virtual address space where the current scene's fog colour is stored. Hopefully this will actually be of use to someone... half of my motivation to post this was to give an example of what this new forum section is good for, as I was the one who suggested it being added in the first place. -- blackhole89. |
Add to favorites | Next newer thread | Next older thread |
Acmlm's Board - I3 Archive - The Pit of Despair - DirectX API calls in assembler, step-by-step. | | Thread closed |