I wrote this over at www.extalia.com as it was requested that I explained how to do it when I posted the beta of the Windower I made for one of the Need For Speed games. I am copy pasting it to here to share with everyone as thats the point of this section.
(Yes I wrote it, so I give myself permission to copy paste it lol..)
Credits goto:
- Wiccaan
- Microsoft (For the detours lib.)
- Azaril (For the overall idea from his old DX8 windower for FFXi.)
DirectX 9.0 Hooking via Injection via C++
- Ok, so beings that this was requested, I decided to write this up. I hope this helps someone and if not, sorry
Part 1. Introduction and Such
- Before we begin, let me go over what this does and how it works. Firstly, this main tutorial method is via injection. What this does is creates a remote thread in the process as its starting up and injects your hook and calls its DllMain (or main function of the DLL) and hooks the Direct3D9Create function. From there, when the game, or DX app you are using calls Direct3D9Create, it will call your hooked function instead of the original. When that happens, instead of using the default DirectX functions, it will use your wrapper "proxy" functions that are modified to your need and such. This allows you to add to the game, or app, such as wireframe, colored primitives, etc. Such things you see in hacks and so on.
Along with that, it opens up the window for any other extra feature(s) you can come up with on your own. The boundaries are small, just what you are able to do and such.
This tutorial is going to explain how to do the basics, create the hook, show an example wrapper, and explain a few small things along with the hook. I will include a full example as well that I will find some random free program to show you how it works and such in as well. I will add wireframe mode to a toggle key to show you how to add some of your own code into the hook as well.
Part 2. Tools Needed
- This is a small list of the things you will need to be able to use this tutorial.
- Microsoft Visual Studio 2005
- You do not need the full version to create things, you can use the express edition as far as I know to compile things as long as they are non-commercial (from what I was told). If not, find the full version on a warez site or be nice and purchase it
- Due to some unknown reason, the makers of the Detours library have removed the functions used in this tutorial from the new version. So you will need to download a precompiled version here:
http://home.comcast.net/~wiccaan/downloads/Detours.rar
- And again, another free download from Microsoft for developers. (Aren't they so nice to programmers? ^_^)
Download: http://msdn2.microsoft.com/en-us/xna/aa937788.aspx
Note: You need to have a real version of Windows for this download so if you don't, just google around for it. I don't think the version matters. I used the October 2006 update for most of my old projects, but have installed the June 2007 version and my stuff still compiles fine so either should be ok.
- A brain, patience, the willing to read and learn, coffee.
Part 3. Installing The Tools
- Firstly, all the tools are in installers, so simply install them to their respected locations. BUT!! I suggest you install Visual Studio first, as some of the other tools may install variables and paths for you to easily include things into your project without lots of crap work and such. So, install VS2005 first, then the others.
VS2005 installs default to C:\Program Files\Microsoft Visual Studio 8
DX SDK installs default to C:\Program Files\Microsoft DirectX SDK (m/y)
Adding The Paths To VS2005
- Next we need to add the paths to the extra used files in the detours and the DirectX SDK folders. This is so you don't have to have long ass include lines in your project, but instead the compiler will look inside the said folders for the included files in the project first. (But be warned, this can cause conflicts if you use more then 1 version of something that has the same file names!)
Open Visual Studio and goto Tools -> Options then click the + next to VC++ Directories. On the right, click the drop down box under "Show Directories For" and choose 'Include files'. Next click the folder icon and then the button that appears in the list box that says '. . .' and point it to the folder:
C:\Program Files\Microsoft DirectX SDK (October 2006)\Include
(change the paths for either of these if they differ)
Next choose 'Library files' from the right drop down list and then click the folder icon again, then the '. . .' button and point it to:
C:\Program Files\Microsoft DirectX SDK (October 2006)\Lib\x86
(change the paths if they differ)
- This is an updated step now due to an issue with Detours 2.1. To install Detours, you could put the detours.h and .lib files where ever you want on your system, just be sure to add their paths to the VS2005 IDE like you did for the DX SDK above. Or, as I will be using in this tutorial, make a new folder in your projects directory with the other source files (.h and .cpp files) and name it 'Detours' and place the files in there. This is probably going to be the easiest way to use them.
Now your IDE should be setup to use the DX SDK files with ease.
If you run into issues later on, just post the error and I will attempt to help you to the best of my abilities.
I'm not Jesus so don't expect me to fix every error.
-= I suggest taking a break here, use the rest room, drink coffee, kick a dog.. its gunna be a lot of reading now. =-
Part 4. Creating The Hook
- To start the hook, create a new DLL project in VS2005. To do that, open VS2005. Then goto File -> New -> Project. Select VC++ from the left box then on the right choose Win32 Project. Select a name for your project and a location for it.
For this tutorial I will be calling mine dxhook and the location will be on my desktop. I also suggest leaving Create directory for solution checked.
Now the Win32 Application Wizard should come up. Click on Application Settings on the left, select DLL under Application Type, and choose Empty Project under Additional Options. Then click finish. You will be dumped into the IDE of VS2005 in a new blank solution.
Although we will have precompiled headers turned off, we are going to make stdafx.cpp/.h files anyway since they are nice to have for includes that multiple files will use. So lets go ahead and add the main files we will be using in this.
To add new files to the project, right click in the solution explorer and select Add then select New Item from the sub menu. You are going to want to add 2 cpp files and 1 header file. So:
Add->New Item-> Header File, name it stdafx.h
Add->New Item-> C++ File, name it stdafx.cpp
Add->New Item-> C++ File, name it main.cpp
So you should have 3 files in your solution now.
- stdafx.cpp
Lets take care of the easier file first. Open stdafx.cpp by double clicking it in the solution explorer to open it in the code window in the center of the IDE. This file needs 1 line of code.
Code: |
#include "stdafx.h" |
- stdafx.h
Next, lets take care of the basic needs inside the stdafx.h file that will be used for the start of this project. So double click on stdafx.h in the solution explorer and lets add some code.
The first thing we will want to do is to get rid of some rather annoying warnings. If you are like me, I have the compiler set to treat warnings as errors. I like things to be as perfect as possible.
The first line of code we will want to add to stdafx.h is:
Code: |
#pragma once |
Code: |
#define DIRECTINPUT_VERSION 0x0900 #define _WIN32_WINNT 0x0500 |
Code: |
#include #include |
- main.cpp
Next, open the main.cpp file. This is where the hooking happens. We will be using Detours for our hooking functions because it's free and easy to use. I will not guarantee that it will be undetected for your game so be careful when testing.
The first line of code we want to add to this file is to include the stdafx.h file as it has the includes that will be needed for this file. So add:
Code: |
#include "stdafx.h" |
Code: |
#pragma comment(lib, "Detours/detours.lib") #include "Detours/detours.h" |
Code: |
HANDLE HookHandle = NULL; |
Code: |
DETOUR_TRAMPOLINE(IDirect3D9* WINAPI Real_Direct3DCreate9(UINT SDKVersion), Direct3DCreate9); |
Code: |
#ifdef __cplusplus extern "C" { #endif DETOUR_TRAMPOLINE(IDirect3D9* WINAPI Real_Direct3DCreate9(UINT SDKVersion), Direct3DCreate9); #ifdef __cplusplus } #endif |
Code: |
IDirect3D9* WINAPI Mine_Direct3DCreate9(UINT SDKVersion) { IDirect3D9* Direct3D = Real_Direct3DCreate9( SDKVersion ); IDirect3D9* Mine_Direct3D = new Direct3D9Wrapper( Direct3D ); return Mine_Direct3D; } |
Code: |
void HookAPI() { DetourFunctionWithTrampoline( (PBYTE)Real_Direct3DCreate9, (PBYTE)Mine_Direct3DCreate9 ); } |
Code: |
void UnhookAPI() { DetourRemove( (PBYTE)Real_Direct3DCreate9, (PBYTE)Mine_Direct3DCreate9 ); } |
Code: |
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if (ul_reason_for_call == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hModule); HookHandle = hModule; HookAPI(); } else if (ul_reason_for_call == DLL_PROCESS_DETACH) { UnhookAPI(); } return TRUE; } |
Code: |
#pragma comment(lib,"d3d9.lib") #include #include |
Part 5. The Wrapper
- Again I encourage a break here. This is where theres going to be a lot of coding. So prepare yourself for it
- First we want to open notepad and open the file located at:
C:\Program Files\Microsoft DirectX SDK (October 2006)\Include\d3d9.h
(Might differ for your version of the SDK but it will be called d3d9.h)
We are opening this because this file contains the functions that we need to add to our wrapper. This is where we get the info we need to understand what each part of DirectX does function wise. So scroll down in that file and you should find a few things we have already done above. Such as our new typedef, it's also used in this file as well as the function to create the instance of Direct3D. Keep scrolling till you find:
Code: |
DECLARE_INTERFACE_(IDirect3D9, IUnknown) |
Code: |
#include "stdafx.h" class Direct3D9Wrapper : public IDirect3D9 { public: Direct3D9Wrapper( LPDIRECT3D9 pDirect3D ); virual ~Direct3D9Wrapper(); IDirect3D9* Direct3D9; }; |
Code: |
/*** IUnknown methods ***/ STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; STDMETHOD_(ULONG,AddRef)(THIS) PURE; STDMETHOD_(ULONG,Release)(THIS) PURE; /*** IDirect3D9 methods ***/ STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE; STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE; STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE; STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE; STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE; STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE; STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE; STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE; STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE; STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE; STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE; STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE; STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE; STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE; |
Code: |
IDirect3D9* Direct3D9; }; |
- Now for this file, I am not writing everything out that will be in it here on the forums because its a shit ton of code. You have to rewrite all the functions that we just added to the .h file. But don't worry its real easy. You are simply creating the functions to return the normal values as if it was the real Direct3D instance.
First, lets add our includes and create the class construct/deconstruct.
So inside ID3D9Wrapper.cpp the includes you will want are:
Code: |
#include "ID3D9Wrapper.h" |
Code: |
Direct3D9Wrapper::Direct3D9Wrapper(LPDIRECT3D9 pDirect3D) { Direct3D9 = pDirect3D; } |
Code: |
Direct3D9Wrapper::~Direct3D9Wrapper() {} |
Code: |
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj); |
Code: |
HRESULT Direct3D9Wrapper::QueryInterface(const IID &riid, void **ppvObj) { return Direct3D9->QueryInterface(riid, ppvObj); } |
Code: |
STDMETHOD_(ULONG,AddRef)(THIS); |
Code: |
ULONG Direct3D9Wrapper::AddRef() { return Direct3D9->AddRef(); } |
Code: |
HRESULT Direct3D9Wrapper::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType,HWND hFocusWindow, DWORD BehaviorFlags,D3DPRESENT_PARAMETERS *pPresentationParameters,IDirect3DDevice9 **ppReturnedDeviceInterface) { IDirect3DDevice9* pDirect3DDevice9; HRESULT hRes = Direct3D9->CreateDevice( Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, &pDirect3DDevice9 ); *ppReturnedDeviceInterface = new Direct3DDevice9Wrapper( pDirect3DDevice9, this, pPresentationParameters ); return hRes; } |
- Just like the other .h file for the wrapper, you need to find the functions that are used by the original Direct3DDevice9 interface. To do that, go back to your notepad that has d3d9.h open and scroll down further from the last spot we copied code from. Not much further down from where we just were in that file, you should find:
Code: |
DECLARE_INTERFACE_(IDirect3DDevice9, IUnknown) |
Code: |
/*** IUnknown methods ***/ STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; STDMETHOD_(ULONG,AddRef)(THIS) PURE; STDMETHOD_(ULONG,Release)(THIS) PURE; |
Code: |
STDMETHOD(DrawTriPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) PURE; STDMETHOD(DeletePatch)(THIS_ UINT Handle) PURE; STDMETHOD(CreateQuery)(THIS_ D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery) PURE; |
Code: |
#pragma once #include "stdafx.h" |
Code: |
class Direct3DDevice9Wrapper : public IDirect3DDevice9 { public: |
Code: |
Direct3DDevice9Wrapper(IDirect3DDevice9* pDirect3DDevice9, IDirect3D9* pDirect3D9, D3DPRESENT_PARAMETERS *pPresentationParameters); virtual ~Direct3DDevice9Wrapper(); |
Code: |
IDirect3DDevice9* Direct3DDevice9; IDirect3D9* Direct3D9; UINT m_Stride; }; |
- Ah, the mother-load file. This file contains the most code as I said, but again as I said, it's just pass through functions at first until you add things yourself. So lets get started..
First, we need our includes for the file:
Code: |
#include "ID3D9Wrapper_Device.h" |
Code: |
Direct3DDevice9Wrapper::Direct3DDevice9Wrapper(IDirect3DDevice9 *pDirect3DDevice9, IDirect3D9 *pDirect3D9, D3DPRESENT_PARAMETERS *pPresentationParameters) { Direct3DDevice9 = pDirect3DDevice9; Direct3D9 = pDirect3D9; } Direct3DDevice9Wrapper::~Direct3DDevice9Wrapper(){} |
Code: |
HRESULT Direct3DDevice9Wrapper::QueryInterface(const IID &riid, void **ppvObj) { return Direct3DDevice9->QueryInterface( riid, ppvObj ); } ULONG Direct3DDevice9Wrapper::AddRef() { return Direct3DDevice9->AddRef(); } ULONG Direct3DDevice9Wrapper::Release() { return Direct3DDevice9->Release(); } |
Code: |
HRESULT Direct3DDevice9Wrapper::SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9 *pStreamData, UINT OffsetInBytes, UINT Stride) { if( StreamNumber == 0 ) m_Stride = Stride; return Direct3DDevice9->SetStreamSource(StreamNumber, pStreamData, OffsetInBytes, Stride); } |
Code: |
HRESULT Direct3DDevice9Wrapper::GetDirect3D(IDirect3D9 **ppD3D9) { *ppD3D9 = Direct3D9; return D3D_OK; } |
Code: |
HRESULT Direct3DDevice9Wrapper::QueryInterface(const IID &riid, void **ppvObj) { HRESULT hRes = Direct3DDevice9->QueryInterface(riid, ppvObj); if( hRes == S_OK ) *ppvObj = this; else *ppvObj = NULL; return hRes; } |
Code: |
#include "ID3D9Wrapper_Device.h" |
Code: |
#include "ID3D9Wrapper.h" |
Code: |
LIBRARY "dxhook" EXPORTS Direct3DCreate9 = Mine_Direct3DCreate9 |
Part 6. The Injector
- Ok first and foremost, let start with, this is a VERY BASIC injector. There are PLENTY of ways to rewrite this and such, and I HIGHLY suggest that you do. There are very nice libs you should use such as ForceLib, and a CRC32 rewriter to keep your hook undetected and such. This is just a small example to show you how to inject using Detours.
Detours comes with a function that wraps CreateProcessEx and injects the module you give it, for you. You can do this yourself as well by using CreateProcess/Ex then pausing the process, creating a new thread, and injecting your code into the thread, then calling the dllmain yourself and such. Again, theres many ways to do it, so you can choose which you want to use.
For this I will be using a simple console app. Nothing major, 1 file, not much code.
To start, lets make a new project so open another instance of VS2005 and goto: File -> New -> Project, select Win32 Project from the box, name it dxlauncher or what ever you want. Again keep "Create directory for solution" checked as it keeps things nice and tidy. Then click ok.
When the Application Wizard shows up, click on the Application Settings link on the left, then choose the following settings:
Application Type: Console Application
Additional options: Empty Project
Then click finish to open the new blank project. Next, right click in the solution explorer, and goto: Add -> New Item, Choose C++ File (.cpp) from the box, enter the name, 'main.cpp' and click add.
Now the new folder for this project should have been made. You will need to copy and paste the Detours folder into this projects directory as well cause we need them for this project too. Instead of really explaining things for this I'm just gunna post the code:
Code: |
#include #include #include #include #include #pragma comment(lib,"Detours/detours.lib") #include "Detours/detours.h" int main() { CString strHookPath; CString strINIPath; //////////////////////////////////// // Get The Current Paths //////////////////////////////////// _wfullpath(strHookPath.GetBuffer(MAX_PATH), L"dxhook.dll", MAX_PATH); strHookPath.ReleaseBuffer(); _wfullpath(strINIPath.GetBuffer(MAX_PATH), L"settings.ini", MAX_PATH); strINIPath.ReleaseBuffer(); //////////////////////////////////// // Check For Files //////////////////////////////////// if( GetFileAttributes( strHookPath ) == 0xFFFFFFFF ) { // Hook Was Not Found return 0; } if( GetFileAttributes( strINIPath ) == 0xFFFFFFFF ) { // INI File Was Not Found return 0; } //////////////////////////////////// // Read INI For Game Path //////////////////////////////////// CString strGamePath; GetPrivateProfileString( _T("settings"), _T("Path"), _T(""), strGamePath.GetBuffer(MAX_PATH), MAX_PATH, strINIPath ); strGamePath.ReleaseBuffer(); //////////////////////////////////// // Create Game Path Variables //////////////////////////////////// CString strGameExe = strGamePath + "\\GAME_EXE_NAME.exe"; //////////////////////////////////// // Launch The Process //////////////////////////////////// STARTUPINFO si = {sizeof(STARTUPINFO)}; PROCESS_INFORMATION pi = {0}; BOOL bResult = DetourCreateProcessWithDll( strGameExe, NULL, 0, 0, TRUE, CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, NULL, strGamePath, &si, &pi, strHookPath, 0 ); if( !bResult ) { // Process Failed To Launch return 0; } //////////////////////////////////// // Done! //////////////////////////////////// return 0; } |
Now, compile both the launcher and the DLL and put them in the same folder. If you are using my launcher, create a new text file and rename it to 'settings.ini' and put inside of it:
Code: |
[settings] Path=C:/Path/To/Your/Game |
Then launch the game and play.
Compiling
- With Visual Studio 2005, you might run into issues with others using your programs after they are compiled. This is due to the runtime library thats set to default when compiled. This is, however, a simple adjustment. Open your project up in VS2005, and open the project properties by going to:
Project ->
Notes
- Detours 2.1 has removed functions that are used for this hook so please use the link provided instead of the real Detours download.
Original post found here:
http://www.extalia.com/forums/viewtopic.php?f=45&t=2578
Results of using this tutorial can be such as:
http://www.extalia.com/forums/viewtopic.php?f=45&t=2597
|
0 comentarios:
Publicar un comentario