Skip to content

Commit e53f775

Browse files
committed
Support patching dlls with other name through .rc files.
1 parent e015314 commit e53f775

File tree

3 files changed

+42
-18
lines changed

3 files changed

+42
-18
lines changed

source/include/DetoursHelpers.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
bool DetoursAttachLoadLibraryFunctions();
66
bool DetoursDetachLoadLibraryFunctions();
77

8-
typedef bool (*DetoursDllPatchFunction)(LPCWSTR lpLibFileName, void* userContext, HMODULE hModule);
8+
using DetoursDllPatchFunction = bool (*)(LPCWSTR lpLibFileName, LPCWSTR patchLibraryPath, void* userContext, HMODULE hModule);
99

1010
/// Automatically patch an existing dll or once it is loaded
11-
void DetoursRegisterDllPatch(const wchar_t* dllName, DetoursDllPatchFunction patchFunction, void* userContext);
11+
void DetoursRegisterDllPatch(const wchar_t* dllName, const wchar_t* patchFolder, DetoursDllPatchFunction patchFunction,
12+
void* userContext);
1213
void DetoursApplyPatches();
1314

1415
/// See GetHookOrdinalInfo

source/src/DetoursAutoPatchDirectory.cpp

+3-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ wchar_t envPathBuffer[maxEnvPathLen];
1414

1515
const wchar_t* patchFolder = (0 != GetEnvironmentVariableW(L"DIABLO2_PATCH", envPathBuffer, maxEnvPathLen)) ? envPathBuffer : LR"(.\patch\)";
1616

17-
bool patchDllWithEmbeddedPatches(LPCWSTR lpLibFileName, void*, HMODULE hModule)
17+
bool patchDllWithEmbeddedPatches(LPCWSTR lpLibFileName, LPCWSTR patchLibraryPath, void*, HMODULE hModule)
1818
{
1919
LOGW(L"Patching {}\n", lpLibFileName);
2020

@@ -23,21 +23,11 @@ bool patchDllWithEmbeddedPatches(LPCWSTR lpLibFileName, void*, HMODULE hModule)
2323

2424
DetourUpdateThread(GetCurrentThread());
2525

26-
wchar_t* finalPatchPath = nullptr;
2726
bool patchSucceeded = false;
2827
// We need to keep the addresses that are given to DetourAttach alive until the transaction finishes,
2928
// so we store them in a temporary vector
3029
std::vector<PVOID> keepAliveOrdinalDetoursAddresses;
31-
wchar_t fileNameWithPatchPrefix[MAX_PATH];
32-
if (nullptr != lstrcpynW(fileNameWithPatchPrefix, lpLibFileName, _countof(fileNameWithPatchPrefix))
33-
//If we want to make sure there are no doubts about what .dll file is loaded (original vs patch) we should just rename the patches.
34-
//&& S_OK == PathCchRenameExtension(fileNameWithPatchPrefix, _countof(fileNameWithPatchPrefix), L".patch")
35-
&& S_OK == PathAllocCombine(patchFolder, fileNameWithPatchPrefix, PATHCCH_ALLOW_LONG_PATHS, &finalPatchPath))
36-
{
37-
patchSucceeded = DetoursPatchModule(hModule, finalPatchPath, keepAliveOrdinalDetoursAddresses);
38-
39-
LocalFree(finalPatchPath);
40-
}
30+
patchSucceeded = DetoursPatchModule(hModule, patchLibraryPath, keepAliveOrdinalDetoursAddresses);
4131

4232
if (NO_ERROR != DetourTransactionCommit())
4333
return false;
@@ -71,7 +61,7 @@ void D2DetoursRegisterPatchFolder()
7161
}
7262
do {
7363
LOGW(L"Registering patch for {}.\n", findData.cFileName);
74-
DetoursRegisterDllPatch(findData.cFileName, patchDllWithEmbeddedPatches, nullptr);
64+
DetoursRegisterDllPatch(findData.cFileName, patchFolder, patchDllWithEmbeddedPatches, nullptr);
7565
} while (FindNextFileW(searchHandle, &findData));
7666

