Friday, September 24, 2010

Function Hooking in PureBasic

This is one of the topic that most of you will found it interesting to read. This time, I shared one of my latest achievement while using PureBasic. The Function Hooking method. Some of you might also called it as "API Hooking method". It can hook many of simple Windows API procedure for example:
  • Winsock Function.
    This is the most fun function that people is trying to hook all the time.
  • MessageBoxA.
    Perhaps you wanna try the code?
  • Many more others that have the same function header like the functions above.
Currently, my method only supports Inline Hooking but who knows I might added some more features soon.

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... I re-post this here so that much more people can look into this code and study it, use it or do anything with it (Improving it perhaps?).

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 :
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:
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] : Added InlineUnhook since I needed it inside my project.
[UPDATE] : Instead of using Poke 3 Times and Peek 3 Times, I replaced it with CopyMemory()

I know this isn't fully complete yet.

No comments:

Post a Comment