C++ Function Hook (memory address only)

User picture User · Oct 23, 2012 · Viewed 25.8k times · Source

I have a memory address, its the memory address of a function in another program (one of its dlls). I am already loaded into the program via DLL injection. I already have the bass address, and the actual location of the function each time the program loads. So, this is not an issue.

I want to just simply hook that location, and grab the variables. I know the function's pseudocode. So this is not an issue. OR another approach that would be great is doing a break point at that memory location and grab the debug registers.

I can not find any clear-cut examples of this. I also do not have the "name" of the function, I just have the memory address. Is there any way to work with just a memory address? Most, if not all the examples have you use the name of the function, which I do not have.

If anyone could point me into the right direction so I can accomplish this task, I would greatly appreciate it. It also might help a lot of other people who may have the same question.

Edit: I should also mention that Id rather not overload my program with someone else code, I really just want the barebones, much like a basic car with roll-up windows. No luxury packages for me please.

Answer

Necrolis picture Necrolis · Oct 23, 2012

You missed the most important part, is this for 32 or 64 bit code? In any case, the code project has a good run-down and lib here that covers both.

If you want to do this "old-school", then it can be done quite simply:

firstly, you need to find the virtual address of the function you want to hook (due to ASLR, you should never rely on it being in the same place), this is generally done with RVA + module base load address for function that are not exported, for exported functions, you can use GetProcAddress.

From there, the type hook depends on what you want to accomplish, in your case, there are two methods:

  1. patch a jump/call out to your function in the target function' prologue
  2. patch all call sites to the function you want to hook, redirecting to your function

the first is simpler, but messy as it generally involves some inline assembly (unless you are hooking a /HOTPATCH binary or you just want to stub it), the second is much cleaner, but requires a bit of work with a debugger.

The function you'll jump out to should have the same parameters and calling convention (ABI) as the function you are hooking, this function is where you can capture the passed parameters, manipulate them, filter calls or whatever you are after.

for both, you need a way to write some assembly to do the patching, under windows, WriteProcessMemory is your first port of call (note: you require RWX permissions to do this, hence the calls to VirtualProtect), this is a little utility function that creates a 32bit relative call or jump (depending on the opcode passed as eType)

#pragma pack(1)
struct patch_t
{
    BYTE nPatchType;
    DWORD dwAddress;
};
#pragma pack()

BOOL ApplyPatch(BYTE eType, DWORD dwAddress, const void* pTarget)
{
    DWORD dwOldValue, dwTemp;
    patch_t pWrite =
    {
        eType,
        (DWORD)pTarget - (dwAddress + sizeof(DWORD) + sizeof(BYTE))
    };

    VirtualProtect((LPVOID)dwAddress,sizeof(DWORD),PAGE_EXECUTE_READWRITE,&dwOldValue);
    BOOL bSuccess = WriteProcessMemory(GetCurrentProcess(),(LPVOID)dwAddress,&pWrite,sizeof(pWrite),NULL);
    VirtualProtect((LPVOID)dwAddress,sizeof(DWORD),dwOldValue,&dwTemp);
    return bSuccess;
}

This function works great for method 2, but for method 1, you'll need to jump to an intermediary assembly trampoline to restore any code that the patch overwrote before returning to the original function, this gets very tedious, which is why its better to just use an existing and tested library.

From the sounds of it, using method 1 and patching a jump over the prologue of your target function will do what you need, as it seems you don't care about executing the function you patched.

(there is a third method using HW breakpoints, but this is very brittle, and can become problematic, as you are limited to 4 HW breakpoints).