Skip to content

Commit

Permalink
Add low-level hooking for plugin loading
Browse files Browse the repository at this point in the history
This seems like the best equivalent, `shcore.dll` gets loaded on the splash, while d3d11 gets loaded *way* before that
  • Loading branch information
FromDarkHell committed Jun 4, 2020
1 parent 4be58c6 commit 0a02722
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 81 deletions.
7 changes: 7 additions & 0 deletions BL3DX11Injection/BL3DX11Injection.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="dllmain.h" />
<ClInclude Include="HookLib.h" />
<ClInclude Include="INIReader.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="PluginLoadHook.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
Expand All @@ -173,6 +175,11 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="PluginLoadHook.cpp" />
</ItemGroup>
<ItemGroup>
<Library Include="HookLib.lib" />
<Library Include="Zydis.lib" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
Expand Down
20 changes: 20 additions & 0 deletions BL3DX11Injection/BL3DX11Injection.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Header Files\Libraries">
<UniqueIdentifier>{9ba04bba-fc2c-4b2e-9fbf-24e8701d4027}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
Expand All @@ -24,6 +27,12 @@
<ClInclude Include="dllmain.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HookLib.h">
<Filter>Header Files\Libraries</Filter>
</ClInclude>
<ClInclude Include="PluginLoadHook.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
Expand All @@ -32,5 +41,16 @@
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PluginLoadHook.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Library Include="HookLib.lib">
<Filter>Header Files\Libraries</Filter>
</Library>
<Library Include="Zydis.lib">
<Filter>Header Files\Libraries</Filter>
</Library>
</ItemGroup>
</Project>
102 changes: 102 additions & 0 deletions BL3DX11Injection/HookLib.h
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 added BL3DX11Injection/HookLib.lib
Binary file not shown.
143 changes: 143 additions & 0 deletions BL3DX11Injection/PluginLoadHook.cpp
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;

}
17 changes: 17 additions & 0 deletions BL3DX11Injection/PluginLoadHook.h
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 added BL3DX11Injection/Zydis.lib
Binary file not shown.
Loading

0 comments on commit 0a02722

Please sign in to comment.