Skip to content

Commit 0a02722

Browse files
committed
Add low-level hooking for plugin loading
This seems like the best equivalent, `shcore.dll` gets loaded on the splash, while d3d11 gets loaded *way* before that
1 parent 4be58c6 commit 0a02722

File tree

9 files changed

+294
-81
lines changed

9 files changed

+294
-81
lines changed

BL3DX11Injection/BL3DX11Injection.vcxproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,10 @@
162162
</ItemDefinitionGroup>
163163
<ItemGroup>
164164
<ClInclude Include="dllmain.h" />
165+
<ClInclude Include="HookLib.h" />
165166
<ClInclude Include="INIReader.h" />
166167
<ClInclude Include="pch.h" />
168+
<ClInclude Include="PluginLoadHook.h" />
167169
</ItemGroup>
168170
<ItemGroup>
169171
<ClCompile Include="dllmain.cpp" />
@@ -173,6 +175,11 @@
173175
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
174176
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
175177
</ClCompile>
178+
<ClCompile Include="PluginLoadHook.cpp" />
179+
</ItemGroup>
180+
<ItemGroup>
181+
<Library Include="HookLib.lib" />
182+
<Library Include="Zydis.lib" />
176183
</ItemGroup>
177184
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
178185
<ImportGroup Label="ExtensionTargets" />

BL3DX11Injection/BL3DX11Injection.vcxproj.filters

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
1414
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
1515
</Filter>
16+
<Filter Include="Header Files\Libraries">
17+
<UniqueIdentifier>{9ba04bba-fc2c-4b2e-9fbf-24e8701d4027}</UniqueIdentifier>
18+
</Filter>
1619
</ItemGroup>
1720
<ItemGroup>
1821
<ClInclude Include="pch.h">
@@ -24,6 +27,12 @@
2427
<ClInclude Include="dllmain.h">
2528
<Filter>Header Files</Filter>
2629
</ClInclude>
30+
<ClInclude Include="HookLib.h">
31+
<Filter>Header Files\Libraries</Filter>
32+
</ClInclude>
33+
<ClInclude Include="PluginLoadHook.h">
34+
<Filter>Header Files</Filter>
35+
</ClInclude>
2736
</ItemGroup>
2837
<ItemGroup>
2938
<ClCompile Include="dllmain.cpp">
@@ -32,5 +41,16 @@
3241
<ClCompile Include="pch.cpp">
3342
<Filter>Source Files</Filter>
3443
</ClCompile>
44+
<ClCompile Include="PluginLoadHook.cpp">
45+
<Filter>Source Files</Filter>
46+
</ClCompile>
47+
</ItemGroup>
48+
<ItemGroup>
49+
<Library Include="HookLib.lib">
50+
<Filter>Header Files\Libraries</Filter>
51+
</Library>
52+
<Library Include="Zydis.lib">
53+
<Filter>Header Files\Libraries</Filter>
54+
</Library>
3555
</ItemGroup>
3656
</Project>

