RITZ
June 28th, 2006, 18:57
A tool I'm making makes a lot of hooks. My basic hooking technique wrote a jump to the hook procedure over the beginning of the function, but the jump being 5 bytes (at least 2 autonomous instructions to write), created a problem; since I needed to unhook by writing the old code back in every hook procedure so that I could call the old function, every so often the unaware process in which the hooks were made would call the function at the same time as I unhooked it, and I'd get data corruption and an eventual exception just on these rare events. So, I tried everything you can think of to try and synchronize the memory access, but it just turned out to be impossible when the other threads of the process don't cooperate.
So - what I decided to do was to save the original function code and tack a jump back to the rest of the function on the end, then I could instead of jumping straight to the hook procedure, check an enabled variable, and jump either to the hook, or to the original function in the buffer, and let that jump on the end jump back to the rest of the function and I'd only need to change the enabled variable to unhook and call the original function, no thread synchronization problems. But that created another stupid problem... and I can't believe I didn't realize it ahead of time. If the original function code has relative addresses and I run it from the buffer it goes all wrong.
Ok, so that was stupid. Can anyone give me some advice on how to solve this problem? Thanks
BTW, here is that hooking class:
It works great if the first 19 bytes of the function doesn't do anything relative.
So - what I decided to do was to save the original function code and tack a jump back to the rest of the function on the end, then I could instead of jumping straight to the hook procedure, check an enabled variable, and jump either to the hook, or to the original function in the buffer, and let that jump on the end jump back to the rest of the function and I'd only need to change the enabled variable to unhook and call the original function, no thread synchronization problems. But that created another stupid problem... and I can't believe I didn't realize it ahead of time. If the original function code has relative addresses and I run it from the buffer it goes all wrong.
Ok, so that was stupid. Can anyone give me some advice on how to solve this problem? Thanks
BTW, here is that hooking class:
Code:
#pragma once
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
extern "C"
{
#include "xde.h"
}
namespace RITZ
{
template<typename FUNCTIONTYPE>
class HOOK
{
public:
DWORD enabled;
// The original hook isn't thread safe. I've been unable to find a perfect solution.
// There's still theoretically a slim chance that even with us setting the thread priority high
// that the function could be called at the same time and since we require more than one
// instruction to write the hooking stub in, there is a possibility for data corruption
// if another thread tries to read the code simultaneously due to calling the function.
// This CAN however be bypassed if all other threads in the process are suspended.
// But unless you are creating the process with THREAD_SUSPEND,
// getting all threads to suspend smoothly is a whole set of problems on its own.
void Insert(FUNCTIONTYPE from, FUNCTIONTYPE to, HMODULE hmod)
{
const int patch_pos_enabled = 2; // Were to write the hook procedure address operand in to the patch
const int patch_pos_hook = 10; // Were to write the hook procedure address operand in to the patch
const int patch_pos_orig = 15; // And the original code operand
static BYTE patch[] = {
0x83, 0x3D, 0,0,0,0, 0x00, // CMP DWORD PTR DS:[enabled],0 ; Compare 'enable' with 0
0x74, 0x05, // JE SHORT [Disabled code] ; Jump to disabled code if 'enabled' is 0
// Enabled code...
0xE9, 0,0,0,0, // Run the hook procedure
// Disabled code...
// 0xFF, 0x25, 0,0,0,0 // Run the original code
0xE9, 0,0,0,0 // Run the original code
};
// Trace to find how much we need to grab so that we have only whole instructions...
while(m_overwrite_len < sizeof(patch))
{
xde_instr ins;
xde_disasm((unsigned char*)to+m_overwrite_len, &ins);
m_overwrite_len += ins.len;
}
system("pause";
BYTE jumpcode[] = {0xE9, 0,0,0,0}; // Offset jump code
// Save the code we are about to overwrite, with a jump to the rest of the code tacked on
ProtectedMemcpy(from, m_original, m_overwrite_len); // Save original code
*((DWORD*)(jumpcode+1)) = (DWORD)from - (DWORD)&m_original - 5; // Write jump operand
ProtectedMemcpy(jumpcode, m_original+m_overwrite_len, sizeof(jumpcode)); // Concatenate the jump
*((DWORD*)(patch+patch_pos_enabled)) = (DWORD)&enabled;
*((DWORD*)(patch+patch_pos_hook)) = (DWORD)to - (DWORD)from - patch_pos_hook - sizeof(DWORD); // Write hook procedure offset operand in
*((DWORD*)(patch+patch_pos_orig)) = (DWORD)&m_original - (DWORD)from - patch_pos_orig - sizeof(DWORD); // Write original code offset operand in
ProtectedMemcpy((void*)&patch, from, sizeof(patch));
orig = from;
hook = to;
m_hmod = hmod;
}
void Insert(char *from_name, FUNCTIONTYPE to, HMODULE hmod)
{
FARPROC procaddr = GetProcAddress(hmod, from_name);
if(procaddr)
{
Insert((FUNCTIONTYPE)procaddr, to, hmod);
}
}
void Remove()
{
ProtectedMemcpy(m_original, orig, m_overwrite_len);
}
void Enable()
{
enabled = 1;
}
void Disable()
{
enabled = 0;
}
void Reinsert()
{
Insert((FUNCTIONTYPE)m_baseaddr, m_to, m_hmod);
}
FUNCTIONTYPE orig; // For calling after unhooking.
FUNCTIONTYPE hook;
private:
BYTE m_original[100];
DWORD m_overwrite_len;
HMODULE m_hmod;
void ProtectedMemcpy(void *from, void *to, DWORD size)
{
DWORD oldprot_from;
DWORD oldprot_to;
VirtualProtect(from, size, PAGE_EXECUTE_READWRITE, &oldprot_from);
VirtualProtect(to, size, PAGE_EXECUTE_READWRITE, &oldprot_to);
DWORD dummy;
int oldprior = GetThreadPriority(GetCurrentThread());
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
BOOL test = WriteProcessMemory(GetCurrentProcess(), to, from, size, &dummy);
SetThreadPriority(GetCurrentThread(), oldprior);
VirtualProtect(from, size, oldprot_from, &oldprot_from);
VirtualProtect(to, size, oldprot_from, &oldprot_to);
}
public:
HOOK()
{
enabled = 1;
}
HOOK(FUNCTIONTYPE from, FUNCTIONTYPE to, HMODULE hmod)
{
Insert(from, to, hmod);
HOOK();
}
~HOOK()
{
Remove();
}
};
}
It works great if the first 19 bytes of the function doesn't do anything relative.