News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Function Hook Macros/Procedures

Started by jag, January 23, 2007, 05:34:58 AM

Previous topic - Next topic

ic2

Run this file:

I place most of your code under one file so i can veiw it better with-out the comments.  I put the fn macro in so I can see what is returning in eax after a few main api calls... I get nothing... I actually only came up with the same point of the problem as you did.

I did not change your call but i added a few standard PUSH routine that did nothing better at all.

I notice invoke VirtualProtect, ebx, 5, pDetour, ADDR pDetour ... pDetour is a DWORD and appear to be addressed wrong but even PUSHING pDetour did not work so im not sure about that one.   Anyway, I had a ball up until now.   I'm totally stumped.  I don't use macros and know nearly nothing about them. If i can get to the data I can load VirtualProtect since this seems to be where the problem is.  I also wonder about the use of GetModuleHandle.

Do your file have to use macros in order to get the job done ? ? ?
Can you easily replace those macro with standard code ? ? ?  Will you replace as many as possible.  You can always test latter to see which way is really the fastest. You know what my vote will be.

invoke VirtualProtect, edi, 5, PAGE_EXECUTE_READWRITE, ADDR targetProc return nothing and it is the first major call made.

I hope i did not make any major mistakes resorting your file.  In the mean time i better hit the hay for a quick power nap.


.486
.model flat, stdcall
option casemap: none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
include \masm32\include\user32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib


;;;; include jagHook.asm


ProcInstallHook    PROTO :DWORD,:DWORD,:DWORD,:DWORD
ProcUninstallHook  PROTO :DWORD,:DWORD
ProcReinstallHook  PROTO :DWORD,:DWORD,:DWORD
pUninstallHook     PROTO :DWORD,:DWORD,:DWORD



    fn MACRO FuncName:REQ,args:VARARG
      arg equ <invoke FuncName>         ;; construct invoke and function name
      FOR var,<args>                    ;; loop through all arguments
        arg CATSTR arg,<,reparg(var)>   ;; replace quotes and append arg
      ENDM
      arg                               ;; write the invoke macro
    ENDM


    str$ MACRO DDvalue
      LOCAL rvstring
      .data
        rvstring db 20 dup (0)
        align 4
      .code

PUSH  offset rvstring
PUSH  DDvalue
CALL  dwtoa

      EXITM <ADDR rvstring>
    ENDM


InstallHook macro hookName:REQ, targetProc:REQ
invoke pInstallHook, OFFSET jagHookBegin_&hookName, jagHookEnd_&hookName, targetProc, SIZEOF jagHookBegin_&hookName
endm

ReinstallHook macro hookName:REQ
invoke pReinstallHook, OFFSET jagHookBegin_&hookName, jagHookEnd_&hookName, SIZEOF jagHookBegin_&hookName
endm

UninstallHook macro hookName:REQ
invoke pUninstallHook, OFFSET jagHookBegin_&hookName, jagHookEnd_&hookName, SIZEOF jagHookBegin_&hookName
endm

BeginHook macro hookName:REQ, patchLen:=<5>, doPushPop
jagHookName TEXTEQU <hookName>
jagHookDoPushPop TEXTEQU <doPushPop>
jagHookBegin_&hookName db patchLen dup (?)
if doPushPop
push ebx
push edi
push esi
push ebp
endif
endm

EndHook macro
if jagHookDoPushPop
pop ebp
pop esi
pop edi
pop ebx
endif
db 0E9h
dd ?
% jagHookEnd_&jagHookName:
endm

StubLen macro patchLen:REQ
exitm <patchLen + 5>
endm

.code







.data
module db "user32.dll", 0
procname db "MessageBoxA", 0
normaltext db "woooot my text!!!", 0
newtext db "Stole your messagebox!", 0


Problem_1 db "1: VirtualProtect returns NOTHING !!!", 0
Problem_2 db "2: RtlMoveMemory can't be true ...", 0
Problem_3 db "3: 0E9h ; <<<<< YOU WERE RIGHT.", 0

.data?
pMBoxDetour db StubLen(5) dup (?)


.code
; 5 bytes need to be replaced for MessageBoxA
; 77D804EA > 8BFF             MOV EDI,EDI
; 77D804EC   55               PUSH EBP
; 77D804ED   8BEC             MOV EBP,ESP

