Skip to content

Commit b9b7e79

Browse files
committed
Minimal Example on how to hook Games using a custom DInput8 DLL.
1 parent 2abd647 commit b9b7e79

File tree

10 files changed

+358
-17
lines changed

10 files changed

+358
-17
lines changed

MinimalDInput8Hook/CustomHooks.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include "stdafx.h"
2+
#include "CustomHooks.h"
3+
4+
typedef HANDLE(*CreateFileA_t)(
5+
LPCSTR lpFileName,
6+
DWORD dwDesiredAccess,
7+
DWORD dwShareMode,
8+
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
9+
DWORD dwCreationDisposition,
10+
DWORD dwFlagsAndAttributes,
11+
HANDLE hTemplateFile);
12+
13+
typedef HANDLE(*CreateFileW_t)(
14+
LPCWSTR lpFileName,
15+
DWORD dwDesiredAccess,
16+
DWORD dwShareMode,
17+
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
18+
DWORD dwCreationDisposition,
19+
DWORD dwFlagsAndAttributes,
20+
HANDLE hTemplateFile);
21+
22+
CreateFileA_t OriginalCreateFileA;
23+
CreateFileW_t OriginalCreateFileW;
24+
25+
HANDLE CreateFileA_Wrapper(
26+
LPCSTR lpFileName,
27+
DWORD dwDesiredAccess,
28+
DWORD dwShareMode,
29+
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
30+
DWORD dwCreationDisposition,
31+
DWORD dwFlagsAndAttributes,
32+
HANDLE hTemplateFile
33+
)
34+
{
35+
// Do our custom stuff and parameter rewriting
36+
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), lpFileName, (DWORD)strlen(lpFileName), nullptr, nullptr);
37+
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), "\n", 1, nullptr, nullptr);
38+
39+
// Call the original CreateFileA function
40+
return OriginalCreateFileA(
41+
lpFileName,
42+
dwDesiredAccess,
43+
dwShareMode,
44+
lpSecurityAttributes,
45+
dwCreationDisposition,
46+
dwFlagsAndAttributes,
47+
hTemplateFile);
48+
}
49+
50+
HANDLE CreateFileW_Wrapper(
51+
LPCWSTR lpFileName,
52+
DWORD dwDesiredAccess,
53+
DWORD dwShareMode,
54+
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
55+
DWORD dwCreationDisposition,
56+
DWORD dwFlagsAndAttributes,
57+
HANDLE hTemplateFile
58+
)
59+
{
60+
// Do our custom stuff and parameter rewriting
61+
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), lpFileName, (DWORD)wcslen(lpFileName), nullptr, nullptr);
62+
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), L"\n", 1, nullptr, nullptr);
63+
64+
// Call the original CreateFileW function
65+
return OriginalCreateFileW(
66+
lpFileName,
67+
dwDesiredAccess,
68+
dwShareMode,
69+
lpSecurityAttributes,
70+
dwCreationDisposition,
71+
dwFlagsAndAttributes,
72+
hTemplateFile);
73+
}
74+
75+
void SetupHooks()
76+
{
77+
// Create a console for Debug output
78+
AllocConsole();
79+
80+
// Setup hooks here, see examples below
81+
82+
OriginalCreateFileA = HookFunction("KERNEL32.dll", "CreateFileA", &CreateFileA_Wrapper);
83+
OriginalCreateFileW = HookFunction("KERNEL32.dll", "CreateFileW", &CreateFileW_Wrapper);
84+
}
85+

MinimalDInput8Hook/CustomHooks.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#pragma once
2+
3+
#include "Hook.h"
4+
5+
void SetupHooks();

MinimalDInput8Hook/DInput8.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
#include <unknwn.h>
3+
4+
5+
typedef HRESULT(*DirectInput8Create_t)(
6+
HINSTANCE hinst,
7+
DWORD dwVersion,
8+
REFIID riidltf,
9+
LPVOID * ppvOut,
10+
LPUNKNOWN punkOuter
11+
);
12+
13+
extern DirectInput8Create_t OriginalFunction;
14+
extern HMODULE DInput8DLL;
15+
16+
extern "C"
17+
{
18+
DINPUT8_API HRESULT DirectInput8Create(
19+
HINSTANCE hinst,
20+
DWORD dwVersion,
21+
REFIID riidltf,
22+
LPVOID * ppvOut,
23+
LPUNKNOWN punkOuter
24+
);
25+
}

