Mouse Hook Demo (Windows only)

Post your FreeBASIC tips and tricks here. Please don’t post your code without including an explanation.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Mouse Hook Demo (Windows only)

Postby MichaelW » Jun 13, 2006 3:34

This was originally based on Iczelion’s Win32 ASM Tutorial 24, available here:

http://win32assembly.online.fr/tutorials.html

To implement a global (system-wide) hook, the hook procedure must be in a DLL, and a single copy of the DLL data (actually, in this case just two variables) must be shared by every application in the desktop. Placing the hook procedure in a DLL is no problem, but sharing the data is a problem when linking with LD. Other linkers, Microsoft’s 32-bit linkers and Jeremy Gordon’s GoLink for example, can set the shared attribute for a DLL data section, but as far as I have been able to determine, LD cannot. So instead of trying to use some other linker, I just patched the DLL. Doing so probably invalidates the checksum, but as far as I know the checksum only matters for Kernel mode drivers and system DLLs.

dialog.bi:

Code: Select all

#define IDD_DLG 101
#define IDC_CLASSNAME 1000
#define IDC_HANDLE 1001
#define IDC_WNDPROC 1002

dialog.rc:
#include "dialog.bi"

#define IDC_STATIC -1
#define DS_MODALFRAME 0x80
#define WS_POPUP 0x80000000
#define WS_CAPTION 0xC00000
#define WS_SYSMENU 0x80000
#define ES_AUTOHSCROLL 0x80
#define ES_READONLY 0x800

IDD_DLG DIALOGEX 0,0,164,78
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Mouse Hook Demo"
FONT 8, "MS Sans Serif"
BEGIN
GROUPBOX "Window Information",IDC_STATIC,8,7,148,63
LTEXT "Class Name: ",IDC_STATIC,17,21,39,8
EDITTEXT IDC_CLASSNAME,60,20,90,10,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Handle: ",IDC_STATIC,32,36,24,8
EDITTEXT IDC_HANDLE,60,35,90,10,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Window Proc: ",IDC_STATIC,14,51,50,8
EDITTEXT IDC_WNDPROC,60,50,90,10,ES_AUTOHSCROLL | ES_READONLY
END

mhook.bas:

Code: Select all

'============================================================================
#include once "windows.bi"
#include "dialog.bi"
'============================================================================
#define WM_MOUSEHOOK WM_USER + 6
'============================================================================

'--------------------------------------------------------------------
'' This is the callback function that processes the messages sent to
'' the dialog. The Microsoft documentation uses "DialogProc" as a
'' placeholder for this function.
'--------------------------------------------------------------------

