Skip to content

Commit 0007547

Browse files
committed
Provide 2 new ways to patch: GetPatchInformationFunctions and DllPreLoadHook
Going forward, `DllPreLoadHook` will be the preferred way to patch. This gives more control to the user (if they need to do partial patching for example), and is easier to understand (less "magic").
1 parent 4108742 commit 0007547

File tree

3 files changed

+318
-136
lines changed

3 files changed

+318
-136
lines changed

source/include/DetoursPatch.h

+100-33
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,109 @@
11
#pragma once
22

3-
enum class PatchAction {
4-
FunctionReplaceOriginalByPatch,
5-
FunctionReplacePatchByOriginal,
6-
PointerReplaceOriginalByPatch,
7-
PointerReplacePatchByOriginal,
8-
Ignore
9-
};
3+
#include <cstdint>
104

11-
struct ExtraPatchAction {
12-
size_t originalDllOffset;
13-
void* patchData;
14-
PatchAction action;
15-
void* detouredPatchedFunction; // Filled with new address of the original function. You can use it to call the old function from your patch.
5+
enum class PatchAction
6+
{
7+
FunctionReplaceOriginalByPatch,
8+
FunctionReplacePatchByOriginal,
9+
PointerReplaceOriginalByPatch,
10+
PointerReplacePatchByOriginal,
11+
Ignore
1612
};
1713

18-
extern "C" {
19-
typedef int(__cdecl* GetIntegerFunctionType)();
20-
typedef PatchAction (__cdecl *GetPatchActionType)(int ordinal);
21-
typedef ExtraPatchAction* (__cdecl* GetExtraPatchActionType)(int extraPatchActionIndex);
22-
}
23-
24-
struct PatchInformationFunctions
14+
struct ExtraPatchAction
2515
{
26-
// Must be exposed by the patch dll and return the 1st ordinal to patch
27-
GetIntegerFunctionType GetBaseOrdinal = nullptr;
28-
// Must be exposed by the patch dll and return the last ordinal to patch
29-
GetIntegerFunctionType GetLastOrdinal = nullptr;
30-
// Must be exposed by the patch dll and return the action to take for a given ordinal
31-
GetPatchActionType GetPatchAction = nullptr;
32-
33-
// Must be exposed by the patch dll if extra patches are needed
34-
GetIntegerFunctionType GetExtraPatchActionsCount = nullptr;
35-
// Must be exposed by the patch dll and return the action to take for a given extra action index
36-
// Note that the returned pointer must be valid until the end of the patching, and not be reused between GetExtraPatchAction calls since we will fill the detouredPatchedFunction field.
37-
GetExtraPatchActionType GetExtraPatchAction = nullptr;
16+
size_t originalDllOffset;
17+
void* patchData;
18+
PatchAction action;
19+
20+
// You may select another location to store the real function pointer. This may make it easier to reference it from your hooks.
21+
void* detouredPatchedFunctionPointerStorageAddress = &detouredPatchedFunctionPointer;
22+
// This will always be filled, but for convenience `detouredPatchedFunctionPointerAddress` may point to your own variable instead of detouredPatchedFunctionPointer.
23+
void* detouredPatchedFunctionPointer = nullptr;
3824
};
3925

26+
extern "C"
27+
{
28+
// Old API, where one had to export GetBaseOrdinal+GetLastOrdinal+GetPatchAction (and optionally GetExtraPatchActionsCount+GetExtraPatchAction), or expose all through GetPatchInformationFunctions
29+
// Please use DllPreLoadHook now, as it gives you more freedom and should now be the preferred API.
30+
// For compatibility and ease of use, and/or if you need multi-dll support, you may use GetPatchInformationFunctions
31+
typedef int(__cdecl* GetIntegerFunctionType)();
32+
typedef PatchAction(__cdecl* GetPatchActionType)(int ordinal);
33+
typedef ExtraPatchAction*(__cdecl* GetExtraPatchActionType)(int extraPatchActionIndex);
34+
35+
struct PatchInformationFunctions
36+
{
37+
// Must be exposed by the patch dll and return the 1st ordinal to patch
38+
GetIntegerFunctionType GetBaseOrdinal = nullptr;
39+
// Must be exposed by the patch dll and return the last ordinal to patch
40+
GetIntegerFunctionType GetLastOrdinal = nullptr;
41+
// Must be exposed by the patch dll and return the action to take for a given ordinal
42+
GetPatchActionType GetPatchAction = nullptr;
43+
44+
// Must be exposed by the patch dll if extra patches are needed
45+
GetIntegerFunctionType GetExtraPatchActionsCount = nullptr;
46+
// Must be exposed by the patch dll and return the action to take for a given extra action index
47+
// Note that the returned pointer must be valid until the end of the patching, and not be reused between
48+
// GetExtraPatchAction calls since we will fill the detouredPatchedFunction field.
49+
GetExtraPatchActionType GetExtraPatchAction = nullptr;
50+
};
51+
typedef PatchInformationFunctions(__cdecl* GetPatchInformationFunctionsType)(const wchar_t* dllName);
52+
53+
enum PatchActionReturn
54+
{
55+
PatchAction_Success,
56+
PatchAction_AlreadyPatched, // Nothing was done because the function was already replaced
57+
PatchAction_CircularPatching, // Nothing was done because the function had been replaced in the other direction before
58+
PatchAction_PatchFunctionWasPatched, // Nothing was done because the source function had already been replaced before
59+
PatchAction_BadInput, // Input parameters are invalid
60+
PatchAction_PatchFailed, // Patching utility failed with an unknown reason. It might be due to a breakpoint being placed at the beginning of the target function, or the target function being patched by another system
61+
};
62+
63+
// Convenience function for all kind of patching with sanity checks
64+
// Similar in spirit to the old API, except you are the one calling the patching functions.
65+
//
66+
// originalDllOffset is the offset (function or address) relative to the base of the original dll. Note that it may be the source or destination of replacement depending on `patchAction`.
67+
// patchDllAddr is the address of the patch dll (function or address of pointer). Note that it may be the source or destination of replacement depending on `patchAction`.
68+
// patchAction is the action you want to do (see the enum)
69+
// realPatchedFunctionPointerStorageAddress is used to store the address of the unpatched function, it may be used to call the function that was replaced from the patch itself. Use nullptr if you do not need it.
70+
typedef PatchActionReturn(__cdecl* ApplyPatchActionType)(struct HookContext* context, uintptr_t originalDllOffset,
71+
void* patchDllAddr, PatchAction patchAction,
72+
void** realPatchedFunctionPointerStorageAddress);
73+
74+
// Mostly the same as ApplyPatchAction for functions only, except that no sanity checks will be done at all.
75+
// Not recommended unless you know what you are doing.
76+
typedef PatchActionReturn(__cdecl* ReplaceAnyFunctionType)(HookContext* context, void* originalFunction,
77+
void* patchFunction, void** realPatchedFunctionStorage);
78+
79+
struct HookContext
80+
{
81+
void* pContextPrivateData;
82+
void* hOriginalModule; // The module you want to patch. May be safely casted to HMODULE
83+
void* hPatchModule; // The module containing the patch. May be safely casted to HMODULE
84+
ApplyPatchActionType ApplyPatchAction;
85+
ReplaceAnyFunctionType ReplaceAnyFunction;
86+
};
87+
88+
89+
// Expected to be exported as ordinal under the name `DllPreLoadHook`
90+
//
91+
// Usage example:
92+
//
93+
// __declspec(dllexport) uint32_t __cdecl DllPreLoadHook(HookContext* ctx, const wchar_t* dllName)
94+
// {
95+
// for (auto& p : MyExtraPatchActions)
96+
// {
97+
// ctx->ApplyPatchAction(ctx, p.originalDllOffset, p.patchData, p.action, (void**)p.detouredPatchedFunctionPointerStorageAddress);
98+
// }
99+
// return 0; // success
100+
// }
101+
//
102+
// Will be called before any patching is done, right after the first `LoadLibrary` call for the original .dll.
103+
// Return 0 on success
104+
// hOriginalDllModule and hPatchDllModule may be safely casted to HMODULE
105+
typedef uint32_t(__cdecl* DllPreLoadHookType)(HookContext* ctx, const wchar_t* dllName);
106+
}
40107
#ifdef DETOURS_PATCH_PRIVATE
41108
#include <Windows.h>
42109
#include <vector>
@@ -45,6 +112,6 @@ struct PatchInformationFunctions
45112
* Patch hOriginalModule using a dll with a given path.
46113
* Ordinals patching is determined by the patch dll, and it must expose the functions in PatchInformationFunctions.
47114
*/
48-
bool DetoursPatchModule(HMODULE hOriginalModule, HMODULE hPatchModule, std::vector<PVOID>& ordinalDetouredAddresses);
115+
bool DetoursPatchModule(LPCWSTR lpLibFileName, HMODULE hOriginalModule, HMODULE hPatchModule,
116+
std::vector<PVOID>& ordinalDetouredAddresses);
49117
#endif
50-

source/src/DetoursAutoPatchDirectory.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ bool patchDllWithEmbeddedPatches(LPCWSTR lpLibFileName, LPCWSTR patchLibraryPath
2828
// We need to keep the addresses that are given to DetourAttach alive until the transaction finishes,
2929
// so we store them in a temporary vector
3030
std::vector<PVOID> keepAliveOrdinalDetoursAddresses;
31-
patchSucceeded = DetoursPatchModule(hModule, hModulePatch, keepAliveOrdinalDetoursAddresses);
31+
patchSucceeded = DetoursPatchModule(lpLibFileName, hModule, hModulePatch, keepAliveOrdinalDetoursAddresses);
3232

3333
if (NO_ERROR != DetourTransactionCommit()) return false;
3434
return patchSucceeded;

0 commit comments

Comments
 (0)