MinimalDInput8Hook/Hook.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "stdafx.h"
2+
#include "Hook.h"
3+
4+
PROCESS_BASIC_INFORMATION BasicProcessInfo;
5+
6+
void InitializeHooking()
7+
{
8+
// Get our Process Handle
9+
const HANDLE Process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, GetCurrentProcessId());
10+
11+
// Find out our base address, where the Executable image is loaded in memory
12+
NtQueryInformationProcess(Process, ProcessBasicInformation, &BasicProcessInfo, sizeof(BasicProcessInfo), nullptr);
13+
}
14+
15+
void* HookFunction_Internal(const char* DLLName, const char* FunctionName, void* NewAddress)
16+
{
17+
// In the PEB structure provided by Microsoft the ImageBaseAddress is part of a "Reserved" array,
18+
// for better clarity we use a custom struct here
19+
CUSTOM_PEB* PEBExtendedInfo = (CUSTOM_PEB*)BasicProcessInfo.PebBaseAddress;
20+
PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)PEBExtendedInfo->ImageBaseAddress;
21+
// This is the start of the executable file in memory
22+
BYTE* BaseAddress = (BYTE*)PEBExtendedInfo->ImageBaseAddress;
23+
24+
// Get the Address of our NT Image headers
25+
PIMAGE_NT_HEADERS64 FileHeader = (PIMAGE_NT_HEADERS64)(BaseAddress + pDOSHeader->e_lfanew);
26+
27+
// Retrieve the import directory in which all imported dlls and functions are listed
28+
IMAGE_DATA_DIRECTORY ImportDirectory = FileHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
29+
const int NumImportDesciptors = ImportDirectory.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
30+
PIMAGE_IMPORT_DESCRIPTOR Descriptors = (PIMAGE_IMPORT_DESCRIPTOR)(BaseAddress + ImportDirectory.VirtualAddress);
31+
32+
// Go through all imported DLLs and find the one we are insteresed in
33+
for (int ImportDLLIndex = 0; ImportDLLIndex < NumImportDesciptors; ++ImportDLLIndex)
34+
{
35+
PIMAGE_IMPORT_DESCRIPTOR Descriptor = Descriptors + ImportDLLIndex;
36+
const char* ImportDLLName = (const char*)BaseAddress + Descriptor->Name;
37+
38+
// Check if we found our DLL in the import table
39+
if (!strcmp(ImportDLLName, DLLName))
40+
{
41+
42+
PIMAGE_THUNK_DATA64 ImportNameTable = (PIMAGE_THUNK_DATA64)(BaseAddress + Descriptor->OriginalFirstThunk);
43+
int Offset = 0;
44+
// The import name table is a null terminated array, so iterate until we either found it or reach the null termination
45+
while (ImportNameTable->u1.AddressOfData != 0)
46+
{
47+
PIMAGE_IMPORT_BY_NAME NameImport = (PIMAGE_IMPORT_BY_NAME)(BaseAddress + ImportNameTable->u1.AddressOfData);
48+
// Null terminated function name start pointer is stored in here
49+
const char* ImportFunctionName = NameImport->Name;
50+
51+
if (!strcmp(FunctionName, ImportFunctionName))
52+
{
53+
PIMAGE_THUNK_DATA64 ImportAddressTable = (PIMAGE_THUNK_DATA64)(BaseAddress + Descriptor->FirstThunk);
54+
// The import address table is in the same order as the import name table
55+
ImportAddressTable += Offset;
56+
57+
void* OriginalAddress = (void*)ImportAddressTable->u1.AddressOfData;
58+
DWORD OldProtection;
59+
// Make the page writable to patch the pointer
60+
VirtualProtect(ImportAddressTable, sizeof(IMAGE_THUNK_DATA64), PAGE_READWRITE, &OldProtection);
61+
ImportAddressTable->u1.AddressOfData = (ULONGLONG)NewAddress;
62+
// Restore page protection to the previous state
63+
VirtualProtect(ImportAddressTable, sizeof(IMAGE_THUNK_DATA64), OldProtection, &OldProtection);
64+
return OriginalAddress;
65+
}
66+
67+
68+
++ImportNameTable;
69+
++Offset;
70+
}
71+
72+
// We've found the DLL but not the function, break here. No need to go through the rest of the DLLs
73+
break;
74+
}
75+
}
76+
77+
// DLL and function import not found return nullptr to signal this
78+
return nullptr;
79+
}
80+

