简体   繁体   中英

Hooking OutputDebugStringA raises exception

My target is to hook OutputDebugStringA so I can read whatever messages are passed to it. I'm not trying to hook any other process, but rather the current one, just to learn how to hook.

I'm not using Detours and I'd prefer to not use them, since I want to learn how hooking works on a bit deepeer level.


I'm using this code to hook:

void MakeJMP(BYTE *pAddress, DWORD dwJumpTo, DWORD dwLen);
void myCallback(LPCSTR s);
DWORD dwAddr = (DWORD) GetProcAddress(GetModuleHandleA("kernel32.dll"), "OutputDebugStringA");
// I cast it to byte and later to DWORD so it adds 5 
DWORD dwRetAddr = ((BYTE)dwAddr) + 5; // +5 because I will override 5 bytes

void __declspec(naked) My_OutputDebugStringA(){
    __asm{
        mov edi, edi
        push ebp
        mov ebp, esp
        // ^those 5 bytes are overriden from the original OutputDebugStringA stub from kernel32.dll, so I restore them here
        pushad;
        pushfd; // to prevent stack messing
        push [ebp+8]; // OutputDebugStringA takes a LPCSTR parameter which is ebp + 8 (KernelBase.dll)
        call myCallback; // call my callback to print the string
        popfd;
        popad;
        jmp [dwRetAddr]; // jump back to next instruction so execution continues
    }
}

int _tmain(int argc, _TCHAR* argv[]){
    printf("OutputDebugStringA address is: %8X\nPress any key to hook...", dwAddr);
    system("pause > nul");
    MakeJMP((BYTE*) dwAddr, (DWORD) My_OutputDebugStringA, 5);
    puts("Hooked. Press any key to call it...\n");
    system("pause > nul");
    printf("Calling OutputDebugStringA (%8X) with \"hi\"\n", dwAddr);
    OutputDebugStringA("hi");
    //puts("Called\n");
    //system("pause");
    return 0;
}

void myCallback(LPCSTR s){
    printf("\n===Inside hook!===\nParam address is %8X", &s);
}

void MakeJMP(BYTE *pAddress, DWORD dwJumpTo, DWORD dwLen){
    DWORD dwOldProtect, dwBkup, dwRelAddr;
    VirtualProtect(pAddress, dwLen, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    dwRelAddr = (DWORD)(dwJumpTo - (DWORD)pAddress) - 5;
    *pAddress = 0xE9;
    *((DWORD *)(pAddress + 0x1)) = dwRelAddr;
    for (DWORD x = 0x5; x < dwLen; x++) *(pAddress + x) = 0x90;
    VirtualProtect(pAddress, dwLen, dwOldProtect, &dwBkup);
    return;
}

My function gets called, like this: 控制台输出

However, it breaks there, and I get an exception: 例外

And it leads me to the file fflush.c : fflush.c

I check what 0x77020c02 has (from the exception), and I see this: 拆卸

From the last image, I suppose it could be a problem with restoring the context and / or flushing, but... honestly I have no idea why does it happen, I've hooked functions (non-windows) like this before and I had no problems.


Note : I'm not using this kind of hooks in real code, I'm just trying to do it without external help like MS Detours.

I'm a bit new, so any explanation will be very appreciated. :)

I solved it

First of all, thanks to Hans Passant, who helped me resolve my first crash.


Solution

Seems that hooking the stub wasn't a good idea at all. Instead of hooking kernel32.dll 's stub, I hooked KernelBase.dll 's real function. Just open the DLL in IDA, search for OutputDebugStringA , and you'll find the function: OutputDebugStringA

If you look at it, you'll see that .text:7D8634A4 stores lpOutputString in ecx , which is something we need. Since it's only 3 bytes it can't be JMP ed, so I hooked the next mov (OutputDebugStringA + 0x12), resulting in something like:

DWORD _temp; // here we'll store our address
void __declspec(naked) My_OutputDebugStringA(){
    __asm{
        mov _temp, ecx; // store ecx, or lpOutputString
        pushad; // preserve stack
        pushfd;
        call myCallback; // call our function
        popfd;
        popad; // pop to restore context
        mov dword ptr ss:[ebp-0234h], ecx; // restore overwritten function
        jmp [dwRetAddr]; // go back to caller + original instruction size
        // ^here the mov instruction was 6 bytes, so it'd be hookAddr+6
    }
}

Then, here's the catch . You would probably check if the address is correct in some memory viewer and see that it indeed contains the proper string, BUT if you do *(char*) _temp to read it, you'll crash. The reason is that the function takes a LPCSTR and not a char* , like this:

void OutputDebugStringA(LPCSTR lpOutputString);

So finally, you write your callback:

void myCallback(){
    printf("%s", (LPCSTR) _temp);
}

And you got it! :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM