API Hook Engine Module (Windows)

Share your advanced PureBasic knowledge/code with the community.
Peyman
Enthusiast
Enthusiast
Posts: 203
Joined: Mon Dec 24, 2007 4:15 pm
Location: Iran

API Hook Engine Module (Windows)

Post by Peyman »

hi, i create this Hook Engine for educational purposes and use it in some of my projects so now share it maybe come in handy for someone.

i try to optimize it to work on X64 & X86 Windows apps and also with Unicode switch on.

Version 1.0
  • Initial
Update 1.1
  • Added InjectDLL for inject a DLL to another process (Injector must be same as Target process both must be X86 or X64)
Update 1.2
  • Added EjectDLL method for unload a DLL from target process
  • Added CallRemoteFunction method to call a proc from a DLL in target process (just one parameter can be passed to Remote Function)
  • Improved Injector, Now X64 Injector can inject in x64 & x86 target but x86 injector just can inject to x86 target.
API_HookEngine.pbi

Code: Select all

; ====================================================================================================
; Title:        API_HookEngine Module
; Description:  With this module you can hook procedures and api in windows
; Author:       Peyman
; Version:      1.0 (02 FEB 2016) initial version
;               1.1 (07 FEB 2016) added Inject DLL
;               1.2 (11 FEB 2016) improved injector, added Eject DLL & CallRemoteFunction with parrameter
; Platform:     Windows (X64 And X86) Unicode And Ansi
; License:      Free But Any improvements to be shared with the community.
; ====================================================================================================

