Solved

Reverse Hook

Posted on 2004-04-18
11
807 Views
Last Modified: 2012-06-21
What's going on here, and how can I UnHook?


bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)
{
    HINSTANCE hMod = GetModuleHandle(module);

    pProc = (BYTE *) GetProcAddress(hMod, proc);

    if (pProc[0] == 0xB8)
    {
        syscall_id = * (unsigned *) (pProc + 1);

        DWORD flOldProtect;

        VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        pProc[0] = 0xE9;
        * (unsigned *) (pProc+1) = (unsigned)pNewProc - (unsigned) (pProc+5);

        pProc += 5;

        return true;
    }
    else
    {
        return false;
    }
}
0
Comment
Question by:rossryan
  • 7
  • 4
11 Comments
 
LVL 10

Expert Comment

by:Mercantilum
ID: 10857177
This Hook calls a dll module.

You should have a

   HHOOK hook = SetWindowsHookEx (...);

Somewhere in your code

Call

    UnhookWindowsHookEx (hook);

To UnHook this hook.
0
 
LVL 10

Expert Comment

by:Mercantilum
ID: 10857204
Well, more precisely
<< The Hook routine hooks certain kind of exported function from a module by directly modifying its starting machine code. The benefit of hacking machine code directly is that you only need to hack into a single place, all the call in a process is taken care of. But hacking machine code directly is very tricky because it's not easy to parse machine code to find extra space for a five byte jump instruction. Chapter 4 of my book contains more generic code to handle this problem. What's shown here only applies to a special case, which applies to BeginPaint and EndPaint on Windows NT/2000 machines. On these machine, BeginPaint and EndPaint calls system services provided by Win32K.SYS. These routines follow a strict pattern, the first instruction stores a DWORD index into the EAX register. The instructions after that issue a software interruption (0x2E), which will be served by Win32K.SYS in kernel mode address space.

The Hook routine uses GetModuleHandle to retrieve module handle, GetProcAddress to retrieve the address of an exported Win32 API function. It then checks if the first instruction is a constant move to EAX register instruction (0xB8). If a match is found, VirtualProtect is used to change the protection flag for that page to PAGE_EXECUTE_READWRITE, which makes it writeable. The system service call index is saved, and then the first five bytes are changed to a jump instruction to a function whose address is passed through the pNewProc parameter. >>

All details here http://www.fengyuan.com/article/wmprint.html
0
 

Author Comment

by:rossryan
ID: 10857302
Yes, that is where this code is from. But calling UnHookWindowEx does not unhook the dll properly (either that or I am calling it wrong). What I need is to reverse the assembly modification made by the hook, so that I can unhook it successfully.
0
 
LVL 10

Expert Comment

by:Mercantilum
ID: 10857349
To reverse it:

1. modify your hooking code:

// create global vars for instance
unsigned save_syscallid;                          // I guess this was the role of syscall_id but anyway...
BYTE *save_pProc = NULL;

bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)
{
    HINSTANCE hMod = GetModuleHandle(module);

    pProc = (BYTE *) GetProcAddress(hMod, proc);

    if (pProc[0] == 0xB8)
    {
        syscall_id = * (unsigned *) (pProc + 1);

       // ++
       save_pProc = pProc;
       save_syscallid = syscall_id;

        DWORD flOldProtect;

        VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        pProc[0] = 0xE9;
        * (unsigned *) (pProc+1) = (unsigned)pNewProc - (unsigned) (pProc+5);

        pProc += 5;

        return true;
    }
    else
    {
        return false;
    }
}


/// Code to release your hook

void ReleaseHook()
{
    if (pProc[0] == 0xE9 && save_pProc)
    {
        DWORD flOldProtect;
        VirtualProtect(save_pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        *save_pProc = 0xB8; // reset first byte
        *(unsigned *) (pProc + 1) = save_syscallid;
    }
}
0
 
LVL 10

Expert Comment

by:Mercantilum
ID: 10857362
Sorry a mistake :)


