Drawing on another Direct3D program’s viewport

Update: See the post for the new version.

The theme of the moment is DLL hooking, and so I thought I’d present an applied example. I already explained how Fraps works, and since I’ve recently been roped into writing a similar tool for a stranger, I thought I’d share the wealth. There isn’t much new material here, but people like examples with source code, so you can download the DLL source (C++) from the project page.

Bioshock Hook Screenshot

If you don’t know how to inject this DLL into a foreign process, then you’ll need to read my previous post or wait for the injection framework I’m working on. But once it’s injected call the Initialise method, via CreateRemoteThread or otherwise, to install the hooks. It works with any program that uses IDirect3DDevice9::Present (or IDirect3DSwapChain::Present) to render, which is probably all of the DirectX 9 games. Similarly, invoke Release to remove the hooks. The source is fairly self-explanatory, with a few exceptions.

  • It’s not safe for 64-bit consumption, though this should be obvious.
  • While there’s no reason it can’t be made to work with Unicode, I’ve written everything in ASCII, for simplicity.
  • By default, the DLL will increase its own reference count to prevent it being unloaded prior to termination of the host process. This is because there is a small risk of the DLL being unloaded by one thread, while a hooked function in another returns to the now dead memory. I figured that it’s best to waste a little bit of everybody’s memory than to crash unnecessarily.
  • The d3d9.dll function addresses (and prologues) are hard-coded, or at least their offsets are. While this may look very unprofessional and rather risky, I can assure you that it’s quite safe. The alternative would be to hack up some virtual-function tables and that’s a whole other story for a whole other post.
  • You may notice that the compiled DLL is dependent upon D3DX. This isn’t necessary for the hook itself, but I used ID3DXFont in my example for demonstrative purposes. The only reason I mention this is that there is no way to guarantee the existence of any D3DX DLLs on a DirectX 9 machine, and distributing them yourself is in violation of the DirectX Runtime EULA. So if you happen to need to distribute this code, you’ll either need to carry the huge runtime installer around, or avoid using D3DX altogether.

Update:

  • The soft-hooks used here will cause problems with PunkBuster if applied to any of its monitored functions. If you need to do this then you’ll have to be a bit cleverer.
  • The source assumes that the graphics device will never become invalid. If you suspect that this isn’t the case (which will be true for any full-screen game at a minimum) then you’ll need to add the appropriate sanity checks (see IDirect3DDevice9::TestCooperativeLevel) before attempting to render anything, lest you want to crash and burn.