MinimalDInput8Hook/Hook.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#pragma once
2+
3+
#include "stdafx.h"
4+
5+
typedef struct _PEB_FREE_BLOCK {
6+
_PEB_FREE_BLOCK *Next;
7+
ULONG Size;
8+
} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
9+
10+
typedef void(*PPEBLOCKROUTINE)(
11+
PVOID PebLock
12+
);
13+
14+
struct CUSTOM_PEB
15+
{
16+
BOOLEAN InheritedAddressSpace;
17+
BOOLEAN ReadImageFileExecOptions;
18+
BOOLEAN BeingDebugged;
19+
BOOLEAN Spare;
20+
HANDLE Mutant;
21+
PVOID ImageBaseAddress;
22+
PPEB_LDR_DATA LoaderData;
23+
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
24+
PVOID SubSystemData;
25+
PVOID ProcessHeap;
26+
PVOID FastPebLock;
27+
PPEBLOCKROUTINE FastPebLockRoutine;
28+
PPEBLOCKROUTINE FastPebUnlockRoutine;
29+
ULONG EnvironmentUpdateCount;
30+
PVOID* KernelCallbackTable;
31+
PVOID EventLogSection;
32+
PVOID EventLog;
33+
PPEB_FREE_BLOCK FreeList;
34+
ULONG TlsExpansionCounter;
35+
PVOID TlsBitmap;
36+
ULONG TlsBitmapBits[0x2];
37+
PVOID ReadOnlySharedMemoryBase;
38+
PVOID ReadOnlySharedMemoryHeap;
39+
PVOID* ReadOnlyStaticServerData;
40+
PVOID AnsiCodePageData;
41+
PVOID OemCodePageData;
42+
PVOID UnicodeCaseTableData;
43+
ULONG NumberOfProcessors;
44+
ULONG NtGlobalFlag;
45+
BYTE Spare2[0x4];
46+
LARGE_INTEGER CriticalSectionTimeout;
47+
ULONG HeapSegmentReserve;
48+
ULONG HeapSegmentCommit;
49+
ULONG HeapDeCommitTotalFreeThreshold;
50+
ULONG HeapDeCommitFreeBlockThreshold;
51+
ULONG NumberOfHeaps;
52+
ULONG MaximumNumberOfHeaps;
53+
PVOID* *ProcessHeaps;
54+
PVOID GdiSharedHandleTable;
55+
PVOID ProcessStarterHelper;
56+
PVOID GdiDCAttributeList;
57+
PVOID LoaderLock;
58+
ULONG OSMajorVersion;
59+
ULONG OSMinorVersion;
60+
ULONG OSBuildNumber;
61+
ULONG OSPlatformId;
62+
ULONG ImageSubSystem;
63+
ULONG ImageSubSystemMajorVersion;
64+
ULONG ImageSubSystemMinorVersion;
65+
ULONG GdiHandleBuffer[0x22];
66+
ULONG PostProcessInitRoutine;
67+
ULONG TlsExpansionBitmap;
68+
BYTE TlsExpansionBitmapBits[0x80];
69+
ULONG SessionId;
70+
};
71+
72+
extern PROCESS_BASIC_INFORMATION BasicProcessInfo;
73+
74+
void InitializeHooking();
75+
76+
void* HookFunction_Internal(const char* DLLName, const char* FunctionName, void* NewAddress);
77+
78+
template<typename T>
79+
T HookFunction(const char* DLLName, const char* FunctionName, T NewAddress)
80+
{
81+
return (T)HookFunction_Internal(DLLName, FunctionName, NewAddress);
82+
}

MinimalDInput8Hook/MinimalDInput8Hook.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,18 @@
22
//
33

44
#include "stdafx.h"
5+
#include "DInput8.h"
6+
#include "CustomHooks.h"
7+
DirectInput8Create_t OriginalFunction = nullptr;
8+
HMODULE DInput8DLL = nullptr;
59

610

11+
12+
DINPUT8_API HRESULT DirectInput8Create(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID * ppvOut, LPUNKNOWN punkOuter)
13+
{
14+
if (OriginalFunction)
15+
{
16+
return OriginalFunction(hinst, dwVersion, riidltf, ppvOut, punkOuter);
17+
}
18+
return S_FALSE;
19+
}

