As soon EasyHook EasyHook64.dll
intercepts the first DefWindowProcW
message, and from it starts a thread, it does not catch any DefWindowProcW
anymore:
|___ DefWindowProcW (caught)
|--
|--
|--
|-- DefWindowProcW (don't 'intercept' anymore)
|-- ...
It stops 'catching' all DefWindowProcW
messages until the thread end.
I was told:
This is by design, it is part of the thread deadlock barrier.
And:
You could install two hooks for the same function, leaving the second disabled until you enter your first hook, you would enable it by setting its ACL inclusive to the current thread, then disable it again as you leave the first hook.
Then I tried to call it like:
HOOK_TRACE_INFO hHook = { NULL };
HOOK_TRACE_INFO hHook2 = { NULL };
HOOK_TRACE_INFO hHook3 = { NULL };
LRESULT __stdcall DefWindowProcW_Hook( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg) {
//......
}
ULONG ACLEntries[1] = { 0 };
LhSetInclusiveACL(ACLEntries, 1, &hHook);
ULONG ACLEntries2[1] = { 0 };
LhSetInclusiveACL(ACLEntries2, 1, &hHook2);
ULONG ACLEntries3[1] = { 0 };
LhSetInclusiveACL(ACLEntries2, 1, &hHook3);
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
// =======================================
void __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* inRemoteInfo)
{
LhInstallHook(
GetProcAddress(GetModuleHandle(TEXT("user32")), "DefWindowProcW"),
DefWindowProcW_Hook, NULL, &hHook);
ULONG ACLEntries[1] = { 0 };
LhSetExclusiveACL(ACLEntries4, 1, &hHook);
LhInstallHook(
GetProcAddress(GetModuleHandle(TEXT("user32")), "DefWindowProcW"),
DefWindowProcW_Hook, NULL, &hHook2);
LhInstallHook(
GetProcAddress(GetModuleHandle(TEXT("user32")), "DefWindowProcW"),
DefWindowProcW_Hook, NULL, &hHook3);
}
But now, looks like all hooks are doing the same thing:
Result:
Would like to ask someone who uses or already used EasyHook
how to properly read the same function when there is a 2nd or more nested call.
There is no elegant solution for this in EasyHook, however if you are happy to install as many intermediate hooks as you need for the nesting levels you can chain them together.
The outermost hook will be the only one enabled initially.
It will first ensure all innermost hooks are disabled, then enable the next hook.
To prevent calling the hook for the same DefWindowProcW call, we can retrieve the next hook's bypass address and call that instead of the original DefWindowProcW like you would normally do.
As you have already discovered, once within a hook handler for a hook it will not trigger that same hook again until after the return statement has completed and we have gone back up the call stack. This is due to the thread-deadlock barrier in EasyHook.
Example:
HOOK_TRACE_INFO hHook = { NULL };
HOOK_TRACE_INFO hHook2 = { NULL };
HOOK_TRACE_INFO hHook3 = { NULL };
typedef LRESULT __stdcall DefWindowProcFunc(HWND, UINT, WPARAM, LPARAM);
// Innermost hook handler
LRESULT __stdcall DefWindowProcW_Hook( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg) {
//......
}
// Innermost hook just call original
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
// Middle hook handler
LRESULT __stdcall DefWindowProcW_Hook2( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg) {
//......
}
// Activate next hook handler for next nesting level
ULONG ACLEntries[1] = { 0 };
LhSetInclusiveACL(ACLEntries, 1, &hHook);
PVOID* bypass_address;
LhGetHookBypassAddress(&hHook, &bypass_address);
//return DefWindowProcW(hWnd, Msg, wParam, lParam);
// Bypass the handler for THIS call to DefWindowProcW - the next nested call will be captured in DefWindowProcW_Hook
return ((DefWindowProcFunc*)bypass_address)(hWnd, Msg, wParam, lParam);
}
// Outermost hook handler
LRESULT __stdcall DefWindowProcW_Hook3( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
// Outermost hook handler - this will be called first for new calls to DefWindowProcW
switch (Msg) {
//......
}
// Disable innermost hook(s) - do this for each inner hook that isn't the next hook
ULONG disableACLEntries[1] = { 0 };
LhSetExclusiveACL(disableACLEntries, 1, &hHook);
// Activate next hook handler for first nesting level
ULONG ACLEntries[1] = { 0 };
LhSetInclusiveACL(ACLEntries, 1, &hHook2);
PVOID* bypass_address;
LhGetHookBypassAddress(&hHook2, &bypass_address);
//return DefWindowProcW(hWnd, Msg, wParam, lParam);
// Bypass the handler for THIS call to DefWindowProcW - the next nested call will be captured in DefWindowProcW_Hook2
return ((DefWindowProcFunc*)bypass_address)(hWnd, Msg, wParam, lParam);
}
// =======================================
void __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* inRemoteInfo)
{
// NOTE: the last installed hook handler will be the first called i.e. hHook3, then hHook2 then hHook.
LhInstallHook(
GetProcAddress(GetModuleHandle(TEXT("user32")), "DefWindowProcW"),
DefWindowProcW_Hook, NULL, &hHook);
LhInstallHook(
GetProcAddress(GetModuleHandle(TEXT("user32")), "DefWindowProcW"),
DefWindowProcW_Hook2, NULL, &hHook2);
LhInstallHook(
GetProcAddress(GetModuleHandle(TEXT("user32")), "DefWindowProcW"),
DefWindowProcW_Hook3, NULL, &hHook3);
// Activate outermost hook (ie last installed hook which will be the first called - hHook3)
ULONG ACLEntries[1] = { 0 };
LhSetExclusiveACL(ACLEntries, 1, &hHook3);
}