void ReleaseHook()
{
    if (save_pProc && save_pProc[0] == 0xE9)
    {
        DWORD flOldProtect;
        VirtualProtect(save_pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        *save_pProc = 0xB8; // reset first byte
        *(unsigned *) (save_pProc + 1) = save_syscallid;
    }
}
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 

Author Comment

by:rossryan
ID: 10857376
Mercantilum, I'd like you (if you will) to take a look at the whole source, and see if there are any other relevant parts to modify.


#include "stdafx.h"
#include <assert.h>

#include "hookpaint.h"

#pragma warning(disable : 4311 4312)

bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)
{
    HINSTANCE hMod = GetModuleHandle(module);

    pProc = (BYTE *) GetProcAddress(hMod, proc);

    if (pProc[0] == 0xB8)
    {
        syscall_id = * (unsigned *) (pProc + 1);

        DWORD flOldProtect;

        VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        pProc[0] = 0xE9;
        * (unsigned *) (pProc+1) = (unsigned)pNewProc - (unsigned) (pProc+5);

        pProc += 5;

        return true;
    }
    else
    {
        return false;
    }
}


static unsigned syscall_BeginPaint = 0;
static BYTE *   pBeginPaint        = NULL;

static unsigned syscall_EndPaint   = 0;
static BYTE *   pEndPaint          = NULL;


LRESULT CPaintHook::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    assert(m_OldWndProc);

    if (uMsg == WM_PRINTCLIENT)
    {
        m_hDC = (HDC) wParam;
        uMsg  = WM_PAINT;
    }
       
    LRESULT hRslt = CallWindowProc(m_OldWndProc, hWnd, uMsg, wParam, lParam);

    m_hDC = NULL;

    return hRslt;
}
      

HDC WINAPI CPaintHook::MyBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
{
    const CPaintHook * pThis = (CPaintHook *) GetWindowLong(hWnd, GWL_WNDPROC);
   
    pThis = (const CPaintHook *) ( (unsigned) pThis - (unsigned) & pThis->m_thunk[0] + (unsigned) pThis );
   
    if (pThis->Within_WM_PRINT())
    {
        memset(lpPaint, 0, sizeof(PAINTSTRUCT));

        lpPaint->hdc = pThis->m_hDC;
       
        GetClientRect(hWnd, & lpPaint->rcPaint);
       
        return pThis->m_hDC;
    }
    else
    {
        __asm   mov     eax, syscall_BeginPaint
        __asm   push    lpPaint
        __asm   push    hWnd
        __asm   call    pBeginPaint
    }
}


BOOL WINAPI CPaintHook::MyEndPaint(HWND hWnd, LPPAINTSTRUCT lpPaint)
{
    const CPaintHook * pThis = (CPaintHook *) GetWindowLong(hWnd, GWL_WNDPROC);
   
    pThis = (const CPaintHook *) ( (unsigned) pThis - (unsigned) & pThis->m_thunk[0] + (unsigned) pThis );
   
    if (pThis->Within_WM_PRINT())
    {
        return TRUE;
    }
    else
    {
        __asm   mov     eax, syscall_EndPaint
        __asm   push    lpPaint
        __asm   push    hWnd
        __asm   call    pEndPaint
    }
}

CPaintHook::CPaintHook()
{
    static bool s_hooked = false;

    if ( ! s_hooked )
    {
        Hook("USER32.DLL", "BeginPaint", syscall_BeginPaint, pBeginPaint, MyBeginPaint);
        Hook("USER32.DLL", "EndPaint",   syscall_EndPaint,   pEndPaint,   MyEndPaint);

        s_hooked = true;
    }

    m_thunk[0]              = 0xB9;               // mov ecx,
    *((DWORD *)(m_thunk+1)) = (DWORD) this;  //          this
      *((DWORD *)(m_thunk+5)) = 0x20FF018B;    // mov eax, [ecx]

    m_OldWndProc = NULL;
    m_hDC        = NULL;
    m_hWnd       = NULL;
}


void CPaintHook::SubClass(HWND hWnd)
{            
    m_hWnd       = hWnd;
    m_OldWndProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);
   
    SetWindowLong(hWnd, GWL_WNDPROC, (LONG) ((void *) m_thunk));
}

CPaintHook::~CPaintHook()
{
    if (m_OldWndProc)
    {
        SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG) m_OldWndProc);
    }
}
0
 

Author Comment