MinimalDInput8Hook/MinimalDInput8Hook.vcxproj

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@
7575
</PropertyGroup>
7676
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
7777
<LinkIncremental>true</LinkIncremental>
78+
<TargetName>DINPUT8</TargetName>
7879
</PropertyGroup>
7980
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
8081
<LinkIncremental>false</LinkIncremental>
8182
</PropertyGroup>
8283
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
8384
<LinkIncremental>false</LinkIncremental>
85+
<TargetName>DINPUT8</TargetName>
8486
</PropertyGroup>
8587
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
8688
<ClCompile>
@@ -102,12 +104,13 @@
102104
<WarningLevel>Level3</WarningLevel>
103105
<Optimization>Disabled</Optimization>
104106
<SDLCheck>true</SDLCheck>
105-
<PreprocessorDefinitions>_DEBUG;MINIMALDINPUT8HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
107+
<PreprocessorDefinitions>_DEBUG;MINIMALDINPUT8HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);DLL_EXPORT</PreprocessorDefinitions>
106108
<ConformanceMode>true</ConformanceMode>
107109
</ClCompile>
108110
<Link>
109111
<SubSystem>Windows</SubSystem>
110112
<GenerateDebugInformation>true</GenerateDebugInformation>
113+
<AdditionalDependencies>ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
111114
</Link>
112115
</ItemDefinitionGroup>
113116
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -136,22 +139,28 @@
136139
<FunctionLevelLinking>true</FunctionLevelLinking>
137140
<IntrinsicFunctions>true</IntrinsicFunctions>
138141
<SDLCheck>true</SDLCheck>
139-
<PreprocessorDefinitions>NDEBUG;MINIMALDINPUT8HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
142+
<PreprocessorDefinitions>NDEBUG;MINIMALDINPUT8HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);DLL_EXPORT</PreprocessorDefinitions>
140143
<ConformanceMode>true</ConformanceMode>
141144
</ClCompile>
142145
<Link>
143146
<SubSystem>Windows</SubSystem>
144147
<EnableCOMDATFolding>true</EnableCOMDATFolding>
145148
<OptimizeReferences>true</OptimizeReferences>
146149
<GenerateDebugInformation>true</GenerateDebugInformation>
150+
<AdditionalDependencies>ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
147151
</Link>
148152
</ItemDefinitionGroup>
149153
<ItemGroup>
154+
<ClInclude Include="CustomHooks.h" />
155+
<ClInclude Include="DInput8.h" />
156+
<ClInclude Include="Hook.h" />
150157
<ClInclude Include="stdafx.h" />
151158
<ClInclude Include="targetver.h" />
152159
</ItemGroup>
153160
<ItemGroup>
154-
<ClCompile Include="dllmain.cpp" />
161+
<ClCompile Include="CustomHooks.cpp" />
162+
<ClCompile Include="DLLMain.cpp" />
163+
<ClCompile Include="Hook.cpp" />
155164
<ClCompile Include="MinimalDInput8Hook.cpp" />
156165
<ClCompile Include="stdafx.cpp">
157166
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>

MinimalDInput8Hook/MinimalDInput8Hook.vcxproj.filters

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
<ClInclude Include="targetver.h">
2222
<Filter>Header Files</Filter>
2323
</ClInclude>
24+
<ClInclude Include="DInput8.h">
25+
<Filter>Header Files</Filter>
26+
</ClInclude>
27+
<ClInclude Include="Hook.h">
28+
<Filter>Header Files</Filter>
29+
</ClInclude>
30+
<ClInclude Include="CustomHooks.h">
31+
<Filter>Header Files</Filter>
32+
</ClInclude>
2433
</ItemGroup>
2534
<ItemGroup>
2635
<ClCompile Include="stdafx.cpp">
@@ -29,7 +38,13 @@
2938
<ClCompile Include="MinimalDInput8Hook.cpp">
3039
<Filter>Source Files</Filter>
3140
</ClCompile>
32-
<ClCompile Include="dllmain.cpp">
41+
<ClCompile Include="CustomHooks.cpp">
42+
<Filter>Source Files</Filter>
43+
</ClCompile>
44+
<ClCompile Include="Hook.cpp">
45+
<Filter>Source Files</Filter>
46+
</ClCompile>
47+
<ClCompile Include="DLLMain.cpp">
3348
<Filter>Source Files</Filter>
3449
</ClCompile>
3550
</ItemGroup>

0 commit comments

Comments
 (0)