function DialogProc( byval hDlg as  HWND, _
                     byval uMsg as UINT, _
                     byval wParam as WPARAM, _
                     byval lParam as LPARAM ) as integer

    '---------------------------------------------------
    '' These need to retain their values between calls.
    '---------------------------------------------------

    static hLib as any ptr
    static InstallHook as function( byval caller_hWnd as HANDLE ) as integer
    static UninstallHook as sub

    dim as integer slen
    dim as RECT rc

    dim as zstring * 128 oldClassName
    dim as zstring * 128 newClassName
    dim as zstring * 16 oldHandle
    dim as zstring * 16 newHandle
    dim as zstring * 16 oldWndProc
    dim as zstring * 16 newWndProc

    select case uMsg

        case WM_CLOSE

            UninstallHook()

            '-----------------------------------------------
            '' This delay prevents an access violation when
            '' terminating the program, running on a slow
            '' system (K5-PR166) under Windows 98 SE.
            '-----------------------------------------------

            sleep 100

            Dylibfree( hLib )
            EndDialog( hDlg, null )

        case WM_INITDIALOG

            '--------------------------------------
            '' Make the dialog the topmost window.
            '--------------------------------------

            GetWindowRect( hDlg, @rc )
            SetWindowPos( hDlg,_
                          HWND_TOPMOST,_
                          rc.left,_
                          rc.top,_
                          rc.right,_
                          rc.bottom,_
                          SWP_SHOWWINDOW )

            '--------------------------------------
            '' Dylibload and Dylibsymbol can fail!
            '--------------------------------------

            hLib = Dylibload( "mhookdll.dll" )
            if hLib = null then
                MessageBox( 0, "Dylibload failed", 0, 0 )
                end
            end if

            '----------------------
            '' Note mangled names.
            '----------------------

            InstallHook = Dylibsymbol( hLib, "INSTALLHOOK@4" )
            if InstallHook = null then
                MessageBox( 0, "Dylibsymbol(InstallHook) failed", 0, 0 )
                Dylibfree( hLib )
                end
            end if

            UninstallHook = Dylibsymbol( hLib, "UNINSTALLHOOK@0" )
            if UninstallHook = null then
                MessageBox( 0, "Dylibsymbol(UninstallHook) failed", 0, 0 )
                Dylibfree( hLib )
                end
            end if

            InstallHook( hDlg )

        case WM_MOUSEHOOK

            '-------------------------------------------------------
            '' To minimize flicker, update only when values change.
            '-------------------------------------------------------

            GetDlgItemText( hDlg,IDC_CLASSNAME, oldClassName, 128 )
            GetClassName( cast( HWND, wParam), newClassName, 128 )
            if oldClassName <> newClassName then
                SetDlgItemText( hDlg,IDC_CLASSNAME, newClassName )
            end if

            slen = GetDlgItemText( hDlg, IDC_HANDLE, oldHandle, 16 )
            oldHandle = left( oldHandle, slen )
            newHandle = ucase( hex( wParam ) )
            if oldHandle <> newHandle then
                SetDlgItemText( hDlg, IDC_HANDLE, newHandle )
            end if

            slen = GetDlgItemText( hDlg, IDC_WNDPROC, oldWndProc, 16 )
            oldWndProc = left( oldWndProc, slen )
            newWndProc = ucase(hex(GetClassLong(_
                    cast( HWND, wParam ), GCL_WNDPROC )))
            if oldWndProc <> newWndProc then
                SetDlgItemText( hDlg, IDC_WNDPROC, newWndProc )
            end if

        case else

            '-------------------------
            '' Message not processed.
            '-------------------------

            return false

    end select

    '---------------------
    '' Message processed.
    '---------------------

    return true

end function

'----------------------------------------------------------
'' Create a modal dialog box from the IDD_MAINDLG template
'' specified in dialog.rc. This function will not return
'' until the dialog box is terminated.
'----------------------------------------------------------

DialogBoxParam( GetModuleHandle( null ),_
                cast( LPCSTR, IDD_DLG ),_
                null,_
                cast( DLGPROC, @DialogProc ),_
                null )

end

mhookdll.bas:

Code: Select all

'============================================================================
#include once "windows.bi"
'============================================================================

'---------------------------------
'' This not in the v0.20 headers.
'---------------------------------

#define WM_MOUSEHOOK &h406

'-----------------------------------------------------------------
'' Beyond the need for these two variables to be accessible from
'' the local procedures, to implement a global (system-wide) hook
'' they must be shared, as a single instance, by every application
'' in the desktop. FBC will place them in the uninitialized data
'' (.bss) section. See external notes for more information.
'-----------------------------------------------------------------

dim shared hHook_MouseProc as HHOOK
dim shared hWnd_mhook as HWND

'============================================================================

'-------------------------------------------------------
'' This is the hook procedure that is called whenever a
'' mouse event occurs. The Microsoft documentation uses
'' "CallWndProc" as a placeholder for this function.
'-------------------------------------------------------

