I created a bunch of macros to make it easy to hook API.
They actually use procedures internally to avoid codebloat since they will be called numerous times perhaps.
Please post any comments.
- Jag
http://modseven.de/pastebin.php?id=1252
My website is down so I put them on pastebin:
http://modseven.de/pastebin.php?id=1252
Great job :U the code looks good however the program crashes when I try one of your examples, I attached the test files, i'll debug in abit but thanks for sharing none the less.
[attachment deleted by admin]
I apologize. When I modified the macros I made a mistake (forgot OFFSET.)
Please redownload the file - I have fixed the problem.
The reason why a crash is happening if not because of the mistake I made is due to your executables write permissions.
When not using the procedural-style hooking, you must make your code section writable.
From the include file:
your .text section needs to be writable if using the non-procedural hooking
if using radasm, add /SECTION:.text|RWE the LINK box under Project -> Project Options]
otherwise, just add /SECTION:.text,RWE to linking arguments
Hope this helps and sorry about the problem in the previous include file.
Below, attached are two radasm projects - one for procedural hooking and the other for non-procedural hooking.
[attachment deleted by admin]
I fire up the jagHookTest.exe and it crash on my XP Pro no service pack .. 498 MHz Intel Celeron. I did not look at the code yet to see what i should have done, i only tried to run the test.exe
Aye, that is wierd. Both of the exes work fine for me.
I have service pack 2 on xp media center edition.
That's the very reason write i write nearly any project using Win 95 to start it off. If work on 95 than XP it should work for everything in-between. Not sure about 2000. I caught hell trying to keep up switching and testing back and forth to 95 - XP but it was worth it. You be surprised when something should work for 95 from a XP project but DON'T... and you know you have done everything 100% correctly.
It because XP will allow code to run but it has to be modified to work on 9x. (Usually something silly)
XP has been a big trip for me but thanks to 95 it can't fool me anymore. Also what works on one XP might not work under another XP of the same Pack. I think some kind of forgiving XP force code to allow your program to run on that machine. You got all kinds of doctors watson or whatever that fix your code to run on that machine but will not run on another machine with-out modifications. I experience this for years but never got tried to get to the bottom of it. I just rely on my win95 when building what XP still allow than go from there and it never fails.
I had the same type of problem with 95 back in the day to where a working program stop working all because i added one line of code to it that should have work... example: I had to copy all of those files to another folder... delete the old folder... re-boot the machine... re-name the new folder to the old name and if there is not a God in Heaven "IT always WORKED".
Bottom line... the OS will fool you... I can promise you that. Every coder may never experence this but if you do new things that the OS api has questions about it simply block the code from running. Crazy but TRUE... Just want to make sure everyone remember this if you step into some (since able) clever coding that the OS "IS" capable of fooling you. I guest that's the life of an OS writer, thinking that something is trying to slip in when it actually not....
Anyway...
Of-course in this case it 9 out of 10 a users would have to have servicepack2 in order to run it. In my opinion no asm program should end up with these dependency unless designed to do so. General purpose coding is the way to go. Hopefully others will test on many OS and let you know what happened. Your work is very interesting.
I tried to run the jagHookTest-NONPROCEDURAL.exe and it also did not run. I even put it on a non-INTERNET straight super clean XP Pro (no-service pack) machine and it still fail. ( clean meaning with absolutely no M$ programs or other new tech products installed ) Example: Nothing higher than Adobe 5.0 or WP 8 for the OS to start a conflict with.
actually 'if it works on 9x it should work on xp' is a bit wrong.. as for the dont know about 2000, if it works on xp it probably will on 2000...
there are some things you can do in 9x in code, that you cant in 2k or higher (nt base), privileged commands and so on, along with tighter memory access rights.. try reading the bios are in 9x, then run the same code in xp..
call enddialog without 1 param, in 9x = gui frozen, do it in 2k or higher.. fine
In win95/98 you can play with ebx/esi/edi on callbacks, but on winnt your app will crash. ".shared" segments are forbidden in w2k/xp.
Toying with esp (without changing bounds via fs: ) will kill your app without a crash-message on w2k/xp.
It isn't nice to crash your whole OS (95/98) or freeze the gui when there's a bug :)
Ultrano, I always thought MS was really for backward compatibility :P
I don't see why my hooks would have any problems with win 95. All they do is read and write.
Aye, well it sucks that I don't have a copy of Win 95 so I may find the problem myself. Is it possible one of you could debug and find out where the exception is occurring? I would really appreciate it.
I was only specking of standard (portable) coding with 95 that would work with XP also. Either way which ever one you start building with it's best to switch before you get to deep in the code to check if it still work or not if you can(and you must some how unless you a **darn** good coder).
I choose 95 over 98 or Me to match up with XP until the program is force to use more api that 95 don't support. All of this is still great info. Now i see why i caught hell trying to make 9x code work on xp, Never did understand why until now... many years latter ...
jag
I will try but i really don't have Debugging experience. There are a few new things I am trying to implement into my program simply because of my excitement over these new ideas and your Hook is one of them. That's is how I learn to use, fix or improve code. Whatever i bump into you will hear from me about it. But for now im sure someone can get to the bottom of the immediate problem.
What the heck, now is as good as latter. I'll be playing with it all day and night to see what i can see.
Alright - sounds good! I recommend you use ollydbg and just step thru the code with f7. Once you get an exception mark down the location it occurs - surrounding assembly instructions and such. Then I can see what code is causing the problem and perhaps fix it. Thanks.
jagHookTest-NONPROCEDURAL:
It's kind of strange that if i EXECUTE TILL RETURN with OLLY the programs shows your 3 message box. After turning the last one off OLLY jump to retn and int3 code is the next line under it.
I bet if you remove int3 from the program it might work for 9x. This is my first guest ...
............................................................
............................................................
............................................................
When i ANIMATE OVER first stop is:
At this reading:
$-FF25 14204000
message1 pop-up
jmp near dword ptr ds: [<&user32.Messagebox]
I close
message2 pop-up
jmp near dword ptr ds: [<&user32.Messagebox]
I close it
message3 pop-up
jmp near dword ptr ds: [<&user32.Messagebox]
I close it
Proess terminated, exit code (259) at
call far fword ptr cs: [BFFC9734]
i touch something and it jumped to:
CMP EAX, 0C0
This is surround by
push dword ptr ss: [esp+10]
push dword ptr ss: [esp+10]
push 2A005C
call KERNEL32.#8
Cmp eax, 0C0 ........................#####
mov esi, eax
jnz short KERNEL32.BFF99B40
call KERNEL32.BFF8502C
mov eax, esi
pop esi
retn 0C
............................................................
............................................................
When i ANIMATE INTO .... on the 59 step it fire up woooot my text!!!
It return all of the above.... this time including the Windows Error Message.
............................................................
............................................................
When I try to run it straight-up from disk like clicking a regular program it return the Window Error Message...
JAGHOOKTEST caused an invalid page fault in
module JAGHOOKTEST.EXE at 0167:0040102a.
Registers:
EAX=0063fdec CS=0167 EIP=0040102a EFLGS=00010217
EBX=407fd074 SS=016f ESP=0063fe18 EBP=0063fe24
ECX=81635440 DS=016f ESI=00000005 FS=2c4f
EDX=bffc9490 ES=016f EDI=bfc0412e GS=0000
Bytes at CS:EIP:
c6 07 e9 89 5f 01 8b 5d 0c 8d 45 08 50 ff 75 08
Stack dump:
8163524c 00000000 00530000 0063ff78 004011e0 004011a7 004011b9 bfc0412e 00000005 bff8b560 00000000 8163524c 00530000 6867614a 746b6f6f 00747365
If this is all that is needed latter on tell me what parts were really important. If what you need is not here. Tell me what i need to do. I don't think i cross anything up if so i will be trying again.
I wonder why OLLY don't have print or allow the user to set speed of step through. I would love to slow it way down and why don't it have a way to ring a bell or something to notify you of something you set reach that point. These are my main reasons for not having interest in debuggers. I never see a way to do these things but if i could print results that would be a good start.
Based on the error information you sent me, the problem is occuring in pInstallHook at this line:
264: mov BYTE PTR [edi], 0E9h
I still don't see why that would cause the problem though. If edi is first virtualprotected, shouldn't that line be fine to execute?
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
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.
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!!!
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.
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
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.
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
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.