MyMsgBoxHook proc hWnd:HWND, lpText:LPCTSTR, lpCaption:LPCTSTR, uType:UINT
invoke pr4 PTR pMBoxDetour, hWnd, OFFSET newtext, lpCaption, uType
ret
MyMsgBoxHook endp

start:
; get address of MessageBoxA
invoke GetModuleHandle, OFFSET module
invoke GetProcAddress, eax, OFFSET procname
; do some hook demonstrations

invoke ProcInstallHook, MyMsgBoxHook, eax, OFFSET pMBoxDetour, 5
invoke MessageBox, NULL, OFFSET normaltext, NULL, MB_OK ; we should see modified title
invoke ProcUninstallHook, OFFSET pMBoxDetour, 5
invoke MessageBox, NULL, OFFSET normaltext, NULL, MB_OK ; we should see normal title
invoke ProcReinstallHook, MyMsgBoxHook, OFFSET pMBoxDetour, 5
invoke MessageBox, NULL, OFFSET normaltext, NULL, MB_OK ; we should see modified title
ret








;  ########################################################
;  ########################################################

pInstallHook proc uses ebx edi esi hookBegin:DWORD, hookEnd:DWORD, targetProc:DWORD, patchLen:DWORD

mov edi, targetProc
mov esi, patchLen
mov ebx, hookBegin

; make the target writable
invoke VirtualProtect, edi, 5, PAGE_EXECUTE_READWRITE, ADDR hookBegin

; copy the to-be-emulated bytes from targetProc to hook's beginning
invoke RtlMoveMemory, ebx, edi, esi

; replace the bytes with a jmp to the hook
sub ebx, 5
sub ebx, edi
mov BYTE PTR [edi], 0E9h
mov [edi + 1], ebx

; put a jmp to the targetProc at the end of hook
mov ebx, hookEnd
; set protections back to old ones to avoid complications
invoke VirtualProtect, edi, 5, hookBegin, ADDR hookBegin
sub edi, ebx
add edi, esi
mov [ebx - 4], edi

ret

pInstallHook endp

;  ########################################################
;  ########################################################

pReinstallHook proc uses ebx hookBegin:DWORD, hookEnd:DWORD, patchLen:DWORD

; get address of targetProc
mov eax, hookEnd
mov ebx, [eax - 4]
add ebx, eax
sub ebx, patchLen

; make the target writable
invoke VirtualProtect, ebx, 5, PAGE_EXECUTE_READWRITE, ADDR hookEnd

; replace the starting bytes with a jmp to the hook
mov eax, hookBegin
sub eax, 5
sub eax, ebx
mov BYTE PTR [ebx], 0E9h
mov [ebx + 1], eax

; set protections back to old ones to avoid complications
invoke VirtualProtect, ebx, 5, hookEnd, ADDR hookEnd

ret

pReinstallHook endp

;  ########################################################
;  ########################################################

pUninstallHook proc uses ebx hookBegin:DWORD, hookEnd:DWORD, patchLen:DWORD

; get address of targetProc
mov eax, hookEnd
mov ebx, [eax - 4]
add ebx, eax
sub ebx, patchLen

; make the target writable
invoke VirtualProtect, ebx, 5, PAGE_EXECUTE_READWRITE, ADDR hookEnd

; copy the emulated bytes from hook back to targetProc, replacing the jmp
mov eax, hookBegin
mov ecx, [eax]
mov dl, BYTE PTR [eax + 4]
mov [ebx], ecx
mov BYTE PTR [ebx + 4], dl

; set protections back to old ones to avoid complications
invoke VirtualProtect, ebx, 5, hookEnd, ADDR hookEnd

ret

pUninstallHook endp

;  ########################################################
;  ########################################################
invoke ProcInstallHook, MyMsgBoxHook, eax, OFFSET pMBoxDetour, 5

ProcInstallHook proc uses ebx edi esi hookProc:DWORD,
                                    targetProc:DWORD,
                                    pDetour:DWORD,
                                    patchLen:DWORD

mov edi, targetProc
mov esi, patchLen
mov ebx, pDetour
;                              PROBLEM ; was ADDR targetProc
;PUSH  targetProc             ; make the target writable
;PUSH  PAGE_EXECUTE_READWRITE
;PUSH  5
;PUSH  edi
;CALL  VirtualProtect



invoke VirtualProtect, edi, 5, PAGE_EXECUTE_READWRITE, targetProc