7767
FindClose(searchHandle);

source/src/DetoursHelpers.cpp

+36-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "DetoursHelpers.h"
2+
#include "DetoursPatch.h"
23

34
#include <Windows.h>
45
#include <detours.h>
@@ -23,6 +24,7 @@
2324
struct DllPatch
2425
{
2526
std::wstring libraryName;
27+
std::wstring patchLibraryPath;
2628
DetoursDllPatchFunction patchFunction = nullptr;
2729
void* userContext = nullptr;
2830
// We need to prevent double patching to avoid infinite recursions
@@ -45,15 +47,46 @@ static void PatchDLL(LPCWSTR lpLibFileName, HMODULE hModule)
4547
// Do this to avoid recursion, as GetProcAdress can call LoadLibrary
4648
patch.alreadyPatched = true;
4749

48-
if (!patch.patchFunction(fileName, patch.userContext, hModule))
50+
if (!patch.patchFunction(fileName, patch.patchLibraryPath.c_str(), patch.userContext, hModule))
4951
LOGW(L"Failed to patch {}\n", patch.libraryName);
5052
}
5153
}
5254
}
5355
}
54-
void DetoursRegisterDllPatch(const wchar_t* dllName, DetoursDllPatchFunction patchFunction, void* userContext)
56+
void DetoursRegisterDllPatch(const wchar_t* dllName, const wchar_t* patchFolder, DetoursDllPatchFunction patchFunction,
57+
void* userContext)
5558
{
56-
dllPatches.push_back(DllPatch{ dllName, patchFunction, userContext });
59+
// We need to make sure we load the patch .dll, not the one we want to patch.
60+
const std::wstring fullDllPath = fmt::format(L"{}\\{}", patchFolder, dllName);
61+
// To support patching another .dll without specifying a naming convention, we use resource (.rc) files.
62+
// This is because we do not have a way to load a .dll without ensuring we don't load .dlls that should not be loaded yet.
63+
// For example we need to wait for D2Common.dll to be loaded before loading a .dll that patches D2Game and may have D2Common in its import table.
64+
//
65+
// So we actually need to know the .dll we want to patch so that we don't load the patch too early...
66+
// We can only rely on either another file (which is not very practical), reading data from the .dll itself, or embedding this in the patch file name.
67+
// Most of those solutions are not very practical, and since it is possible to load a .dll without loading its dependencies
68+
// nor calling its DllMain (LOAD_LIBRARY_AS_DATAFILE) but still be able to read its resources, that's what we use...
69+
// We then unload the patch .dll if it wasn't already loaded, so that the next LoadLibrary actually loads it correctly.
70+
//
71+
// You will need to create a .rc file with the following content:
72+
// NameOfModuleToPatch 256 { L"TheDLLIWantToPatch.dll\0" }
73+
if (HMODULE patchDLL = TrueLoadLibraryExW(fullDllPath.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE))
74+
{
75+
if (HRSRC NameOfModuleToPatch = FindResource(patchDLL, TEXT("NameOfModuleToPatch"), MAKEINTRESOURCE(256)))
76+
{
77+
HGLOBAL res = nullptr;
78+
const wchar_t* dllToPatch = nullptr;
79+
if ((res = LoadResource(patchDLL, NameOfModuleToPatch)) && (dllToPatch = (const wchar_t*)LockResource(res)))
80+
{
81+
LOGW(L"{} will be used to patch {}\n", dllName, dllToPatch);
82+
dllPatches.push_back(DllPatch{dllToPatch, fullDllPath, patchFunction, userContext});
83+
FreeLibrary(patchDLL);
84+
return;
85+
}
86+
}
87+
FreeLibrary(patchDLL);
88+
}
89+
dllPatches.push_back(DllPatch{dllName, fullDllPath, patchFunction, userContext});
5790
}
5891

5992
void DetoursApplyPatches()

0 commit comments

Comments
 (0)