35 Responses to “Drawing on another Direct3D program’s viewport”

  1. I tried this on BF2, BF2142 and Crysis and it didnt work.
    Does it only work with BioShock? I am using the DirectX SDK from February 2007.

    After injection (WinJect) It said it injected it ok, but didnt do anything (No text painted on screen)

    Any Ideas?

  2. Never mind I got it working. You need to call Initilise as such in dllmain attach:

    CreateThread(0, 0, Initialise, 0, 0, 0);

  3. Good to hear it works for you.

    There’s no need to create a separate thread from DllMain as Initialise returns almost immediately. However I wouldn’t recommend calling Initialise from DllMain as it invokes a lot of heavy work while in a fragile state and could potentially create a deadlock. Perhaps moreover, if the DLL is injected early on (in particular, by a system-wide hook) then the Initialise event is liable to fail since d3d9.dll may not have been loaded.

    For this reason it’s a much better idea to periodically invoke Initialise by a remote call (i.e. from a different process) until it succeeds. I suggested using CreateRemoteThread for this, with a similar method to that described in the DLL Injection post.

    Greg

  4. I took care of that by doing this:

    DWORD D3d9Base = NULL;
    for (; D3d9Base == NULL; Sleep(250))
    D3d9Base = (DWORD)GetModuleHandle(”d3d9.dll”);

  5. Sorry for cluttering up your comments (to bad no edit button)
    I was in a rush those last few comments. I wanted to say thank you for your
    blogs, I find them very interesting.

    I added DrawIndexPrimitive(….) to your source, and it seems to work nicely. I am going to be testing against PunkBuster later tonight to see if they catch these nice new hooks. I see no need to hook begin/end scene as I can do overlays in Present as you have noted.

    Thanks Again for your hard work, and very nice clean code. I am shocked someone even goes as far to clear/clean the font device/resource.

    KozmoK

  6. Greg,

    Here are my findings. Using your code unmodified (except for minor changes) Crysis crashes/locks up after map loads. If I inject while into game/map its fine until next reload.

    Adding DrawIndexPrimitive(…) hook with the same method you used for Present patching original and patched, I get a Disallowed Program/Driver from PB. PB is scanning the first 15 bytes or so for this function, so I think it sees the JMP.

    I guess just hooking EndScene with a normal detour (which is not scanned by PB) will give you the same results without all the patching for what your doing (Overlay only)

    KozmoK
    kozmo_1970 AT Hotmail dot c0m

  7. Thanks for sharing, KozmoK.

    I’m aware that these soft hooks are PunkBuster-incompatible for the more sensitive functions, but I have no plans to address that. Detours may well be a solution - maybe you’d be so kind as to let us know if you get it working that way.

    And about the crashes in Crysis. I’ve experienced the same thing with a few other programs. The problem lies with a lost device. Presumably, Crysis switches graphics mode between maps, which invalidates the device. Any subsequent rendering calls predictably cause a crash. The situation could quite easily be rectified with a call to TestCooperativeLevel. Maybe I’ll add this to the source when I get a chance.

    Greg

  8. Hey, just wanted to let you know I really DIG your injection style. I’ve been doing injection on and off for a while now and i’ve never seen anything so simple, elegant and well explained. Good work! I think i’ll get my ass in gear and start writing some hooks using your injection code!

  9. Hi,

    Greg - thanks for a great and a very interesting blog. I wondered if you had your code tested on Vista? I’ve finally managed to get it running, using the dll injection framework (I had to add _declspec(dllexport) extern “C” to Initialize and then called DLLInjection::CallThreadProc), but VerifyAddresses() fails on the first comparison.

    any ideas?

    thanks much
    Joel

  10. Hi Joel.

    Yes, the code has been tested on Vista, but like you say, it needs some modification. The main source of problems is that Vista uses a different version of d3d9.dll from XP, with the functions having different addresses and prologues. All that would need changing are the hard-coded addresses and the five-byte buffers of their default opcodes (and some conditional checks, if both OSs need to be supported simultaneously). Note that this all lies under the assumption that the DLL is uniform and not a part of the side-by-side assemblies scheme - I think this is the case, but haven’t verified it.

    I would post the details of the source changes right here, but I don’t have access to a Vista computer. It would only take a few minutes in a debugger if you know what you’re doing, but the learning-curve is perhaps a little steep if you’ve never debugged a foreign binary before. Alternatively, you could get creative and implement the virtual-address resolution discussed here. Drop me an email if you want to go into further detail.

    Good luck
    Greg

  11. thanks. I indeed saw a nice discussion with Mark Russinovich - http://channel9.msdn.com/Showpost.aspx?postid=365911 - where he talks how in Vista they are loading dlls in random locations, to deter hackers, and I wonder whether this has something to do with it (?). I’ve switched to a vtable-supported injection (took some nice code from Taksi, you might want to check it out). it still crashes, still working on it :). I’m planning now to use a debugger, but the thing is, the game I’m trying to hook (Call of Duty) is full screen. any idea if I can make it windowed? any recommendations for a debugger?

    thanks,
    J

  12. A Direct3D program can be forced to run ‘windowed’ by altering the D3DPRESENT_PARAMETERS struct passed to IDirect3D9::CreateDevice - this can be done by means of a hook just like the others. Set Windowed to 1 and FullScreen_RefreshRateInHz to 0. This will at least prevent the program from acquiring exclusive access to the screen (and so allows you to flip back and forth to the debugger) but depending on other factors, the device-context may not be bound to a movable window in the way we’re all used to. I’ll have a poke around with this and report any success I have.

    As for a debugger recommendation, I would advise against anything other than OllyDbg, which gets my full approval.

    And as for Vista’s DLL-basing, this may be a problem. As of XP, all DLLs are necessarily relocatable with the exception of ntdll, user32 and kernel32, which always reside at the respective addresses. The Direct3D hooking sample itself doesn’t take advantage of this, but the DLL Injection class does. The fix simply involves using EnumProcessModules to find the base of kernel32.dll in the remote process and updating the LPTHREAD_START_ROUTINE in DLLInjection::InjectDLL accordingly, but as I say, I don’t have Vista so I can’t make any promises. It may be worth a try, replacing the call to GetProcAddress with the corresponding GetRemoteProcAddress (in InjectDLL), as this would do exactly that, IIRC.

    Greg

  13. Hi Greg,

    Thank you for the great articles on this subject. Do you think that Detours’ way (copying target function into a buffer so it doesn’t need to be continuously patched and repatched) is a bit more fail-safe? Would it be possible that the application makes concurrent calls to those Direct3D methods, making the Hook methods call a Present/Clear/SwapChain that is NOT backed up because of concurrency with a simultaneous call?

    If anyone else has suggestions, they would be appreciated. I’m wondering what is the most solid way to do it (less prone to PunkBuster and such). I want to intercept Direct3D calls in games to control a device.

    Thank you,
    Alexandre

  14. As of Direct3D 10, at least, multithreading is not supported. That means that you need not concern yourself with asynchronous calls to a function while its in the process of hooking or unhooking itself. I couldn’t vouch for Detours’s effectiveness in this situation, but from what I can tell it’s a powerful and mature library - certainly worth a look. I shall have to make a post about Detours once I’ve fully evaluated it, as it bears much more relevance to my recent work than I initially understood.

    From what I’ve gathered, the Direct3D functions mentioned here are in PunkBuster’s demilitarised zone, so to speak. This is presumably to maintain compatibility with Fraps, XFire and the likes. However, if you try to mess with DrawIndexedPrimitive or its friends then you’ll quickly find yourself in trouble.

  15. Ok so I got the hook installed (i believe) but when i try and call the initialize method it crashes the program (not mine but the game). Here is what I got. This is in addition to the code your provided for injecting the dll… Can you see if Im doing something wrong?

    DWORD thread_id1 = 1;
    HMODULE dllhook = GetModuleHandleA(”dllhooktest”);
    LPTHREAD_START_ROUTINE remote_lla1 = reinterpret_cast(GetProcAddress(dllhook, “Initialise”));

    HANDLE thread1 = CreateRemoteThread(
    process_handle,
    NULL,
    0,
    remote_lla1,
    NULL,
    0,
    &thread_id1);

  16. Joey, are you running XP SP1/2? The code here doesn’t work for anything else, and will presumably crash before very long. I’m now running Vista and so I’ll get to making this sample compatible as soon as I get the opportunity. If you really can’t wait for that then you’re free to replace the relevant constants with the appropriate values yourself.

    If that isn’t the problem then you could give me a little more input so as to fix the problem. In particular, what OS are you running, which game are you injecting into and what are the details of the exception that’s raised? Are you checking all the return-values?

    Greg

  17. Hmm.. I am currently using vista and cant get the game to run in a vmware machine so eh. Im not sure what those constants should be for vista either. The game can be downloaded at http://www.xenimus.com.

  18. All you Vista-users, see the project update. I’ve tested it on Vista 64; would like confirmation that it also works on the 32-bit version.

    Greg

  19. OK so that worked with the Vertices.exe I was using but not with Xenimus. Im not sure why. And I was only able to make it work when I called initialize inside the dllmain. Im unclear on how to call dll methods using CreateRemoteThread. Any help with this would be awesome thanks!

  20. It seems to be injecting the dll (Ive tested it in other apps) into the apps (including xenimus) but it doesn’t want to execute the code in the dll for direc3d in Xenimus at all. Any Ideas on this?

  21. Joey , I seen you say your on Vista,like joel said above vista dynamically loads the d3d9..look at post 4 by kozmo and do base+0×0000.

    To find the offsets hook createdevice and log interface offset that you want.

  22. hmm i feel really dumb about this. If I didn’t mention before the other apps actually worked and displayed the text after the recent vista update… Ok so Im not exactly sure what you were saying (fairly new to all of this as its a learning moment) but I put that sleep method before GetModuleHandle like
    // Look for a suitable DLL
    char* address_d3d9 = NULL;
    for (; address_d3d9 == NULL; Sleep(250))
    char* address_d3d9 = reinterpret_cast (GetModuleHandleA(path_d3d9_dll));

    This may have been wrong im not sure but after I did this it just crashes the game. Btw Im putting Initialize in the dllmain because I dont know how to call it any other way…

  23. Microsoft explicitly dictate that nothing too involved should go into DllMain. This certainly includes an idle loop that waits for another DLL to load - indeed, the single-threaded nature of DirectX suggests that if the first GetModuleHandle fails then no amount of waiting around will do any good as the entire program is being blocked. So when the DLL-loading operation inevitably times out, the program doesn’t know how to continue.

    For some information on the CreateRemoteThread method of remote procedure calling, see this post. You need only adapt it to call Initialise rather than LoadLibraryA - I deliberately made Initialise have the same prototype for this reason.

    Alternatively, unless you’re using your own methods to inject the DLL (such as a system-wide hook - yuck) then you could get all the work done for you by my injection framework. Then the remote call would reduce to a single call to:

    injection.CallThreadProc(”Initialise”, NULL, 5000, exit_code);

    This will only work with a valid injection object, which means you won’t be able to inject via your own methods. If that’s necessary then you can still cannibalise the code to see how to make the call.

    Greg

  24. DWORD Base=(DWORD)GetModuleHandle(”d3d9.dll”);

    example:
    static DWORD dwBeginScene = Base + 0×47A30;

    example of beginscene:
    what i did to narrow it down was log the base address plus the offsets
    and did ( offsets - d3d9modualbase =0×47a30 )

    This way you grab the right offset everytime.

  25. Ok Ive looked at every way on the planet to actually inject the dll. Believe this is working ok… I think my issues are 100% with directx hooks. Considering I dont know much about directx its hard heh. In fact I believe Im using your framework and that post you have on injection to do it and I have a few other examples that all seem to work. They dont return errors or anything when injecting so I assume they are working. I think I understand where your going Matt… If I read right then Im assuming your saying I need to get the location of the d3d9.dll in memory and the offset is the function address so that I can do something with them. This is kind of where I get lost. Im not sure what to do when I get them or even how to get them I guess. I have another direct3d hook dll that uses a wrapper and detour but it didn’t seem to work either. When I open xenimus and inject the dll now nothing is crashing but no text is displayed. My only reason for doing this project is really to display text on the game. I have all the memory address mapped out for information I need and I need a way to display the info. I kind of would like to add images as well but we will see. So far my luck is horrible.

    I read at http://www.gamedev.net/community/forums/topic.asp?topic_id=359794 that if the game is loading the d3d9.dll manually via LoadLibrary then the method he showed would not work. Is this the case for your dll as well? Xenimus may do this but Im not sure or even know how to tell. I dont know maybe you could look at the game http://www.xenimus.com and see if you could make this work if you had time… Ill keep trying and learning. Btw thanks a ton for everyones help I definatly appreaciate it. Im finished with my novel now… heh

  26. Seems everytime I call HMODULE kernel32 = GetModuleHandleA(”dhook1.dll”); the value is 0. Does this mean its not being loaded into memory?

  27. I downloaded the Xenimus demo, and it seems there’s a very good reason the D3D9 hook isn’t working. While the website says that the game requires DirectX 9, and it does indeed reference the d3dx9_34 helper library, all the rendering is done in Direct3D 8. This is evident from the presence of d3d8.dll and the absence of d3d9.dll in the process’s address-space (and the fact that Initialise returns -1).

    So you could port my code to work with DX8, which should be fairly straightforward, if a little tedious, provided you can work a debugger. Failing that, I’m sure there are some other code samples out there targeted towards Direct3D 8. I’m afraid I’m not prepared to do the all work for you though, as DX8 is pretty dated and I’d rather not clutter up the code.

    Since we’re now off-topic, I’d appreciate it if you directed any further questions to me via email rather than commenting on the post.

    Good luck
    Greg

  28. Joey you might want to start with the d3d starter kit by az0rbix.

    I never pointed out good stuff greg. :>

    @ http://www.gamedeception.net

  29. Greg,

    The offsets you provided for Vista do not work under Vista x86. I can get a HMODULE of d3d9.dll just fine within the remote process so I add up the offset to it and the memcmp between the opcodes you provided and what is actually there is different. I even tried using your code directly instead of mine and the result is exactly the same. Does this have to do with what matt was saying regarding dynamic offsets? I didn’t quite get it.

    Additionally, was it hard for you to get those offsets? I’m not expecting you to show me how but I’m just wondering if it’s within the scope of a guy like me or if it requires quite a bit of reverse engineering experience. The reason I’m asking is because I will most likely need to intercept other methods. I think you mentionned OllyBdg, would that do that?

    Thanks,
    Alexandre

  30. I forgot to specify that it does work under XP with the same code. Can anyone else confirm?

    Thanks,
    Alexandre

  31. That’s a little worrying. Perhaps there is more than one version of d3d9.dll in the wild for 32-bit Vista. If that’s the case, I’ll have to resort to using the method in this post (which you evidently found and I hope answers your questions). That would involve a compromise somewhere along the line, though, as it requires an initialised IDirect3DDevice9 object in order to function.

    So indeed, can anybody confirm that the new offsets are causing problems?

  32. I am also interested how you got the offsets for the three functions you hooked, in order to hook the other functions using this method.

  33. Austin. Take a look here. There’s currently a little doubt surrounding the effectiveness of this method as I’ve received (but not yet investigated) reports of it producing entirely erroneous results. It worked for me though, so it’s certainly worth a try.

    If that doesn’t work out for you, it should be simple enough to code up a program with a call to the virtual function in question immediately after something easily identifiable (say, two successive calls to IsDebuggerPresent). Now by throwing the resulting executable at your debugger and setting an appropriate breakpoint you can trace into the call (which will almost certainly be of the form CALL ) and see where you end up. Once you have the absolute address of the function, subtract the base address of the DLL (where the PE header begins in memory) for the relative offset which can be used under all circumstances.

    For the record, it’s not generally too easy to find these function locations in a debugger using somebody else’s binary as the compiler trashes all easily processable mappings of function names to addresses. If for some reason you are entirely unable to debug your own Direct3D program then you’ll have to be a little creative and reverse-engineer the target to find the pertinent call, paying attention to stack parameters and comparing the arguments with those specified by the Direct3D docs.

  34. hook Direct3DCreate9->CreateDevice->ppReturnedDeviceInterface

    interface are in order in the d3d9.h

  35. […] By popular demand, I’ve updated the Direct3D 9 Hooking Sample to accommodate Windows Vista. The same binary should work on both Vista and XP. I’ve only tested it on Vista 64-bit, so it’d be nice to know if it works with Vista 32 or not. Other than this, most of the same caveats apply as last time. […]

Leave a Reply