1
1
#include " DetoursHelpers.h"
2
+ #include " DetoursPatch.h"
2
3
3
4
#include < Windows.h>
4
5
#include < detours.h>
23
24
struct DllPatch
24
25
{
25
26
std::wstring libraryName;
27
+ std::wstring patchLibraryPath;
26
28
DetoursDllPatchFunction patchFunction = nullptr ;
27
29
void * userContext = nullptr ;
28
30
// We need to prevent double patching to avoid infinite recursions
@@ -45,15 +47,46 @@ static void PatchDLL(LPCWSTR lpLibFileName, HMODULE hModule)
45
47
// Do this to avoid recursion, as GetProcAdress can call LoadLibrary
46
48
patch.alreadyPatched = true ;
47
49
48
- if (!patch.patchFunction (fileName, patch.userContext , hModule))
50
+ if (!patch.patchFunction (fileName, patch.patchLibraryPath . c_str (), patch. userContext , hModule))
49
51
LOGW (L" Failed to patch {}\n " , patch.libraryName );
50
52
}
51
53
}
52
54
}
53
55
}
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)
55
58
{
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});
57
90
}
58
91
59
92
void DetoursApplyPatches ()
0 commit comments