invoke MessageBox, 0, str$ (eax),  addr Problem_1, MB_OK

;                       copy the to-be-emulated bytes from
;                       targetProc to detour's beginning

PUSH esi
PUSH edi
PUSH ebx
CALL RtlMoveMemory

invoke MessageBox, 0, str$ (eax),  addr Problem_2, MB_OK

mov eax, hookProc          ; replace the bytes with
sub eax, edi               ; a jmp to the hook
sub eax, 5

invoke MessageBox, 0, str$ (VirtualProtect),  addr Problem_3, MB_OK


mov BYTE PTR [edi], 0E9h ; <<<<< YOU WERE RIGHT. IT ALL STOPS HERE

mov [edi + 1], eax
;                                  put a jmp to the targetProc
;                                  at the end of detour


add esi, ebx



;                              PROBLEM ; was ADDR targetProc
;PUSH  offset targetProc             ; set protections back to
;PUSH  targetProc             ; old ones to avoid complications
;PUSH  5
;PUSH  edi
;CALL  VirtualProtect
invoke VirtualProtect, edi, 5, targetProc, ADDR targetProc


sub edi, ebx
sub edi, 5
mov BYTE PTR [esi], 0E9h
mov [esi + 1], edi

ret

ProcInstallHook endp

;  ########################################################
;  ########################################################

ProcReinstallHook proc uses ebx hookProc:DWORD, pDetour:DWORD, patchLen:DWORD
; get address of targetProc
mov eax, pDetour
mov ecx, patchLen
mov ebx, [eax + ecx + 1]
add ebx, eax
add ebx, 5

; make the target writable
invoke VirtualProtect, ebx, 5, PAGE_EXECUTE_READWRITE, ADDR pDetour

; replace the starting bytes with a jmp to the hook
mov eax, hookProc
sub eax, 5
sub eax, ebx
mov BYTE PTR [ebx], 0E9h
mov [ebx + 1], eax

; set protections back to old ones to avoid complications
invoke VirtualProtect, ebx, 5, pDetour, ADDR pDetour

ret

ProcReinstallHook endp

;  ########################################################
;  ########################################################

ProcUninstallHook proc uses ebx edi pDetour:DWORD, patchLen:DWORD

; get address of targetProc
mov ebx, pDetour
mov eax, patchLen
mov edi, [ebx + eax + 1]
add edi, ebx
add edi, 5

; make the target writable
invoke VirtualProtect, edi, 5, PAGE_EXECUTE_READWRITE, ADDR pDetour

; copy the emulated bytes from hook back to targetProc, replacing the jmp
mov eax, [ebx]
mov cl, BYTE PTR [ebx + 4]
mov [edi], eax
mov BYTE PTR [edi + 4], cl

; set protections back to old ones to avoid complications
invoke VirtualProtect, edi, 5, pDetour, ADDR pDetour

ret

ProcUninstallHook endp


; ###############################################
; ###############################################


  ; This procedure was written by Tim Roberts
  ; Minor fix by Jibz, December 2004

align 4

dwtoa proc dwValue:DWORD, lpBuffer:DWORD

    ; -------------------------------------------------------------
    ; convert DWORD to ascii string
    ; dwValue is value to be converted
    ; lpBuffer is the address of the receiving buffer
    ; EXAMPLE:
    ; invoke dwtoa,edx,ADDR buffer
    ;
    ; Uses: eax, ecx, edx.
    ; -------------------------------------------------------------

    push ebx
    push esi
    push edi

    mov eax, dwValue
    mov edi, [lpBuffer]

    test eax,eax
    jnz sign

  zero:
    mov word ptr [edi],30h
    jmp dtaexit

  sign:
    jns pos
    mov byte ptr [edi],'-'
    neg eax
    add edi, 1

  pos:
    mov ecx, 3435973837
    mov esi, edi

    .while (eax > 0)
      mov ebx,eax
      mul ecx
      shr edx, 3
      mov eax,edx
      lea edx,[edx*4+edx]
      add edx,edx
      sub ebx,edx
      add bl,'0'
      mov [edi],bl
      add edi, 1
    .endw

    mov byte ptr [edi], 0       ; terminate the string

    ; We now have all the digits, but in reverse order.

    .while (esi < edi)
      sub edi, 1
      mov al, [esi]
      mov ah, [edi]
      mov [edi], al
      mov [esi], ah
      add esi, 1
    .endw

  dtaexit:

    pop edi
    pop esi
    pop ebx


    ret