function MouseProc( byval nCode as integer,_
                    byval wParam as WPARAM,_
                    byval lParam as LPARAM ) as LRESULT

    dim target as HWND

    '---------------------------------------------------------
    '' See the Microsoft documentation for the nCode details.
    '---------------------------------------------------------

    if nCode >= 0 then

        '-----------------------------------------------
        '' Get the handle for the Window that the mouse
        '' cursor is currently over and pass it in a
        '' message to the mhook.exe dialog window.
        '-----------------------------------------------

        target = WindowFromPoint( cast( LPMOUSEHOOKSTRUCT, lParam )->pt )
        PostMessage( hWnd_mhook, WM_MOUSEHOOK, cast( LPARAM, target ), 0 )

    end if

    '-------------------------------------------------------
    '' Pass the hook information to the next hook procedure
    '' in the current hook chain, and then return whatever
    '' the CallNextHookEx function returns.
    '-------------------------------------------------------

    return CallNextHookEx( hHook_MouseProc, nCode, wParam, lParam )

end function

'============================================================================

'-------------------------------------------------------------
'' To set a global hook, the DLL module handle must be passed
'' to the SetWindowsHookEx function. The DLL module handle is
'' normally passed to the DLLMain function in the hinstDLL
'' parameter. Starting with FBC 0.14 the compiler creates a
'' DllMain that is inaccessible, so we must get the module
'' handle when we install the hook.
'-------------------------------------------------------------

function InstallHook( byval hWnd_caller as HWND ) as HHOOK export

    '----------------------------------------------------
    '' Save the handle to the mhook.exe dialog window so
    '' the hook procedure can send a message to it.
    '----------------------------------------------------

    hWnd_mhook = hWnd_caller

    '-----------------------------------------------
    '' Save the handle to the hook procedure so the
    '' hook can be uninstalled.
    '-----------------------------------------------

    hHook_MouseProc = SetWindowsHookEx( WH_MOUSE,_
                  @MouseProc, GetModuleHandle( "mhookdll.dll" ), null )

    return hHook_MouseProc

end function

'============================================================================

sub UninstallHook export

    UnhookWindowsHookEx( hHook_MouseProc )

end sub

'============================================================================

patchit.bat:

Code: Select all

:
: See patchit.txt for information.
:
debug < patchit.txt
pause

patchit.txt:

Code: Select all

N This is a DEBUG script that will set the IMAGE_SCN_MEM_SHARED
N attribute for the .bss section of mhookdll.dll.
N
N *** The byte being changed should have an initial value of C0h ***
N
N mhookdll.dll
L 100
E 317
D1
W
Q

Edit:

Updated mhook.bas and mhookdll.bas to work with FBC Version 0.21.0 (03-09-2009) for win32 (target:win32) and Version 0.20.0 (08-10-2008) for win32 (target:win32), and cleaned up the code.

Updated patchit.txt to work with the DLL created with these same FBC versions. Note that before the DLL is patched it will work only for a local hook.

I still do not know how to use LD (or some other tool from binutils) to set the IMAGE_SCN_MEM_SHARED attribute for the .bss section of mhookdll.dll.
Last edited by MichaelW on Jul 29, 2009 6:31, edited 7 times in total.
VirusScanner
Posts: 775
Joined: Jul 01, 2005 18:45

Postby VirusScanner » Jun 13, 2006 3:40

I've done keyboard hooks in the app itself before even though it's supposed to only work in a DLL, mouse hooks should work the same way. If I can find an example I'll post it, but for some reason I don't think I saved any of that.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Postby MichaelW » Jun 13, 2006 11:03

For local hooks the hook procedure can be local. For remote hooks, the hook procedure must be mapped into the address space of other processes, and this requires that it be located in a DLL.
bobfresh
Posts: 27
Joined: Sep 18, 2009 13:50

Postby bobfresh » May 29, 2010 18:44

I know I'm bumping quite an old topic but this was the only windows-api hooking example I found anywhere so props to micheal for getting it working.

I was wondering if an answer had been found to defining a "shared" memory section in the dll. Is it possible to set the section attributes using the linker and #PRAGMA(or w/e).

Im porting a little app i wrote some time ago in masm that lets you assign actions to l/m/r clicking windows titlebars.. Ill be happy to release the source in a day or two.

Im currently using a dirty little patcher hacked together from a file compare between a Patched and an original.....Id like to post without using external pe patcher apps if possible.

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 2 guests