-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
4be58c6
commit 0a02722
Showing
9 changed files
with
294 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
#pragma once | ||
|
||
#ifdef __cplusplus | ||
#define HOOKLIB_EXPORT extern "C" | ||
#else | ||
#define HOOKLIB_EXPORT | ||
#endif | ||
|
||
#ifndef _KERNEL_MODE | ||
HOOKLIB_EXPORT HMODULE _GetModuleHandle(LPCWSTR ModuleName); | ||
HOOKLIB_EXPORT PVOID _GetProcAddress(HMODULE hModule, LPCSTR FunctionName); | ||
#define QueryProcAddress(LibName, FuncName) _GetProcAddress(_GetModuleHandle(LibName), FuncName) | ||
#endif | ||
|
||
HOOKLIB_EXPORT BOOLEAN NTAPI SetHook(void* Target, const void* Interceptor, void** Original); | ||
HOOKLIB_EXPORT BOOLEAN NTAPI RemoveHook(void* Original); | ||
|
||
#ifdef __cplusplus | ||
#define Hook(RetType, Convention, FuncName, FuncAddress, InitialStatus, ...) \ | ||
typedef RetType (Convention *FuncName##Type)(__VA_ARGS__);\ | ||
static RetType Convention FuncName##Handler(__VA_ARGS__); \ | ||
static HookStorage<FuncName##Type> FuncName##Hook(FuncAddress, &FuncName##Handler, InitialStatus); \ | ||
static RetType Convention FuncName##Handler(__VA_ARGS__) | ||
|
||
#define HookKnown(RetType, Convention, Func, ...) \ | ||
Hook(RetType, Convention, Func, &Func, TRUE, __VA_ARGS__) | ||
|
||
#define HookImport(RetType, Convention, Lib, Func, ...) \ | ||
Hook(RetType, Convention, Func, (RetType(Convention*)(__VA_ARGS__))QueryProcAddress(L##Lib, #Func), TRUE, __VA_ARGS__) | ||
|
||
#define DeclareHookKnown(RetType, Convention, Func, ...) \ | ||
Hook(RetType, Convention, Func, &Func, FALSE, __VA_ARGS__) | ||
|
||
#define DeclareHookImport(RetType, Convention, Lib, Func, ...) \ | ||
Hook(RetType, Convention, Func, (RetType(Convention*)(__VA_ARGS__))QueryProcAddress(L##Lib, #Func), FALSE, __VA_ARGS__) | ||
|
||
#define DeclareHook(RetType, Convention, Func, ...) \ | ||
Hook(RetType, Convention, Func, (RetType(Convention*)(__VA_ARGS__))NULL, FALSE, __VA_ARGS__) | ||
|
||
#define CallOriginal(Func) (Func##Hook.Original) | ||
|
||
#define HookObject(Func) (Func##Hook) | ||
#define EnableHook(Func) HookObject(Func).Enable() | ||
#define DisableHook(Func) HookObject(Func).Disable() | ||
#define IsHookEnabled(Func) HookObject(Func).GetState() | ||
#define SetHookTarget(Func, Target) HookObject(Func).ReinitTarget((Func##Type)Target) | ||
#define ApplyHook(Func, Target) \ | ||
SetHookTarget(Func, Target); \ | ||
EnableHook(Func) | ||
|
||
template<typename T> | ||
class HookStorage { | ||
private: | ||
T m_Target; | ||
T m_Interceptor; | ||
T m_Original; | ||
BOOLEAN m_State; | ||
public: | ||
inline T GetOriginal() const { | ||
return m_Original; | ||
} | ||
__declspec(property(get = GetOriginal)) T Original; | ||
|
||
HookStorage() = delete; | ||
HookStorage(const HookStorage&) = delete; | ||
HookStorage(HookStorage&&) = delete; | ||
HookStorage& operator = (const HookStorage&) = delete; | ||
HookStorage& operator = (HookStorage&&) = delete; | ||
|
||
HookStorage(T Target, T Interceptor, BOOLEAN InitialState) | ||
: m_Target(Target), m_Interceptor(Interceptor), m_Original(NULL), m_State(FALSE) | ||
{ | ||
if (Target && InitialState) Enable(); | ||
} | ||
|
||
~HookStorage() { | ||
Disable(); | ||
} | ||
|
||
BOOLEAN ReinitTarget(T Target) { | ||
if (!Target) return FALSE; | ||
if (m_State) return FALSE; | ||
m_Target = Target; | ||
return TRUE; | ||
} | ||
|
||
BOOLEAN Enable() { | ||
if (!m_Target) return FALSE; | ||
if (m_State) return TRUE; | ||
return m_State = SetHook(m_Target, m_Interceptor, reinterpret_cast<LPVOID*>(&m_Original)); | ||
} | ||
|
||
BOOLEAN Disable() { | ||
if (!m_State) return TRUE; | ||
return m_State = !RemoveHook(m_Original); | ||
} | ||
|
||
inline BOOLEAN GetState() const { | ||
return m_State; | ||
} | ||
}; | ||
#endif |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
#include <set> | ||
#include <fstream> | ||
#include <tchar.h> | ||
#include <list> | ||
#include <vector> | ||
|
||
#include "pch.h" | ||
#include "INIReader.h" // https://github.com/jtilly/inih | ||
|
||
#include "HookLib.h" | ||
#include "PluginLoadHook.h" | ||
#pragma comment(lib, "Zydis.lib") | ||
#pragma comment(lib, "HookLib.lib") | ||
|
||
std::wstring pluginsPath; | ||
static std::wstring iniPath; | ||
static std::vector<HMODULE> loadedModules; | ||
|
||
#pragma region Hooks | ||
|
||
#pragma region Plugin Freeing | ||
using _ExitProcess = VOID(WINAPI*)(ULONG ExitCode); | ||
_ExitProcess OriginalExitProcess = NULL; | ||
VOID WINAPI ExitProcessHook(ULONG ExitCode) | ||
{ | ||
std::wcout << "Exiting Process..."; | ||
|
||
Sleep(1000); | ||
|
||
// Now free all of our other libs that we loaded | ||
for (auto&& hMod : loadedModules) { | ||
FreeLibrary(hMod); | ||
} | ||
|
||
RemoveHook(OriginalExitProcess); | ||
ExitProcess(ExitCode); | ||
} | ||
|
||
#pragma endregion | ||
|
||
#pragma region Plugin Loading | ||
void LoadPlugins() { | ||
std::wcout << "Loading Plugins..." << std::endl; | ||
|
||
std::list<std::wstring> pluginsToLoad = {}; | ||
|
||
std::fstream file; | ||
file.open(iniPath.c_str(), std::ios::out | std::ios::in | std::ios::app); | ||
if (!file) { | ||
file.open(iniPath.c_str(), std::ios::in || std::ios::out || std::ios::trunc); | ||
file << "[PluginLoader]\n"; | ||
file.flush(); | ||
file.close(); | ||
} | ||
|
||
INIReader reader(iniPath.c_str()); | ||
if (reader.ParseError() != 0) { | ||
std::wcout << "Unable to load 'pluginLoader.ini'" << std::endl; | ||
} | ||
|
||
WIN32_FIND_DATA fd; // This'll store our data about the plugin we're currently loading in. | ||
const HANDLE dllFile = FindFirstFile((pluginsPath + L"*.dll").c_str(), &fd); // Get the first DLL file in our plugins dir | ||
int dllCount = 0; | ||
|
||
if (dllFile == INVALID_HANDLE_VALUE) { | ||
std::wcout << "No Plugins Found..." << std::endl; | ||
return; // Just return now, no need to bother to execute the rest of the code | ||
} | ||
|
||
do { | ||
if ((!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))) { | ||
std::wstring pluginName = (std::wstring)fd.cFileName; | ||
std::string s(pluginName.begin(), pluginName.end()); | ||
|
||
if (reader.Sections().count(s)) { | ||
float delayTime = reader.GetFloat(s, "delaySeconds", 0); | ||
std::wcout << "Waiting " << delayTime << " seconds to load " << WidenString(s) << std::endl; | ||
Sleep(delayTime * 1000); | ||
} | ||
|
||
|
||
std::wstring filePath = pluginsPath + (pluginName); // Generate our file path + the name of our plugin to load | ||
HMODULE hMod = LoadLibrary(filePath.c_str()); | ||
if (hMod) { | ||
loadedModules.push_back(hMod); | ||
dllCount++; | ||
} | ||
else | ||
std::wcout << "Unable to load plugin: " << filePath << ": " << GetLastErrorAsString() << std::endl; | ||
} | ||
|
||
} while (FindNextFile(dllFile, &fd)); | ||
|
||
FindClose(dllFile); | ||
} | ||
|
||
using _LoadLibrary = HMODULE(WINAPI*)(LPCWSTR lpLibFileName); | ||
_LoadLibrary OriginalLoadLibrary = NULL; | ||
HMODULE WINAPI LoadLibraryWHook(LPCWSTR lpLibFileName) { | ||
std::wcout << "Loading Library: " << std::wstring(lpLibFileName) << std::endl; | ||
|
||
if (std::wstring(lpLibFileName)._Equal(L"shcore.dll")) { | ||
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)LoadPlugins, NULL, NULL, NULL); | ||
RemoveHook(OriginalLoadLibrary); | ||
return LoadLibrary(lpLibFileName); | ||
} | ||
|
||
return OriginalLoadLibrary(lpLibFileName); | ||
} | ||
|
||
#pragma endregion | ||
|
||
#pragma endregion | ||
|
||
void InitializePluginHooks(HMODULE gameModule) { | ||
WCHAR pluginsFilePath[513] = { 0 }; | ||
GetModuleFileNameW(gameModule, pluginsFilePath, 512); | ||
std::wstring pPath = pluginsFilePath; | ||
pPath = pPath.substr(0, pPath.rfind('\\')) + L"\\Plugins\\"; | ||
std::wcout << "Plugins Path: " << pPath << std::endl; | ||
|
||
// This'll hapen if we are magically unable to create the plugins dir. | ||
if (!CreateDirectory(pPath.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) { | ||
std::wcout << "Unable to create plugins folder..." << std::endl; | ||
return; | ||
} | ||
pluginsPath = pPath; | ||
iniPath = pluginsPath + L"pluginLoader.ini"; | ||
|
||
PVOID Target = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "ExitProcess"); | ||
|
||
if (SetHook(Target, ExitProcessHook, reinterpret_cast<PVOID*>(&OriginalExitProcess))) | ||
std::wcout << "Initialized ExitProcess(...) hook" << std::endl; | ||
else | ||
std::wcout << "Unable to initialize ExitProcess(...) hook" << std::endl; | ||
|
||
PVOID loadLibraryHook = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); | ||
if (SetHook(loadLibraryHook, LoadLibraryWHook, reinterpret_cast<PVOID*>(&OriginalLoadLibrary))) | ||
std::wcout << "Initialized LoadLibraryW(...) hook..." << std::endl; | ||
else | ||
std::wcout << "Unable to initialize LoadLibraryW(...) hook" << std::endl; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
#include <set> | ||
#include <fstream> | ||
#include <tchar.h> | ||
#include <list> | ||
#include <vector> | ||
|
||
#include "pch.h" | ||
#include "INIReader.h" // https://github.com/jtilly/inih | ||
|
||
#include "HookLib.h" | ||
|
||
VOID WINAPI ExitProcessHook(ULONG ExitCode); | ||
|
||
void InitializePluginHooks(HMODULE gameModule); | ||
|
||
void LoadPlugins(); |
Binary file not shown.
Oops, something went wrong.