i try to optimize it to work on X64 & X86 Windows apps and also with Unicode switch on.
Version 1.0
- Initial
- Added InjectDLL for inject a DLL to another process (Injector must be same as Target process both must be X86 or X64)
- 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.
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