dwtoa endp

; ###############################################
; ###############################################

end start


jag

The macros are used to make you type less. For example, when you declare a hook you give it a name. Using that name you can install the hook, reinstall it, remove it etc.
If you look at the actual parameters the macros pass to the procedures that do the work, they are derived from the parameters originally specified when creating a hook with BeginHook and EndHook.
The parameters are the address of the beginning of the hook, end of the hook, and the length of the patch.

As for the procedural hooking, macros aren't used anyways, other than StubLen which just returns the amount of bytes needed for a stub.



***** I am going to get a copy of windows 95 from a friend of mine later today and use vmware so I can get to the bottom of this. Stay tuned.

ic2

Right-On jag

You work is very interesting and you comments are great, but something is standing in the way of such a simple beginning.  I'M HOOKED on it NOW.  I'm going to still try to take apart the BeginHook macro an see if I can figure out what the hold-up is all about.  It make no since for this not to  work on other machines and i use three.

I am not a macro person but I like the debugger.  Now i see all you have to do is dissemble the executable and tracked the results founded with a debugger.  So that's how things are done.  It don't stop NOW!!!

jag

Aight well I figured out some stuff. In Windows 95, when using a debugger, no direct addresses are filled into the iat or when getprocessaddress is called -- instead debug thunks (push xxxx jmp kernel32.xxxxxxxx) are placed.
I got around that by some pre-runtime modifications to the iat and getprocesaddress call.

I then saw the problem. You were right. VirtualProtect returns 0.
The reason why it returns 0 on 95 is this (from MSDN page on VirtualProtect):

Windows Me/98/95:  You cannot use VirtualProtect on any memory region located in the shared virtual address space (from 0x80000000 through 0xBFFFFFFF).

I don't think there's a way around this in 95' as multiple copies of the system dlls aren't loaded -- there is just 1 which writing to would screw up all other programs currently running that use that dll in the first place.

Lesson Learned: IAT hooking is what's necessary on 95 :P

I'll add IAT hooking within the next month to jagHook.

ecube

Alright I noticed you were using virtualprotect incorrectly take note of the code below, I forget who wrote this but hes a member on this forum, but anyway should fix that issue, if they're others sure they'll be small fixes. I wish I could take a moment to test everything and really look at your code but have been so busy, but in any case thanks again for posting it.


HookProc proc afunc:PVOID, aNewFunc:PVOID
LOCAL oldprotect:ULONG
LOCAL newoldprotect:ULONG
.if afunc==NULL
jmp fail
.endif
.if aNewFunc==NULL
jmp fail
.endif
invoke VirtualProtect, afunc,5,PAGE_EXECUTE_READWRITE, addr oldprotect ;Unprotect Memory
.if eax==NULL
jmp fail   
.endif
mov eax, afunc ;Move Address to eax of original Function
mov byte ptr [eax], 0E9h ;Add the JMP Short
mov ebx, aNewFunc ;Mov ebx the addy of new Function
add eax, 5 ;next intruction to calculate offset
sub ebx, eax ;Subtract FiveBytesAfterFunction - AddressofnewFunction
sub eax, 4 ;to write offset
mov dword ptr [eax], ebx
invoke VirtualProtect, afunc,5,oldprotect, addr newoldprotect ;Reprotect Memory
.if eax==NULL
jmp fail   
.endif
mov eax,1
ret
fail:
mov eax,0
ret
HookProc endp

jag

I don't believe I am doing anything wrong with VirtualProtect aside from no error-checking.
I use one of the arguments to the hook procedures as lpflOldProtect in order to avoid creating a new local variable if that's what you are wondering.

ecube

Quote from: jag on February 18, 2007, 05:13:05 AM
I don't believe I am doing anything wrong with VirtualProtect aside from no error-checking.
I use one of the arguments to the hook procedures as lpflOldProtect in order to avoid creating a new local variable if that's what you are wondering.

Well you having the lpAddress and lpflOldProtect variable pointing to the same thing makes virtualprotect fail on my windows xp sp2, however when you use a new variable it works fine. I am going to debug your code today and will have a fixed version uploaded as your code is quite useful. Thanks

jag

I always use a register as lpAddress though. The register was filled with the value at the beginning of the function  -- so messing with the local variables won't modify the register.

Can you post an example of what you see as the mistake I made? I am not seeing it.