DeclareModule API_HookEngine
  Declare.i Hook(*OldFunctionAddress, *NewFunctionAddress)
  Declare.i UnHook(*hook_ptr)
  Declare.i ProcAddress(ModuleName$, ProcName$)
  Declare.i InjectDLL(PID, DLL$)
  Declare.b EjectDLL(PID, DLL$, *_Module = #Null)
  Declare.i CallRemoteFunction(PID, *Func, Proc2Call$, WaitForReturn.b = #False, *retValue = #Null, *Parameter = #Null, nSize = #Null)
EndDeclareModule


Module API_HookEngine  
  EnableExplicit
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    #INJECTOR_IS_64 = #True
  CompilerElse
    #INJECTOR_IS_64 = #False
  CompilerEndIf
  
  
  Structure opcode
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      mov.u
    CompilerElse
      mov.a
    CompilerEndIf
    addr.i
    push.a
    ret.a
  EndStructure
  
  
  Structure hookstruct
    addr.i
    hook.opcode
    orig.a[SizeOf(opcode)]
  EndStructure
  
  
  CompilerIf #PB_Compiler_Unicode
    Import "kernel32.lib"
      GetProcAddress(hModule, lpProcName.p-ascii)
    EndImport
  CompilerElse
    Import "kernel32.lib"
      GetProcAddress(hModule, lpProcName.s)
    EndImport
  CompilerEndIf
  

  Import ""
    GetNativeSystemInfo(*info)
  EndImport
  
  
  Procedure.i ProcAddress(ModuleName$, ProcName$)
    Protected moduleH.i
    
    moduleH = GetModuleHandle_(ModuleName$)
    If moduleH = #Null
      moduleH = LoadLibrary_(ModuleName$)
      If moduleH = #Null
        ProcedureReturn #Null
      EndIf
    EndIf
    
    ProcedureReturn GetProcAddress(moduleH, ProcName$)
  EndProcedure


  Procedure Hook(*OldFunctionAddress, *NewFunctionAddress)
    Protected *hook_ptr.hookstruct
    
    If Not *OldFunctionAddress
      ProcedureReturn #Null
    EndIf
    
    *hook_ptr = AllocateMemory(SizeOf(hookstruct))
    *hook_ptr\addr = *OldFunctionAddress
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      *hook_ptr\hook\mov = $B848
    CompilerElse
      *hook_ptr\hook\mov = $B8
    CompilerEndIf
    *hook_ptr\hook\addr = *NewFunctionAddress
    *hook_ptr\hook\push = $50
    *hook_ptr\hook\ret = $C3
     
    CopyMemory(*OldFunctionAddress, @*hook_ptr\orig, SizeOf(opcode))
    If Not WriteProcessMemory_(GetCurrentProcess_(), *OldFunctionAddress, @*hook_ptr\hook, SizeOf(opcode), #Null)
      FreeMemory(*hook_ptr)
      ProcedureReturn #Null
    Else
      ProcedureReturn *hook_ptr
    EndIf
  EndProcedure
  

  Procedure.i UnHook(*hook_ptr.hookstruct)
    Protected retValue.i
    
    If *hook_ptr
      If *hook_ptr\addr
        If WriteProcessMemory_(GetCurrentProcess_(), *hook_ptr\addr, @*hook_ptr\orig, SizeOf(opcode), #Null)
          retValue = *hook_ptr\addr
          FreeMemory(*hook_ptr)
          ProcedureReturn retValue
        EndIf
      EndIf
    EndIf
    
    ProcedureReturn #Null
  EndProcedure
  
  
  Procedure Is64Process(PID); is the Process is 64;
    Protected *IsWow64Process, retvalue, hProcess
    Protected Info.SYSTEM_INFO, is_64 = #False
    
    GetNativeSystemInfo(Info)
    If info\wProcessorArchitecture = 9 ; is a X64 os    
      *IsWow64Process = ProcAddress("kernel32.dll", "IsWow64Process")
      If *IsWow64Process
        hProcess = OpenProcess_(#PROCESS_QUERY_INFORMATION, #False, PID)
        If hProcess
          CallFunctionFast(*IsWow64Process, hProcess, @retvalue)
          CloseHandle_(hProcess)
          If retvalue = #False
            is_64 = #True
          EndIf
        EndIf
      EndIf
    EndIf
    
    ProcedureReturn is_64
  EndProcedure
  
  
  Procedure TraverseRemoteModules(PID, _Module$)
    Protected modules.MODULEENTRY32
    Protected snapshot, m_ok, modBaseAddr
  	
    snapshot = CreateToolhelp32Snapshot_(#TH32CS_SNAPMODULE | #TH32CS_SNAPMODULE32, PID)
    If snapshot
      modules\dwSize = SizeOf(MODULEENTRY32)
      m_ok = Module32First_(snapshot, modules)
      While m_ok
        If FindString(PeekS(@modules\szModule), _Module$, 1, #PB_String_NoCase)
          modBaseAddr = modules\modBaseAddr
          Break
        EndIf
        m_ok = Module32Next_(snapshot, modules)
      Wend
      CloseHandle_(snapshot)  
    EndIf
    
    ProcedureReturn modBaseAddr
  EndProcedure
  
  
  Procedure TraverseRemoteProcs(PID, _Module$, ProcName$, *_Module = #Null)
    Protected hProcess, modBaseAddr, retValue = #Null
    Protected index, index2, TempChar.a, TempFunctionName.s
    Protected DosHeader.IMAGE_DOS_HEADER
    Protected Signature.l
    Protected FileHeader.IMAGE_FILE_HEADER
    Protected Is64Bit.b
    Protected OptHeader64.IMAGE_OPTIONAL_HEADER64
    Protected OptHeader32.IMAGE_OPTIONAL_HEADER32
    Protected ExportDirectory.IMAGE_DATA_DIRECTORY
    Protected ExportTable.IMAGE_EXPORT_DIRECTORY
    Protected Dim ExportFunctionTable.l(0)
    Protected Dim ExportNameTable.l(0)
    Protected Dim ExportOrdinalTable.w(0)
  	
  	hProcess = OpenProcess_(#PROCESS_VM_READ, #False, PID)
  	If hProcess
  	  If *_Module
  	    modBaseAddr = *_Module
  	  Else
  	    modBaseAddr = TraverseRemoteModules(PID, _Module$)
  	  EndIf
      
      If Not modBaseAddr
        Goto TRM_END
      EndIf
      
    	If ReadProcessMemory_(hProcess, modBaseAddr, @DosHeader, SizeOf(IMAGE_DOS_HEADER), #Null) And DosHeader\e_magic <> $5A4D
    	  Goto TRM_END
    	EndIf
    	
    	If ReadProcessMemory_(hProcess, modBaseAddr + DosHeader\e_lfanew, @Signature, SizeOf(Signature), #Null) And Signature <> $4550
    	  Goto TRM_END
    	EndIf
    	
    	If Not ReadProcessMemory_(hProcess, modBaseAddr + DosHeader\e_lfanew + SizeOf(Signature), @FileHeader, SizeOf(IMAGE_FILE_HEADER), #Null)
        Goto TRM_END
      EndIf
      
    	If FileHeader\SizeOfOptionalHeader = SizeOf(IMAGE_OPTIONAL_HEADER64)
    		Is64Bit = #True
    	ElseIf FileHeader\SizeOfOptionalHeader = SizeOf(IMAGE_OPTIONAL_HEADER32)
    		Is64Bit = #False
    	Else
    	  Goto TRM_END
    	EndIf
    	
  	  If Is64Bit
  		  If ReadProcessMemory_(hProcess, modBaseAddr + DosHeader\e_lfanew + SizeOf(Signature) + SizeOf(IMAGE_FILE_HEADER),  @OptHeader64, FileHeader\SizeOfOptionalHeader, #Null) And OptHeader64\Magic <> $020B
  		    Goto TRM_END
  		  EndIf
  	  Else
  		  If ReadProcessMemory_(hProcess, modBaseAddr + DosHeader\e_lfanew + SizeOf(Signature) + SizeOf(IMAGE_FILE_HEADER), @OptHeader32, FileHeader\SizeOfOptionalHeader, #Null) And OptHeader32\Magic <> $010B
  		    Goto TRM_END
  		  EndIf
  		EndIf
  		
    	If Is64Bit And OptHeader64\NumberOfRvaAndSizes >= #IMAGE_DIRECTORY_ENTRY_EXPORT + 1
    		ExportDirectory\VirtualAddress = OptHeader64\DataDirectory[#IMAGE_DIRECTORY_ENTRY_EXPORT]\VirtualAddress
    		ExportDirectory\Size = OptHeader64\DataDirectory[#IMAGE_DIRECTORY_ENTRY_EXPORT]\Size
    	ElseIf OptHeader32\NumberOfRvaAndSizes >= #IMAGE_DIRECTORY_ENTRY_EXPORT + 1
    		ExportDirectory\VirtualAddress = OptHeader32\DataDirectory[#IMAGE_DIRECTORY_ENTRY_EXPORT]\VirtualAddress
    		ExportDirectory\Size = OptHeader32\DataDirectory[#IMAGE_DIRECTORY_ENTRY_EXPORT]\Size
    	Else
    	  Goto TRM_END
    	EndIf
    	
  	  If Not ReadProcessMemory_(hProcess, modBaseAddr + ExportDirectory\VirtualAddress, @ExportTable, SizeOf(IMAGE_EXPORT_DIRECTORY), #Null)
  	    Goto TRM_END
  	  EndIf
  	  
    	ReDim ExportFunctionTable(ExportTable\NumberOfFunctions)
    	ReDim ExportNameTable(ExportTable\NumberOfNames)
    	ReDim ExportOrdinalTable.w(ExportTable\NumberOfNames)
    	
    	If Not ReadProcessMemory_(hProcess, modBaseAddr + ExportTable\AddressOfFunctions,	@ExportFunctionTable(), ExportTable\NumberOfFunctions * SizeOf(Long), #Null)
    	  Goto TRM_END
    	EndIf
     
    	If Not ReadProcessMemory_(hProcess, modBaseAddr + ExportTable\AddressOfNames, @ExportNameTable(), ExportTable\NumberOfNames * SizeOf(Long), #Null)
    	  Goto TRM_END
    	EndIf
     
    	If Not ReadProcessMemory_(hProcess, modBaseAddr + ExportTable\AddressOfNameOrdinals, @ExportOrdinalTable(), ExportTable\NumberOfNames * SizeOf(Word), #Null)
    	  Goto TRM_END
    	EndIf
  
    	For index = 0 To ExportTable\NumberOfNames - 1
    	  TempFunctionName = ""
    	  index2 = 0
    
    	  Repeat
    	    If Not ReadProcessMemory_(hProcess, modBaseAddr + ExportNameTable(index) + index2, @TempChar, SizeOf(TempChar), #Null)
    	      Break 2
          EndIf
    
    			If TempChar = #Null
    			  Break
    			Else
    			  TempFunctionName + Chr(TempChar)
    			EndIf
    			
    			index2 + 1
    		ForEver
    	
      	If TempFunctionName = ProcName$
      	  retValue = modBaseAddr + ExportFunctionTable(ExportOrdinalTable(index))
      	  Break
      	EndIf
      Next
      
      TRM_END:
      CloseHandle_(hProcess)
      FreeArray(ExportFunctionTable())
      FreeArray(ExportNameTable())
      FreeArray(ExportOrdinalTable())
    EndIf
    
    ProcedureReturn retValue
  EndProcedure
  
  
  Procedure InjectDLL(PID.i, DLL$)
    Protected address, ThreadHandle, BufferSize, *buffer
    Protected ProcessHandle.i, retvalue = #Null, *dll_unicode
    Protected is_target_64.b = #False
    
    If FileSize(Dll$) > 0
      is_target_64 = Is64Process(PID)
      If #INJECTOR_IS_64 = is_target_64
        address = ProcAddress("kernel32.dll", "LoadLibraryW")
      ElseIf #INJECTOR_IS_64
        address = TraverseRemoteProcs(PID, "kernel32.dll", "LoadLibraryW")
      Else
        Debug "X86 Injector --> X64 Target not Supported yet!!"
        ProcedureReturn #Null
      EndIf
      
      ProcessHandle = OpenProcess_(#PROCESS_CREATE_THREAD | #PROCESS_QUERY_INFORMATION | #PROCESS_VM_OPERATION | #PROCESS_VM_WRITE | #PROCESS_VM_READ, #False, PID)
      If ProcessHandle
        BufferSize = Len(Dll$) * 2 + 1
        *dll_unicode = AllocateMemory(BufferSize)
        PokeS(*dll_unicode, Dll$, -1, #PB_Unicode)
        *buffer = VirtualAllocEx_(ProcessHandle, #Null, BufferSize, #MEM_COMMIT, #PAGE_READWRITE)
        If *buffer
          WriteProcessMemory_(ProcessHandle, *buffer, *dll_unicode, BufferSize, #Null)
          ThreadHandle = CreateRemoteThread_(ProcessHandle, #Null, #Null, address, *buffer, #Null, #Null)
          If ThreadHandle
            WaitForSingleObject_(ThreadHandle, #INFINITE)
            CloseHandle_(ThreadHandle)
            retvalue = TraverseRemoteModules(PID, GetFilePart(Dll$))
          EndIf
          VirtualFreeEx_(ProcessHandle, *buffer, #Null, #MEM_RELEASE)          
        EndIf
        CloseHandle_(ProcessHandle)
        FreeMemory(*dll_unicode)
      EndIf
    EndIf
    
    ProcedureReturn retvalue
  EndProcedure
  
  
  Procedure.b EjectDLL(PID, DLL$, *_Module = #Null)
    Protected address, ThreadHandle
    Protected ProcessHandle, retvalue = #False
    Protected is_target_64.b = #False
    
    If FileSize(Dll$) > 0 Or *_Module
      is_target_64 = Is64Process(PID)
      If #INJECTOR_IS_64 = is_target_64
        address = ProcAddress("kernel32.dll", "FreeLibrary")
      ElseIf #INJECTOR_IS_64
        address = TraverseRemoteProcs(PID, "kernel32.dll", "FreeLibrary")
      Else
        Debug "X86 Ejector --> X64 Target not Supported yet!!"
        ProcedureReturn #False
      EndIf
      
      If Not *_Module
        *_Module = TraverseRemoteModules(PID, GetFilePart(DLL$))
        If Not *_Module
          ProcedureReturn #False
        EndIf
      EndIf
      
      ProcessHandle = OpenProcess_(#PROCESS_CREATE_THREAD | #PROCESS_QUERY_INFORMATION | #PROCESS_VM_OPERATION | #PROCESS_VM_WRITE | #PROCESS_VM_READ, #False, PID)
      If ProcessHandle
        ThreadHandle = CreateRemoteThread_(ProcessHandle, #Null, #Null, address, *_Module, #Null, #Null)
        If ThreadHandle
          WaitForSingleObject_(ThreadHandle, #INFINITE)
          CloseHandle_(ThreadHandle)
          retvalue = #True
        EndIf
        CloseHandle_(ProcessHandle)
      EndIf
    EndIf
    
    ProcedureReturn retvalue
  EndProcedure
  
  
  Procedure CallRemoteFunction(PID, *Func, Proc2Call$, WaitForReturn.b = #False, *retValue = #Null, *Parameter = #Null, nSize = #Null)
    Protected ret, ProcessHandle, address, *buffer
    Protected ThreadHandle
    
    ProcessHandle = OpenProcess_(#PROCESS_CREATE_THREAD | #PROCESS_QUERY_INFORMATION | #PROCESS_VM_OPERATION | #PROCESS_VM_WRITE | #PROCESS_VM_READ, #False, PID)
    
    If ProcessHandle
      address = TraverseRemoteProcs(PID, "", Proc2Call$, *Func)
      If address
        *buffer = #Null
        If *Parameter And nSize
          *buffer = VirtualAllocEx_(ProcessHandle, #Null, nSize, #MEM_COMMIT, #PAGE_READWRITE)
          WriteProcessMemory_(ProcessHandle, *buffer, *Parameter, nSize, #Null)
        EndIf
              
        ThreadHandle = CreateRemoteThread_(ProcessHandle, #Null, #Null, address, *buffer, #Null, #Null)
        If ThreadHandle
          If WaitForReturn
            WaitForSingleObject_(ThreadHandle, #INFINITE)
            If *retValue
              GetExitCodeThread_(ProcessHandle, *retValue)
            EndIf
          EndIf
          CloseHandle_(ThreadHandle)
          ret = #True
        EndIf
        
        If *buffer
          VirtualFreeEx_(ProcessHandle, *buffer, #Null, #MEM_RELEASE)
        EndIf
        CloseHandle_(ProcessHandle)
      EndIf
    EndIf
    
    ProcedureReturn ret
  EndProcedure
  

EndModule

Simple Sample.pb:

Code: Select all

; Simple Sample
IncludeFile "API_HookEngine.pbi"

EnableExplicit
Global *MessageBoxW, *MessageBoxA

UseModule API_HookEngine

Procedure My_MessageBoxW(hWnd, lpText$, lpCaption$, uType)
  Protected func.i
  Protected ret_value.i
  
  Debug "You Called MessageBoxW(" + hWnd + ", '" + lpText$ + "', '" + lpCaption$ + "', " + uType + ")"
  
  func = UnHook(*MessageBoxW)
  ret_value = MessageBox_(hWnd, lpText$, "Hooked Message", uType)
  *MessageBoxW = Hook(func, @My_MessageBoxW())
  
  ProcedureReturn ret_value
EndProcedure



Procedure My_MessageBoxA(hWnd, lpText$, lpCaption$, uType)
  Protected func.i
  Protected ret_value.i
  
  Debug "You Called MessageBoxA(" + hWnd + ", '" + lpText$ + "', '" + lpCaption$ + "', " + uType + ")"
  
  func = UnHook(*MessageBoxA)
  ret_value = MessageBox_(hWnd, lpText$, "Hooked Message", uType)
  *MessageBoxA = Hook(func, @My_MessageBoxA())
  
  ProcedureReturn ret_value
EndProcedure


*MessageBoxW = Hook(ProcAddress("user32.dll", "MessageBoxW"), @My_MessageBoxW())
*MessageBoxA = Hook(ProcAddress("user32.dll", "MessageBoxA"), @My_MessageBoxA())


MessageRequester("Test", "Hello World!!")

UnHook(*MessageBoxW)
UnHook(*MessageBoxA)

UnuseModule API_HookEngine
Last edited by Peyman on Thu Feb 11, 2016 5:53 pm, edited 7 times in total.
Sorry for my bad english.
User avatar
RSBasic
Moderator
Moderator
Posts: 1218
Joined: Thu Dec 31, 2009 11:05 pm
Location: Gernsbach (Germany)
Contact:

Re: API Hook Engine Module (Windows)

Post by RSBasic »

Thank you for sharing. Image
Image
Image
infratec
Always Here
Always Here
Posts: 6818
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: API Hook Engine Module (Windows)

Post by infratec »

Hi,

small hint:

Use

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  :
  :
  :
CompilerEndIf
Than your small demo code can always be inside the file.

Bernd
Peyman
Enthusiast
Enthusiast
Posts: 203
Joined: Mon Dec 24, 2007 4:15 pm
Location: Iran

Re: API Hook Engine Module (Windows)

Post by Peyman »

@RSBasic
your welcome, enjoy it. 8)

@infratec
very good point, thanks for hint. UPDATED.
Sorry for my bad english.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: API Hook Engine Module (Windows)

Post by Kwai chang caine »

Works great
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: API Hook Engine Module (Windows)

Post by Lunasole »

Looks great, but won't work on Windows 7 Ultimate x64 running with admin righs. I've tried both 32bit and 64.
- if app that set hook calls captured function, it works
- if another app calls while captured, it has no reaction
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Thorium
Addict
Addict
Posts: 1271
Joined: Sat Aug 15, 2009 6:59 pm

Re: API Hook Engine Module (Windows)

Post by Thorium »

Lunasole wrote:Looks great, but won't work on Windows 7 Ultimate x64 running with admin righs. I've tried both 32bit and 64.
- if app that set hook calls captured function, it works
- if another app calls while captured, it has no reaction
Thats how it works. It's hooked only in the process the code is running in.
If you want to hook API functions in other apps you have to inject the code into the process. Search for DLL injection on the forum.
Peyman
Enthusiast
Enthusiast
Posts: 203
Joined: Mon Dec 24, 2007 4:15 pm
Location: Iran

Re: API Hook Engine Module (Windows)

Post by Peyman »

@Kwai chang caine
your welcome.

@Lunasole
as Thorium said with this you just can hook your programs api calls.
for other processes you have 2 option:
1 - DLL Injection : you create a DLL with this hook on apis you want and inject it to a program (as Thorium said)
2 - use a system wide API hooking : its a interesting solution to create a kernel driver and after a process created that kernel driver automatically inject our DLL to that process.

i really like 2nd solution and maybe try to create it if PB members like it.
Sorry for my bad english.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: API Hook Engine Module (Windows)

Post by Lunasole »

Thanks for explanations, then it is all ok.
Peyman wrote: 1 - DLL Injection : you create a DLL with this hook on apis you want and inject it to a program (as Thorium said)
2 - use a system wide API hooking : its a interesting solution to create a kernel driver and after a process created that kernel driver automatically inject our DLL to that process.

i really like 2nd solution and maybe try to create it if PB members like it.
Sounds cool, I remember there was bare driver example, but nothing with such stuff.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Peyman
Enthusiast
Enthusiast
Posts: 203
Joined: Mon Dec 24, 2007 4:15 pm
Location: Iran

Re: API Hook Engine Module (Windows)

Post by Peyman »

hi update 1.1 with a simple dll injector, now working on a professional injector.


Update 1.1
  • Added InjectDLL for inject a DLL to another process (Injector must be same as Target process both must be X86 or X64)
here is a sample for injecting a dll to notepad and hook ShellAboutA/W Apis then every time you click on Help -> About Notepad see your MessageBox 8)

DLL :

Code: Select all

IncludeFile "API_HookEngine.pbi"

UseModule API_HookEngine
EnableExplicit

Global *ShellAboutW
Global *ShellAboutA

Procedure My_ShellAboutW(hWnd, *szApp, *szOtherStuff, hIcon)
  Protected func.i
  Protected ret_value.i
  
  ;Show Our Message
  MessageRequester("About Notepad", "its a hooked Message." + #CRLF$ + "Ha ha ha ha")
  
  
  ;func = UnHook(*ShellAboutW)
  ;ret_value = CallFunctionFast(func, hWnd, *szApp, *szOtherStuff, hIcon)
  ;*ShellAboutW = Hook(func, @My_ShellAboutW())
  
  ProcedureReturn ret_value
EndProcedure



Procedure My_ShellAboutA(hWnd, *szApp, *szOtherStuff, hIcon)
  Protected func.i
  Protected ret_value.i
  
  ;Show Our Message
  MessageRequester("About Notepad", "its a hooked Message." + #CRLF$ + "Ha ha ha ha")
  
  ;func = UnHook(*ShellAboutA)
  ;ret_value = CallFunctionFast(func, hWnd, *szApp, *szOtherStuff, hIcon)
  ;*ShellAboutA = Hook(func, @My_ShellAboutA())
  
  ProcedureReturn ret_value
EndProcedure



ProcedureDLL AttachProcess(Instance)
  Protected name.s = Space(#MAX_PATH)
  
  *ShellAboutW = Hook(ProcAddress("Shell32.dll", "ShellAboutW"), @My_ShellAboutW())
  *ShellAboutA = Hook(ProcAddress("Shell32.dll", "ShellAboutA"), @My_ShellAboutA())
  GetModuleFileName_(#Null, @name, #MAX_PATH)
  OutputDebugString_("Im Loaded in " + GetFilePart(name))
  OutputDebugString_("ShellAboutW = " + Str(*ShellAboutW))
  OutputDebugString_("ShellAboutA = " + Str(*ShellAboutA))
EndProcedure


ProcedureDLL dummy()
  
EndProcedure

UnuseModule API_HookEngine

and the injector code :

Code: Select all

IncludeFile "API_HookEngine.pbi"

UseModule API_HookEngine

program = RunProgram("notepad.exe", "", "", #PB_Program_Open)
If InjectDll(ProgramID(program), "D:\NotepadHook.dll") = #False
  Debug "Error Cant Inject DLL"
EndIf

UnuseModule API_HookEngine
change the path to created Dll, Injector and Target process must be same (X86 or X64).
Sorry for my bad english.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: API Hook Engine Module (Windows)

Post by Lunasole »

Thanks, very interesting examples. Long ago I wished to experiment with hooks and similar stuff to understand it better.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
marroh
User
User
Posts: 72
Joined: Wed Aug 06, 2008 8:21 am

Re: API Hook Engine Module (Windows)

Post by marroh »

Image
PureBASIC v5.41 LTS , Windows v8.1 x64
Forget UNICODE - Keep it BASIC !
Peyman
Enthusiast
Enthusiast
Posts: 203
Joined: Mon Dec 24, 2007 4:15 pm
Location: Iran

Re: API Hook Engine Module (Windows)

Post by Peyman »

hi update 1.2 with a more professional and smarter DLL injector, and way to Eject loaded DLL, and a CallRemoteFunction!!
based on article in Microsoft for DllMain some tasks you should never perform them, like LoadLibrary, CreateProcess, ... (DllMain in purebasic are AttachThread, DetachThread, ....).
So for ex if we want to inject a dll to a process and that dll want to load a library this task can cause a deadlock or a crash (Base on article on msdn), so how we can achive this ? here CallRemoteFunction appears :D
we can create a Dll with some Export and write our tasks to them then inject it to target process and after injection calling that exports.

just one parameter can be passed to Remote procedures, so for more parameter you can use a structure, see below example.


Update 1.2
  • Added EjectDLL method for unload a DLL from target process
  • Added CallRemoteFunction method to call a proc from a DLL in target process (just one parameter can be passed to Remote Function)
  • Improved Injector, Now X64 Injector can inject in x64 & x86 target but x86 injector just can inject to x86 target.
DLL:

Code: Select all

IncludeFile "API_HookEngine.pbi"

UseModule API_HookEngine
EnableExplicit

Global *ShellAboutW
Global *ShellAboutA

Structure Params
  ShowDebug.b
  uMsg.s{100}
EndStructure

Procedure My_ShellAboutW(hWnd, *szApp, *szOtherStuff, hIcon)
  Protected func.i
  Protected ret_value.i
  
  ;Show Our Message
  MessageRequester("About Notepad", "its a hooked Message." + #CRLF$ + "Ha ha ha ha", #MB_ICONINFORMATION)
  
  
  ;func = UnHook(*ShellAboutW)
  ;ret_value = CallFunctionFast(func, hWnd, *szApp, *szOtherStuff, hIcon)
  ;*ShellAboutW = Hook(func, @My_ShellAboutW())
  
  ProcedureReturn ret_value
EndProcedure



Procedure My_ShellAboutA(hWnd, *szApp, *szOtherStuff, hIcon)
  Protected func.i
  Protected ret_value.i
  
  ;Show Our Message
  MessageRequester("About Notepad", "its a hooked Message." + #CRLF$ + "Ha ha ha ha", #MB_ICONINFORMATION)
  
  ;func = UnHook(*ShellAboutA)
  ;ret_value = CallFunctionFast(func, hWnd, *szApp, *szOtherStuff, hIcon)
  ;*ShellAboutA = Hook(func, @My_ShellAboutA())
  
  ProcedureReturn ret_value
EndProcedure


ProcedureDLL _End()
  UnHook(*ShellAboutW)
  UnHook(*ShellAboutA)
EndProcedure


ProcedureDLL Start(*Parameters.Params)
  Protected name.s = Space(#MAX_PATH)
  
  *ShellAboutW = Hook(ProcAddress("Shell32.dll", "ShellAboutW"), @My_ShellAboutW())
  *ShellAboutA = Hook(ProcAddress("Shell32.dll", "ShellAboutA"), @My_ShellAboutA())
  If *Parameters\ShowDebug
    GetModuleFileName_(#Null, @name, #MAX_PATH)
    OutputDebugString_("Im Loaded in " + GetFilePart(name))
    OutputDebugString_("ShellAboutW = " + Str(*ShellAboutW))
    OutputDebugString_("ShellAboutA = " + Str(*ShellAboutA))  
  EndIf
  MessageRequester("Message From Injector", *Parameters\uMsg)
  
EndProcedure
Injector :

Code: Select all

IncludeFile "API_HookEngine.pbi"

UseModule API_HookEngine

Structure Params
  ShowDebug.b
  uMsg.s{100}
EndStructure

Define program = RunProgram("notepad.exe", "", "", #PB_Program_Open)
Define *func
Define DLL$ = "D:\NotepadHook.dll"
Define Parameters.Params
Parameters\ShowDebug = #False
Parameters\uMsg = "WOW OMG Message Passing!!"


If OpenWindow(0, 0, 0, 240, 90, "DLL Injection Sample", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  ButtonGadget(0, 40, 30, 60, 30, "Inject")
  ButtonGadget(1, 130, 30, 60, 30, "Eject")
  
  Repeat
    Event = WaitWindowEvent()
     
    Select Event
     
      Case #PB_Event_Gadget
        Select EventGadget()
          Case 0
            *func = InjectDll(ProgramID(program), DLL$)
            If *func
              Debug "DLL Injected Sucessfuly"
              Delay(1000)
              If CallRemoteFunction(ProgramID(program), *func, "Start", #True, #Null, @Parameters, SizeOf(Parameters))
                Debug "Start Procedure Called"
                MessageRequester("", "Now Check Help -> About Notepad", #MB_ICONINFORMATION)
              Else
                Debug "Cannot Call Start Procedure"
              EndIf
            Else
              Debug "Error in Injecting DLL to program"
            EndIf
            
          Case 1
            If *func
              If CallRemoteFunction(ProgramID(program), *func, "_End", #True)
                Debug "_End Procedure Called"
                If EjectDLL(ProgramID(program), "", *func)
                  Debug "DLL Ejected Sucessfuly"
                  *func = #Null
                  MessageRequester("", "Another time Check Help -> About Notepad", #MB_ICONINFORMATION)
                Else
                  Debug "Error in Ejecting DLL from program"
                EndIf
              Else
                Debug "Cannot Call _End Procedure"
              EndIf
            Else
              Debug "First inject DLL to program"
            EndIf
            
        EndSelect
        
    EndSelect
  Until Event = #PB_Event_CloseWindow
  
EndIf

If ProgramRunning(program)
  KillProgram(program)
EndIf
Sorry for my bad english.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: API Hook Engine Module (Windows)

Post by Kwai chang caine »

For me that not works on v5.40 and W7 :|
Error in Injecting DLL to program
Perhaps a problem to translate something in french ?
ImageThe happiness is a road...
Not a destination
uweb
User
User
Posts: 98
Joined: Wed Mar 15, 2006 9:40 am
Location: Germany

Re: API Hook Engine Module (Windows)

Post by uweb »

for 64 Bit :

Code: Select all

Define program = RunProgram("C:\Windows\SysWOW64\notepad.exe", "", "", #PB_Program_Open)
Please pardon my English, my native tongue is German.
Post Reply