Extended Inline API Hooking

Share your advanced PureBasic knowledge/code with the community.
PyroStrex
User
User
Posts: 61
Joined: Mon Mar 22, 2010 3:08 pm

Extended Inline API Hooking

Post by PyroStrex »

I though i should share it for future benefit and also for my future reference. Just a simple inline API hooking with the ability to call the old function back. It took me 1 week to fully complete this :oops: . So here goes...

My inspiration : http://help.madshi.net/ApiHookingMethods.htm

Please do not use it for creating malware or anything like it. Use it to help others is fully recommended.

Tested with PureBasic 4.50

The Hook Function :

Code: Select all

Procedure.i InlineHook(LibraryName$, LibraryFunction$, NewFunction.l, OldFunction.l)
    DLLHandle.l = LoadLibrary_(LibraryName$)
    
    OldAddr.l = GetProcAddress_(DLLHandle, LibraryFunction$)
    NewAddr.l = NewFunction
    RetAddr.l = OldAddr + 5
    
    processHandle.l = OpenProcess_(#PROCESS_ALL_ACCESS, #False, GetCurrentProcessId_())
    codeAddress.l = VirtualAllocEx_(processHandle, NULL, 9, #MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
    
    If codeAddress = 0
        Debug "Failed to allocate memory address"
        ProcedureReturn #False
    EndIf
    
    PokeW(codeAddress, PeekW(OldAddr))
    PokeB(codeAddress+2, PeekB(OldAddr+2))
    PokeW(codeAddress+3, PeekW(OldAddr+3))
    PokeB(codeAddress+5, $E9)
    PokeI(codeAddress+6, RetAddr - (codeAddress + 10))
    
    VirtualProtect_(OldAddr, $05, #PAGE_EXECUTE_READWRITE, @OldProtection)
    
    PokeB(OldAddr, $E9)
    PokeI(OldAddr+1, ((NewAddr - OldAddr) - 5))

    PokeL(OldFunction, codeAddress)

    CloseHandle_(processHandle)
EndProcedure

Procedure.i InlineUnhook(LibraryName$, LibraryFunction$, NewFunction.l, OldFunction.l)
    DLLHandle.l = LoadLibrary_(LibraryName$)
    
    OldAddr.l = GetProcAddress_(DLLHandle, LibraryFunction$)
    codeAddress = PeekL(OldFunction)
    
    processHandle.l = OpenProcess_(#PROCESS_ALL_ACCESS, #False, GetCurrentProcessId_())
    
    VirtualProtect_(OldAddr, $05, #PAGE_EXECUTE_READWRITE, @OldProtection)
    
    If codeAddress = 0
        Debug "Failed to get that code Address"
        ProcedureReturn #False
    EndIf
    
    PokeW(OldAddr, PeekW(codeAddress))
    PokeB(OldAddr+2, PeekB(codeAddress+2))
    PokeW(OldAddr+3, PeekW(codeAddress+3))
    
  FillMemory(codeAddress, 9, 0)

  PokeL(OldFunction, NewFunction)
  
  VirtualFreeEx_(processHandle, codeAddress, 0, #MEM_RELEASE) 
  CloseHandle_(processHandle)
EndProcedure
The Working Example :

Code: Select all

Prototype.l proMessageBox(hwnd.l, lpText.s, lpTitle.s, Type.i)

Global oldMessageBox.proMessageBox

Procedure.i InlineHook(LibraryName$, LibraryFunction$, NewFunction.l, OldFunction.l)
    DLLHandle.l = LoadLibrary_(LibraryName$)
    
    OldAddr.l = GetProcAddress_(DLLHandle, LibraryFunction$)
    NewAddr.l = NewFunction
    RetAddr.l = OldAddr + 5
    
    processHandle.l = OpenProcess_(#PROCESS_ALL_ACCESS, #False, GetCurrentProcessId_())
    codeAddress.l = VirtualAllocEx_(processHandle, NULL, 9, #MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
    
    If codeAddress = 0
        Debug "Failed to allocate memory address"
        ProcedureReturn #False
    EndIf
    
    PokeW(codeAddress, PeekW(OldAddr))
    PokeB(codeAddress+2, PeekB(OldAddr+2))
    PokeW(codeAddress+3, PeekW(OldAddr+3))
    PokeB(codeAddress+5, $E9)
    PokeI(codeAddress+6, RetAddr - (codeAddress + 10))
    
    VirtualProtect_(OldAddr, $05, #PAGE_EXECUTE_READWRITE, @OldProtection)
    
    PokeB(OldAddr, $E9)
    PokeI(OldAddr+1, ((NewAddr - OldAddr) - 5))

    PokeL(OldFunction, codeAddress)

    CloseHandle_(processHandle)
EndProcedure

Procedure.i InlineUnhook(LibraryName$, LibraryFunction$, NewFunction.l, OldFunction.l)
    DLLHandle.l = LoadLibrary_(LibraryName$)
    
    OldAddr.l = GetProcAddress_(DLLHandle, LibraryFunction$)
    codeAddress = PeekL(OldFunction)
    
    processHandle.l = OpenProcess_(#PROCESS_ALL_ACCESS, #False, GetCurrentProcessId_())
    
    VirtualProtect_(OldAddr, $05, #PAGE_EXECUTE_READWRITE, @OldProtection)
    
    If codeAddress = 0
        Debug "Failed to get that code Address"
        ProcedureReturn #False
    EndIf
    
    PokeW(OldAddr, PeekW(codeAddress))
    PokeB(OldAddr+2, PeekB(codeAddress+2))
    PokeW(OldAddr+3, PeekW(codeAddress+3))
    
  FillMemory(codeAddress, 9, 0)

  PokeL(OldFunction, NewFunction)
  
  VirtualFreeEx_(processHandle, codeAddress, 0, #MEM_RELEASE) 
  CloseHandle_(processHandle)
EndProcedure

Procedure.l hookedMessageBox(hwnd.l, lpText.s, lpTitle.s, Type.i)
  ProcedureReturn oldMessageBox(hwnd, "This is inside the hooked function!", lpTitle, Type)
EndProcedure

InlineHook("user32.dll", "MessageBoxA", @hookedMessageBox(), @oldMessageBox)

MessageRequester("Extended Inline API Hooking Example", "Testing this Extended Inline API Hooking system")

InlineUnhook("user32.dll", "MessageBoxA", @hookedMessageBox(), @oldMessageBox)

MessageRequester("Extended Inline API Hooking Example", "It is now unhooked!.")
Update Code: Added InlineUnhook since I needed it inside my project.
Update Code: Instead of using Poke 3 Times and Peek 3 Times, I replaced it with CopyMemory()

I know it isn't fully complete yet.
Last edited by PyroStrex on Sun Sep 26, 2010 2:22 am, edited 4 times in total.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: Extended Inline API Hooking

Post by rsts »

Pretty slick.

Thanks for sharing :)

cheers
Thorium
Addict
Addict
Posts: 1271
Joined: Sat Aug 15, 2009 6:59 pm

Re: Extended Inline API Hooking

Post by Thorium »

Is misses a disassembler to verify code integrity.
It works on the standard WinAPI dll's because most functions there start with the same instructions.
But it will lead to crashes if you hook functions in other dll's.
PyroStrex
User
User
Posts: 61
Joined: Mon Mar 22, 2010 3:08 pm

Re: Extended Inline API Hooking

Post by PyroStrex »

@rsta
No problem. Hope you enjoyed it.

@Thorium
Heheh, well i doesn't go that far because all i want is to be able to hook winsock send, recv, WSASend and WSARecv function. With the help of the Disassambler, I think it can easily done. But I don't even know ASM very well. So, yeah~..
Thorium
Addict
Addict
Posts: 1271
Joined: Sat Aug 15, 2009 6:59 pm

Re: Extended Inline API Hooking

Post by Thorium »

PyroStrex wrote: @Thorium
Heheh, well i doesn't go that far because all i want is to be able to hook winsock send, recv, WSASend and WSARecv function. With the help of the Disassambler, I think it can easily done. But I don't even know ASM very well. So, yeah~..
Ok, but you should at least check if the 5 bytes you are overwritting are the instructions you expect to be. You dont need a disassembler for that.

PB once had a working disassembler engine and it was very easy to use. Since x64 there is a new one thats not working very well, especially on the first instruction of WinAPI functions.
PyroStrex
User
User
Posts: 61
Joined: Mon Mar 22, 2010 3:08 pm

Re: Extended Inline API Hooking

Post by PyroStrex »

@Thorium
Yep, that why i really miss the old disassembler engine. Owh, btw, i forgot to thank you Thorium. FYI, this was one of my reference :P http://www.purebasic.fr/english/viewtop ... 92#p327492 . Thank you again.

Edit: Oh, and can you also tell me what other function that cannot be hooked? ASM view would be nice :)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Extended Inline API Hooking

Post by Rescator »

This has most of the same issues as this one http://www.purebasic.fr/english/viewtop ... 12&t=43299 (scroll some way down for my post).
PyroStrex
User
User
Posts: 61
Joined: Mon Mar 22, 2010 3:08 pm

Re: Extended Inline API Hooking

Post by PyroStrex »

@Rescator
As you state in your post,
Same issues as the other hook post.

This only works for the Ascii calls, not the Unicode "W" calls.

Secondly this only works within the current process, starting a second process while the first is running and the second behaves just like normal, no hooking shown.

Thirdly, it's a good thing it doesn't work on other processes etc. Because in the example you forgot to restore the original.
Not only that but the unhook routines, seem odd, for some reason they re-read the proc address instead of simply using the ProcAddr in the array.
And there really should be a unhook all that one could call at the end of the program.


Then again, since this only works in the current process it's just simpler to use a macro anyway. *laughs*

The way hooks are easily done is making a dll, putting that dll in the program folder. Obviously that dll need to replace/pass through ALL functions of the original dll.
The other way is a Loader (or injector).
A third would be a Trainer but not really intended for this kind of hooking.

The proper way to do this is a Debugger hook, and it must be running as admin and it must have write to process privileges etc. (I'm sure some of the folks around here knows how to properly do this stuff?)

But I wouldn't be surprised if your code works as intended on Win 9x though.
1. I've tried this example and it works for both MessageBoxA and MessageBoxW (But i don't know if you actually mean like this. I wish you can give me more information about this)

2. Yep, it's a shame it only works for local hook. I don't actually have interest in doing global hook. Maybe, just maybe, soon i will try to create a global hook system.

3. In my example, to call the old/original function, you don't need to unhook and rehook. Just simply call the old function back which have been extended into another address space (which is why i call this example "Extended Inline API Hooking").

4. In my example, Yes it is an EXE. But you can easily change it to work in a DLL (Since my real application use this function insde a DLL). I also demonstrated the use of Macro just like you said.

Anything i missed or incorrect. Please help me to correct it :). People always make mistakes you know~. Heheh :D
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Extended Inline API Hooking

Post by Rescator »

PyroStrex wrote:1. I've tried this example and it works for both MessageBoxA and MessageBoxW (But i don't know if you actually mean like this. I wish you can give me more information about this)
Actually no, try turning on Unicode and you will see it fails. (Source memory pointer is 0)
PyroStrex wrote:2. Yep, it's a shame it only works for local hook. I don't actually have interest in doing global hook. Maybe, just maybe, soon i will try to create a global hook system.
That's the part I don't get. It's easier just to um use your own code/procedure instead of hooking an API call for your process and calling that.
PyroStrex wrote:3. In my example, to call the old/original function, you don't need to unhook and rehook. Just simply call the old function back which have been extended into another address space (which is why i call this example "Extended Inline API Hooking").
Yeah, that's kinda neat, but you might want to put Local in the title somewhere to avoid confusion, as this isn't an Injector like DarkDragon's code is. (it's in that thread you pointed to somewhere)
PyroStrex wrote:4. In my example, Yes it is an EXE. But you can easily change it to work in a DLL (Since my real application use this function insde a DLL). I also demonstrated the use of Macro just like you said.
I do not see the point in that either as you could just write a dll that does exactly what you want no point in-hooking your own code. You don't even need to hook anything even if you making a "fake" user32.dll and put that in the exe's folders, just wrap anything you don't want to change and code the replacement for the replacements.
Obviously with a DLL you get more complications as "almost" all WIN32 API dll's have both A and W functions.

And obviously this will only work with stdcall not cdecl or fastcall. (on x86 things are simpler surprisingly enough, as x64 uses only fastcall)

Other issues with at least API stuff is that some functions may free or allocate memory, and you obviously can't use AllocateMemory or FreeMemory to handle that but instead use whatever the original function actually does. (could be malloc, could be API or CoAllocate or whatever)

I'm not saying your code looks complicated or anything, it's just that I really do not see the use of it as it is right now.
A local hooking/injector like this seems kinda redundant because either you control (coded) the dll being loaded or you control the exe (coded),
so unless you lost the source to your own program or dll I really do not see much need for local hooking. *laughs*

PS! What exactly "are" you using a local process hooking for anyway? I have no idea what it could be and am really curious...
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Extended Inline API Hooking

Post by Rescator »

I hope I don't think I'm an ass or anything, but the example above could just as easily be done like this:

Code: Select all

Procedure.l replacementMessageBox(hwnd.i,lpText.s,lpTitle.s,Type.l)
	MessageBox_(hwnd,"Inside our replacement",lpTitle,Type)
	ProcedureReturn MessageBox_(hwnd,lpText,lpTitle,Type)
EndProcedure

Macro MessageBox_(hwnd,lpText,lpTitle,Type)
	replacementMessageBox(hwnd,lpText,lpTitle,Type)
EndMacro

MessageBox_(#Null,"Simple macro function replacement","Testing...",#Null)
So you can hopefully understand that I fail to see the exact use of the code (as shown in the example above that is).
DarkDragon
Addict
Addict
Posts: 2218
Joined: Mon Jun 02, 2003 9:16 am
Location: Germany
Contact:

Re: Extended Inline API Hooking

Post by DarkDragon »

Rescator: Your definition of global and local hooks is somewhat different than everyone else's.

Global hooks: you hook all applications running on the system or eh .. the whole system. Just like you've replaced kernel32.dll
Local hooks: you only hook one or a few applications

With this code it is possible to hook other applications. Just replace the process id with another one.
bye,
Daniel
Thorium
Addict
Addict
Posts: 1271
Joined: Sat Aug 15, 2009 6:59 pm

Re: Extended Inline API Hooking

Post by Thorium »

Rescator wrote: So you can hopefully understand that I fail to see the exact use of the code (as shown in the example above that is).
The code is very usefull.
You will likely use it in a dll you inject into another process to alter it's behavior or log actions of the process, like Winsock activity.

The pro is you dont have to place a .dll into the process directory and you can hook after the process is launched. Which is needed very often because of exe protectors.
Plus you can hook procedures not exported or procedures in the processes .exe not only in a .dll. But for that the code have to be enhanced with a disassembler.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Extended Inline API Hooking

Post by Rescator »

Well, that makes more sense, but that's not what the example shows.

DarkDragon's code in that thread is a more complete example of injection (taking Vista and Win7 behavior into account).
And there are at least one Trainer example in the forum as well.

Now if the example here showed how to replace something in another process,
and make sure that it's properly unhooked (so you do not end up crashing the process) then the example would make sense.

As it is now an amateur would either complain it doesn't work, or they use a another process ID, and ending up crashing something if they don't pay attention. And we all know how great the attention span of us coders really are. ;)
Then again an amateur shouldn't try injections or hooking or trainers or process memory manipulation anyway without reading the MSDN docs carefully.
DarkDragon wrote:Global hooks: you hook all applications running on the system or eh .. the whole system. Just like you've replaced kernel32.dll
Local hooks: you only hook one or a few applications
I agree on the Global definition.
I do not agree on the local one in this context.
What you call local, I would call targeted. Though I guess what I call local could also be called internal hooking, which might explain why I really can't seem to find the source in the first post to have a point (other than it working which is cool, nothing worse than broken sources *laughs*) it's functionality is the same as using a macro and procedure.

So PyroStrex you could flesh out the example a little more (notepad is a popular target right?) so that it has some practical use.
Then just update the first post and my ramblings here will just seem weird and people will laugh at me, right? :P

PS! Don't you have to also freeze the target process to avoid a race condition? (although rare it is possible the other process may change the pointers as well, though hopefully the one writing the injector knows the target process well enough for this)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Extended Inline API Hooking

Post by Rescator »

Thorium wrote:
Rescator wrote: So you can hopefully understand that I fail to see the exact use of the code (as shown in the example above that is).
The code is very usefull.
You will likely use it in a dll you inject into another process to alter it's behavior or log actions of the process, like Winsock activity.
Yeah but again the example doesn't show that. DarkDragon's does http://www.purebasic.fr/english/viewtop ... 92#p327492 and he uses ReadProcessMemory and WriteProcessMemory which is the only way to do it as that translates the memory addresses between your process and the target process.
The original example in this thread as it is now, if you just simply choose a different process id will most likely fail with a memory error read/write error since you are just referencing your own process' memory range.
Thorium
Addict
Addict
Posts: 1271
Joined: Sat Aug 15, 2009 6:59 pm

Re: Extended Inline API Hooking

Post by Thorium »

Rescator wrote:
Thorium wrote:
Rescator wrote: So you can hopefully understand that I fail to see the exact use of the code (as shown in the example above that is).
The code is very usefull.
You will likely use it in a dll you inject into another process to alter it's behavior or log actions of the process, like Winsock activity.
Yeah but again the example doesn't show that. DarkDragon's does http://www.purebasic.fr/english/viewtop ... 92#p327492 and he uses ReadProcessMemory and WriteProcessMemory which is the only way to do it as that translates the memory addresses between your process and the target process.
The original example in this thread as it is now, if you just simply choose a different process id will most likely fail with a memory error read/write error since you are just referencing your own process' memory range.
The example is fine. It just shows the functionality of the code.
As i said the code will be in a dll that will be injected into the target process. So the code runs in the context of the target process. No WriteProcessMemory is needed. You have to inject code into the target process anyway, because execution cant jump to another process address space.

Code injection and inline hooking going hand in hand. There is no inline hook without code injection.
Post Reply