BL3DX11Injection/HookLib.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#pragma once
2+
3+
#ifdef __cplusplus
4+
#define HOOKLIB_EXPORT extern "C"
5+
#else
6+
#define HOOKLIB_EXPORT
7+
#endif
8+
9+
#ifndef _KERNEL_MODE
10+
HOOKLIB_EXPORT HMODULE _GetModuleHandle(LPCWSTR ModuleName);
11+
HOOKLIB_EXPORT PVOID _GetProcAddress(HMODULE hModule, LPCSTR FunctionName);
12+
#define QueryProcAddress(LibName, FuncName) _GetProcAddress(_GetModuleHandle(LibName), FuncName)
13+
#endif
14+
15+
HOOKLIB_EXPORT BOOLEAN NTAPI SetHook(void* Target, const void* Interceptor, void** Original);
16+
HOOKLIB_EXPORT BOOLEAN NTAPI RemoveHook(void* Original);
17+
18+
#ifdef __cplusplus
19+
#define Hook(RetType, Convention, FuncName, FuncAddress, InitialStatus, ...) \
20+
typedef RetType (Convention *FuncName##Type)(__VA_ARGS__);\
21+
static RetType Convention FuncName##Handler(__VA_ARGS__); \
22+
static HookStorage<FuncName##Type> FuncName##Hook(FuncAddress, &FuncName##Handler, InitialStatus); \
23+
static RetType Convention FuncName##Handler(__VA_ARGS__)
24+
25+
#define HookKnown(RetType, Convention, Func, ...) \
26+
Hook(RetType, Convention, Func, &Func, TRUE, __VA_ARGS__)
27+
28+
#define HookImport(RetType, Convention, Lib, Func, ...) \
29+
Hook(RetType, Convention, Func, (RetType(Convention*)(__VA_ARGS__))QueryProcAddress(L##Lib, #Func), TRUE, __VA_ARGS__)
30+
31+
#define DeclareHookKnown(RetType, Convention, Func, ...) \
32+
Hook(RetType, Convention, Func, &Func, FALSE, __VA_ARGS__)
33+
34+
#define DeclareHookImport(RetType, Convention, Lib, Func, ...) \
35+
Hook(RetType, Convention, Func, (RetType(Convention*)(__VA_ARGS__))QueryProcAddress(L##Lib, #Func), FALSE, __VA_ARGS__)
36+
37+
#define DeclareHook(RetType, Convention, Func, ...) \
38+
Hook(RetType, Convention, Func, (RetType(Convention*)(__VA_ARGS__))NULL, FALSE, __VA_ARGS__)
39+
40+
#define CallOriginal(Func) (Func##Hook.Original)
41+
42+
#define HookObject(Func) (Func##Hook)
43+
#define EnableHook(Func) HookObject(Func).Enable()
44+
#define DisableHook(Func) HookObject(Func).Disable()
45+
#define IsHookEnabled(Func) HookObject(Func).GetState()
46+
#define SetHookTarget(Func, Target) HookObject(Func).ReinitTarget((Func##Type)Target)
47+
#define ApplyHook(Func, Target) \
48+
SetHookTarget(Func, Target); \
49+
EnableHook(Func)
50+
51+
template<typename T>
52+
class HookStorage {
53+
private:
54+
T m_Target;
55+
T m_Interceptor;
56+
T m_Original;
57+
BOOLEAN m_State;
58+
public:
59+
inline T GetOriginal() const {
60+
return m_Original;
61+
}
62+
__declspec(property(get = GetOriginal)) T Original;
63+
64+
HookStorage() = delete;
65+
HookStorage(const HookStorage&) = delete;
66+
HookStorage(HookStorage&&) = delete;
67+
HookStorage& operator = (const HookStorage&) = delete;
68+
HookStorage& operator = (HookStorage&&) = delete;
69+
70+
HookStorage(T Target, T Interceptor, BOOLEAN InitialState)
71+
: m_Target(Target), m_Interceptor(Interceptor), m_Original(NULL), m_State(FALSE)
72+
{
73+
if (Target && InitialState) Enable();
74+
}
75+
76+
~HookStorage() {
77+
Disable();
78+
}
79+
80+
BOOLEAN ReinitTarget(T Target) {
81+
if (!Target) return FALSE;
82+
if (m_State) return FALSE;
83+
m_Target = Target;
84+
return TRUE;
85+
}
86+
87+
BOOLEAN Enable() {
88+
if (!m_Target) return FALSE;
89+
if (m_State) return TRUE;
90+
return m_State = SetHook(m_Target, m_Interceptor, reinterpret_cast<LPVOID*>(&m_Original));
91+
}
92+
93+
BOOLEAN Disable() {
94+
if (!m_State) return TRUE;
95+
return m_State = !RemoveHook(m_Original);
96+
}
97+
98+
inline BOOLEAN GetState() const {
99+
return m_State;
100+
}
101+
};
102+
#endif

BL3DX11Injection/HookLib.lib

55.3 KB
Binary file not shown.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#include <set>
2+
#include <fstream>
3+
#include <tchar.h>
4+
#include <list>
5+
#include <vector>
6+
7+
#include "pch.h"
8+
#include "INIReader.h" // https://github.com/jtilly/inih
9+
10+
#include "HookLib.h"
11+
#include "PluginLoadHook.h"
12+
#pragma comment(lib, "Zydis.lib")
13+
#pragma comment(lib, "HookLib.lib")
14+
15+
std::wstring pluginsPath;
16+
static std::wstring iniPath;
17+
static std::vector<HMODULE> loadedModules;
18+
19+
#pragma region Hooks
20+
21+
#pragma region Plugin Freeing
22+
using _ExitProcess = VOID(WINAPI*)(ULONG ExitCode);
23+
_ExitProcess OriginalExitProcess = NULL;
24+
VOID WINAPI ExitProcessHook(ULONG ExitCode)
25+
{
26+
std::wcout << "Exiting Process...";
27+
28+
Sleep(1000);
29+
30+
// Now free all of our other libs that we loaded
31+
for (auto&& hMod : loadedModules) {
32+
FreeLibrary(hMod);
33+
}
34+
35+
RemoveHook(OriginalExitProcess);
36+
ExitProcess(ExitCode);
37+
}
38+
39+
#pragma endregion
40+
41+
#pragma region Plugin Loading
42+
void LoadPlugins() {
43+
std::wcout << "Loading Plugins..." << std::endl;
44+
45+
std::list<std::wstring> pluginsToLoad = {};
46+
47+
std::fstream file;
48+
file.open(iniPath.c_str(), std::ios::out | std::ios::in | std::ios::app);
49+
if (!file) {
50+
file.open(iniPath.c_str(), std::ios::in || std::ios::out || std::ios::trunc);
51+
file << "[PluginLoader]\n";
52+
file.flush();
53+
file.close();
54+
}
55+
56+
INIReader reader(iniPath.c_str());
57+
if (reader.ParseError() != 0) {
58+
std::wcout << "Unable to load 'pluginLoader.ini'" << std::endl;
59+
}
60+
61+
WIN32_FIND_DATA fd; // This'll store our data about the plugin we're currently loading in.
62+
const HANDLE dllFile = FindFirstFile((pluginsPath + L"*.dll").c_str(), &fd); // Get the first DLL file in our plugins dir
63+
int dllCount = 0;
64+
65+
if (dllFile == INVALID_HANDLE_VALUE) {
66+
std::wcout << "No Plugins Found..." << std::endl;
67+
return; // Just return now, no need to bother to execute the rest of the code
68+
}
69+
70+
do {
71+
if ((!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))) {
72+
std::wstring pluginName = (std::wstring)fd.cFileName;
73+
std::string s(pluginName.begin(), pluginName.end());
74+
75+
if (reader.Sections().count(s)) {
76+
float delayTime = reader.GetFloat(s, "delaySeconds", 0);
77+
std::wcout << "Waiting " << delayTime << " seconds to load " << WidenString(s) << std::endl;
78+
Sleep(delayTime * 1000);
79+
}
80+
81+
82+
std::wstring filePath = pluginsPath + (pluginName); // Generate our file path + the name of our plugin to load
83+
HMODULE hMod = LoadLibrary(filePath.c_str());
84+
if (hMod) {
85+
loadedModules.push_back(hMod);
86+
dllCount++;
87+
}
88+
else
89+
std::wcout << "Unable to load plugin: " << filePath << ": " << GetLastErrorAsString() << std::endl;
90+
}
91+
92+
} while (FindNextFile(dllFile, &fd));
93+
94+
FindClose(dllFile);
95+
}
96+
97+
using _LoadLibrary = HMODULE(WINAPI*)(LPCWSTR lpLibFileName);
98+
_LoadLibrary OriginalLoadLibrary = NULL;
99+
HMODULE WINAPI LoadLibraryWHook(LPCWSTR lpLibFileName) {
100+
std::wcout << "Loading Library: " << std::wstring(lpLibFileName) << std::endl;
101+
102+
if (std::wstring(lpLibFileName)._Equal(L"shcore.dll")) {
103+
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)LoadPlugins, NULL, NULL, NULL);
104+
RemoveHook(OriginalLoadLibrary);
105+
return LoadLibrary(lpLibFileName);
106+
}
107+
108+
return OriginalLoadLibrary(lpLibFileName);
109+
}
110+
111+
#pragma endregion
112+
113+
#pragma endregion
114+
115+
void InitializePluginHooks(HMODULE gameModule) {
116+
WCHAR pluginsFilePath[513] = { 0 };
117+
GetModuleFileNameW(gameModule, pluginsFilePath, 512);
118+
std::wstring pPath = pluginsFilePath;
119+
pPath = pPath.substr(0, pPath.rfind('\\')) + L"\\Plugins\\";
120+
std::wcout << "Plugins Path: " << pPath << std::endl;
121+
122+
// This'll hapen if we are magically unable to create the plugins dir.
123+
if (!CreateDirectory(pPath.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
124+
std::wcout << "Unable to create plugins folder..." << std::endl;
125+
return;
126+
}
127+
pluginsPath = pPath;
128+
iniPath = pluginsPath + L"pluginLoader.ini";
129+
130+
PVOID Target = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "ExitProcess");
131+
132+
if (SetHook(Target, ExitProcessHook, reinterpret_cast<PVOID*>(&OriginalExitProcess)))
133+
std::wcout << "Initialized ExitProcess(...) hook" << std::endl;
134+
else
135+
std::wcout << "Unable to initialize ExitProcess(...) hook" << std::endl;
136+
137+
PVOID loadLibraryHook = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
138+
if (SetHook(loadLibraryHook, LoadLibraryWHook, reinterpret_cast<PVOID*>(&OriginalLoadLibrary)))
139+
std::wcout << "Initialized LoadLibraryW(...) hook..." << std::endl;
140+
else
141+
std::wcout << "Unable to initialize LoadLibraryW(...) hook" << std::endl;
142+
143+
}

BL3DX11Injection/PluginLoadHook.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
#include <set>
3+
#include <fstream>
4+
#include <tchar.h>
5+
#include <list>
6+
#include <vector>
7+
8+
#include "pch.h"
9+
#include "INIReader.h" // https://github.com/jtilly/inih
10+
11+
#include "HookLib.h"
12+
13+
VOID WINAPI ExitProcessHook(ULONG ExitCode);
14+
15+
void InitializePluginHooks(HMODULE gameModule);
16+
17+
void LoadPlugins();

BL3DX11Injection/Zydis.lib

1.23 MB
Binary file not shown.

0 commit comments

Comments
 (0)