by:rossryan
ID: 10857381
To be honest, I'm not upt the creek about the functioning of this code: I'm heading out to sea.
0
 
LVL 10

Expert Comment

by:Mercantilum
ID: 10857458
Oh it is not a hooking actually :)
Before to release the DLL simply invert the operation for *both* calls

Change to my previous code:

// create global vars for instance for 2 CALLS !!

#define CALLS   2

unsigned save_syscallid[CALLS];  
BYTE *save_pProc[CALLS] = { NULL, NULL };
int call_number = 0;

bool Hook(const TCHAR * module, const TCHAR * proc, unsigned & syscall_id, BYTE * & pProc, const void * pNewProc)
{
    HINSTANCE hMod = GetModuleHandle(module);

    pProc = (BYTE *) GetProcAddress(hMod, proc);

    if (pProc[0] == 0xB8)
    {
        syscall_id = * (unsigned *) (pProc + 1);

       // ++
       save_pProc[call_number] = pProc;
       save_syscallid[call_number] = syscall_id;
       call_number++;

        DWORD flOldProtect;

        VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

        pProc[0] = 0xE9;
        * (unsigned *) (pProc+1) = (unsigned)pNewProc - (unsigned) (pProc+5);

        pProc += 5;

        return true;
    }
    else
    {
        return false;
    }
}


/// Code to reset your DLL procs

void ResetDLLProcs()
{
    int i;

     for (i=0 ; i<call_number ; i++)
     {
       if (save_pProc[i] && save_pProc[i][0] == 0xE9)
       {
           DWORD flOldProtect;
           VirtualProtect(save_pProc[i], 5, PAGE_EXECUTE_READWRITE, & flOldProtect);

           save_pProc[i][0] = 0xB8; // reset first byte
           *(unsigned *) (save_pProc[i] + 1) = save_syscallid;
       }
    }
}

/// Call ResetDLLProcs before to release your DLL USER32.dll
/// using FreeLibrary ()  (or when returning from the program)
0
 
LVL 10

Expert Comment

by:Mercantilum
ID: 10857464
Aaah another small mistake :)
Should be :
// Last line of ResetDLLProcs:

*(unsigned *) (save_pProc[i] + 1) = save_syscallid[i];
0
 

Author Comment

by:rossryan
ID: 10857496
Oh, it's a hooking alright ;). Here's the API interface (from another lovely file).

// hook.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "hookpaint.h"


// Shared Data

#pragma data_seg("Shared")

HHOOK   h_WndProcHook  = NULL;

#pragma data_seg()

HINSTANCE hInstance;

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                               )
{
    hInstance = (HINSTANCE) hModule;

    return TRUE;
}


LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CWPRETSTRUCT * pInfo = (CWPRETSTRUCT *) (lParam - 4);

    if ((pInfo != NULL) && (pInfo->message == WM_USER) && (pInfo->lParam == 130869856))
    {
        CaptureWindow(pInfo->hwnd);
    }
      /*
      if ((pInfo != NULL) && (pInfo->message == WM_USER) && (pInfo->lParam == 130869857))
    {
         UnhookWindowsHookEx(h_WndProcHook);
    }
    */
    if (h_WndProcHook)
    {
        return CallNextHookEx(h_WndProcHook, nCode, wParam, lParam);
    }
    else
    {
        return 0;
    }
}


void _declspec(dllexport) InstallHook(void)
{
         h_WndProcHook  = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC) CallWndProc, hInstance, 0);
      
}


Using your modified code, this will reverse the dlls effect?
0
 
LVL 10

Accepted Solution

by:
Mercantilum earned 500 total points
ID: 10857537
There are two different things

1. the hacking of DLL procs - that was the ResetDLLProcs () function I made

    - calling  the initiall program with Hook ...
    - calling ResetDLLProcs before to leave the program

2. the actuall hooking

   The last portion of code you provided is inserting a hook to the window management.
    (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Hooks/HookReference/HookFunctions/CallWndProc.asp)

   This is a common way to interfere with the windows management of your program.
   This one does not need to be released (since the hook is part of your program's windows management)

To summarize, in the first program you have better to call ResetDLLProcs() before to terminate.
FOr the last one (window hook) you don't have to release the hook.
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now