Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Reverse Hook

Posted on 2004-04-18
11
Medium Priority
?
871 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
Independent Software Vendors: 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 2000 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

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…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
Suggested Courses

610 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