Solved

Reverse Hook

Posted on 2004-04-18
11
841 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 

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

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

734 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