├── flow.png ├── LICENSE ├── README.md ├── hash.py ├── x86 ├── inline_hook.asm └── iat_hook.asm └── x64 ├── inline_hook.asm └── iat_hook.asm /flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EgeBalci/Hook_API/HEAD/flow.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ege Balcı 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hook API 2 | Assembly blocks for hooking windows API functions. 3 | 4 | 5 | ## iat_hook.asm 6 | It finds the import address table index of API functions by parsing the `_IMAGE_IMPORT_DESCRIPTOR` structure entries inside the import table of the PE file. It first calculates the ROR(13) hash of the (module name + function name) and compares with the hash passed to block. If the hash matches it replaces IAT entry with the passed address. Sometimes the memory space that is containing the import address table is not writable by the running thread. In such cases this block uses `VirtualProtect` function for changing the virtual address space permissions that is containing the IAT entry we want. 7 | 8 | ![Description](https://github.com/EgeBalci/hook_api/raw/master/flow.png) 9 | 10 | IMPORTANT !! 11 | - The function that is called with hook_api must be imported by the PE file or it will crash. 12 | 13 | ### Example 14 | 15 | Following code hooks the `DeleteFileA` windows API function using the hook_api block. After hooking the function it will always return nonzero value. When a process executes this code it will not able to delete any file. 16 | 17 | ``` 18 | [BITS 32] 19 | pushad ; Save all registers to stack 20 | pushfd ; Save all flags to stack 21 | cld ; Clear direction flags 22 | call start ; call start 23 | %include "iat_api.asm" ; iat_api.asm goes here 24 | start: ; ... 25 | pop ebp ; Pop out the address of iat_api.asm to EBP 26 | call fin ; Push the address of hooked code to stack 27 | hooked_code: ; ... 28 | mov eax,0xFF ; Move non-zero value to EAX 29 | ret ; Return 30 | fin: ; ... 31 | push 0x13DD2ED7 ; hash( "KERNEL32.dll", "DeleteFileA" ) 32 | call ebp ; Call the iat_api block 33 | popfd ; Pop back saved flags 34 | popad ; Pop back saved registers 35 | ret ; Return to caller 36 | 37 | ``` 38 | 39 | Following code hooks the `TerminateProcess` windows API function using the hook_api block. After hooking the function it will always return nonzero value. When a process executes this code it will not able to terminate any other process with `TerminateProcess` API function. 40 | 41 | ``` 42 | [BITS 64] 43 | 44 | cld ; Clear direction flags 45 | push r10 ; Save R10 register 46 | %include "iat_api.asm" ; iat_api.asm goes here 47 | start: ; ... 48 | pop rbp ; Pop out the address of iat_api.asm to RBP 49 | call get_return_true ; Call get_return_true 50 | return_true: ; ... 51 | mov rax,0x01 ; Move non zero value to RAX 52 | ret ; Return 53 | get_return_true: ; ... 54 | mov r10d,0x5ECADC87 ; hash( "KERNEL32.dll", "TerminateProcess" ) 55 | call rbp ; Call the iat_api block 56 | pop rax ; Clear stack 57 | pop r10 ; Restore R10 58 | ret ; Return to caller 59 | 60 | ``` 61 | 62 | ## inline_hook.asm 63 | It finds the address of the target API functions by parsing the `PEB->Ldr->InMemoryOrderModuleList`. After finding the address it replaces the beginng of the function with the given `patch` binary. This binary can be used as a prologue for redirecting the target API function to elsewhere or returning any arbitrary value. 64 | 65 | ### Example 66 | 67 | Following code hooks the `AdjustTokenPrivileges` windows API function using the inline_hook.asm block. After hooking the function it will always return nonzero value. When a process executes this code it will not be able to escalate privileges. 68 | 69 | Content of `patch` binary 70 | ``` 71 | db 0x32,0xc0 ; xor eax,eax 72 | db 0xc3 ; ret 73 | ``` 74 | 75 | x86 Hook code: 76 | ``` 77 | [BITS 32] 78 | 79 | 80 | cld ; Clear direction flags 81 | call get_hook_api ; Get the address of inline_hook_api.asm to stack 82 | %include "inline_hook.asm" ; inline_hook.asm goes here 83 | get_hook_api: ; ... 84 | pop ebp ; Pop out the address of inline_hook_api.asm to EBP 85 | push 0x330A1F75 ; hash("NTDLL.dll", "AdjustTokenPrivileges") 86 | call ebp ; hook("RtlSetDaclSecurityDescriptor") 87 | ``` 88 | 89 | x64 Hook code: 90 | ``` 91 | [BITS 64] 92 | 93 | cld ; Clear direction flags 94 | call get_hook_api ; Get the address of inline_hook_api.asm to stack 95 | %include "inline_hook.asm" ; inline_hook.asm goes here 96 | get_hook_api: ; ... 97 | pop rbp ; Pop out the address of inline_hook_api.asm to EBP 98 | mov r10d,0x330A1F75 ; hash("ADVAPI32.dll", "AdjustTokenPrivileges") 99 | call rbp ; hook("RtlSetDaclSecurityDescriptor") 100 | 101 | ``` -------------------------------------------------------------------------------- /hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.7 2 | #=============================================================================# 3 | # This script can detect hash collisions between exported API functions in 4 | # multiple modules by either scanning a directory tree or just a single module. 5 | # This script can also just output the correct hash value for any single API 6 | # function for use with the 'api_call' function in 'block_api.asm'. 7 | # 8 | # Example: Detect fatal collisions against all modules in the C drive: 9 | # >hash.py /dir c:\ 10 | # 11 | # Example: List the hashes for all exports from kernel32.dll (As found in 'c:\windows\system32\') 12 | # >hash.py /mod c:\windows\system32\ kernel32.dll 13 | # 14 | # Example: Simply print the correct hash value for the function kernel32.dll!WinExec 15 | # >hash.py kernel32.dll WinExec 16 | # 17 | # Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) 18 | #=============================================================================# 19 | from sys import path 20 | import os, time, sys 21 | 22 | # Modify this path to pefile to suit your machine... 23 | pefile_path = "D:\\Development\\Frameworks\\pefile\\" 24 | 25 | path.append( pefile_path ) 26 | import pefile 27 | #=============================================================================# 28 | collisions = [ ( 0x006B8029, "ws2_32.dll!WSAStartup" ), 29 | ( 0xE0DF0FEA, "ws2_32.dll!WSASocketA" ), 30 | ( 0x6737DBC2, "ws2_32.dll!bind" ), 31 | ( 0xFF38E9B7, "ws2_32.dll!listen" ), 32 | ( 0xE13BEC74, "ws2_32.dll!accept" ), 33 | ( 0x614D6E75, "ws2_32.dll!closesocket" ), 34 | ( 0x6174A599, "ws2_32.dll!connect" ), 35 | ( 0x5FC8D902, "ws2_32.dll!recv" ), 36 | ( 0x5F38EBC2, "ws2_32.dll!send" ), 37 | 38 | ( 0x5BAE572D, "kernel32.dll!WriteFile" ), 39 | ( 0x4FDAF6DA, "kernel32.dll!CreateFileA" ), 40 | ( 0x13DD2ED7, "kernel32.dll!DeleteFileA" ), 41 | ( 0xE449F330, "kernel32.dll!GetTempPathA" ), 42 | ( 0x528796C6, "kernel32.dll!CloseHandle" ), 43 | ( 0x863FCC79, "kernel32.dll!CreateProcessA" ), 44 | ( 0xE553A458, "kernel32.dll!VirtualAlloc" ), 45 | ( 0x300F2F0B, "kernel32.dll!VirtualFree" ), 46 | ( 0x0726774C, "kernel32.dll!LoadLibraryA" ), 47 | ( 0x7802F749, "kernel32.dll!GetProcAddress" ), 48 | ( 0x601D8708, "kernel32.dll!WaitForSingleObject" ), 49 | ( 0x876F8B31, "kernel32.dll!WinExec" ), 50 | ( 0x9DBD95A6, "kernel32.dll!GetVersion" ), 51 | ( 0xEA320EFE, "kernel32.dll!SetUnhandledExceptionFilter" ), 52 | ( 0x56A2B5F0, "kernel32.dll!ExitProcess" ), 53 | ( 0x0A2A1DE0, "kernel32.dll!ExitThread" ), 54 | 55 | ( 0x6F721347, "ntdll.dll!RtlExitUserThread" ), 56 | 57 | ( 0x23E38427, "advapi32.dll!RevertToSelf" ) 58 | ] 59 | 60 | collisions_detected = {} 61 | modules_scanned = 0 62 | functions_scanned = 0 63 | #=============================================================================# 64 | def ror( dword, bits ): 65 | return ( dword >> bits | dword << ( 32 - bits ) ) & 0xFFFFFFFF 66 | #=============================================================================# 67 | def unicode( string, uppercase=True ): 68 | result = ""; 69 | if uppercase: 70 | string = string.upper() 71 | for c in string: 72 | result += c + "\x00" 73 | return result 74 | #=============================================================================# 75 | def hash( module, function, bits=13, print_hash=True ): 76 | module_hash = 0 77 | function_hash = 0 78 | for c in unicode( module + "\x00" ): 79 | module_hash = ror( module_hash, bits ) 80 | module_hash += ord( c ) 81 | for c in str( function + "\x00" ): 82 | function_hash = ror( function_hash, bits ) 83 | function_hash += ord( c ) 84 | h = module_hash + function_hash & 0xFFFFFFFF 85 | if print_hash: 86 | print "[+] 0x%08X = %s!%s" % ( h, module.lower(), function ) 87 | return h 88 | #=============================================================================# 89 | def scan( dll_path, dll_name, print_hashes=False, print_collisions=True ): 90 | global modules_scanned 91 | global functions_scanned 92 | try: 93 | dll_name = dll_name.lower() 94 | modules_scanned += 1 95 | pe = pefile.PE( os.path.join( dll_path, dll_name ) ) 96 | for export in pe.DIRECTORY_ENTRY_EXPORT.symbols: 97 | if export.name is None: 98 | continue 99 | h = hash( dll_name, export.name, print_hash=print_hashes ) 100 | for ( col_hash, col_name ) in collisions: 101 | if col_hash == h and col_name != "%s!%s" % (dll_name, export.name): 102 | if h not in collisions_detected.keys(): 103 | collisions_detected[h] = [] 104 | collisions_detected[h].append( (dll_path, dll_name, export.name) ) 105 | break 106 | functions_scanned += 1 107 | except: 108 | pass 109 | #=============================================================================# 110 | def scan_directory( dir ): 111 | for dot, dirs, files in os.walk( dir ): 112 | for file_name in files: 113 | if file_name[-4:] == ".dll":# or file_name[-4:] == ".exe": 114 | scan( dot, file_name ) 115 | print "\n[+] Found %d Collisions.\n" % ( len(collisions_detected) ) 116 | for h in collisions_detected.keys(): 117 | for (col_hash, col_name ) in collisions: 118 | if h == col_hash: 119 | detected_name = col_name 120 | break 121 | print "[!] Collision detected for 0x%08X (%s):" % ( h, detected_name ) 122 | for (collided_dll_path, collided_dll_name, collided_export_name) in collisions_detected[h]: 123 | print "\t%s!%s (%s)" % ( collided_dll_name, collided_export_name, collided_dll_path ) 124 | print "\n[+] Scanned %d exported functions via %d modules.\n" % ( functions_scanned, modules_scanned ) 125 | #=============================================================================# 126 | def main( argv=None ): 127 | if not argv: 128 | argv = sys.argv 129 | try: 130 | if len( argv ) == 1: 131 | print "Usage: hash.py [/dir ] | [/mod ] | [ ]" 132 | else: 133 | print "[+] Ran on %s\n" % ( time.asctime( time.localtime() ) ) 134 | if argv[1] == "/dir": 135 | print "[+] Scanning directory '%s' for collisions..." % argv[2] 136 | scan_directory( argv[2] ) 137 | elif argv[1] == "/mod": 138 | print "[+] Scanning module '%s' in directory '%s'..." % ( argv[3], argv[2] ) 139 | scan( argv[2], argv[3], print_hashes=True ) 140 | else: 141 | hash( argv[1], argv[2] ) 142 | except Exception, e: 143 | print "[-] ", e 144 | #=============================================================================# 145 | if __name__ == "__main__": 146 | main() 147 | #=============================================================================# 148 | -------------------------------------------------------------------------------- /x86/inline_hook.asm: -------------------------------------------------------------------------------- 1 | ;-----------------------------------------------------------------------------; 2 | ; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) 3 | ; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4 4 | ; Version: 1.0 (3 April 2019) 5 | ; Size: 130 bytes 6 | ;-----------------------------------------------------------------------------; 7 | 8 | [BITS 32] 9 | 10 | ; Input: The hash of the API that will be hooked and address for redirecting the API. 11 | ; Output: No return value. 12 | ; Clobbers: EAX, ECX and EDX (ala the normal stdcall calling convention) + EBX, ESI 13 | ; Un-Clobbered: EDI, ESP and EBP can be expected to remain un-clobbered. 14 | ; Note: This function assumes the direction flag has allready been cleared via a CLD instruction. 15 | ; Note: This function is unable to call forwarded exports. 16 | 17 | 18 | inline_hook: 19 | pop ebx ; Pop the return address 20 | call api_call ; Get the address of the API that will be hooked 21 | mov esi,eax ; Save the API address to ESI 22 | push 0xC38AE110 ; hash("KERNEL32.dll", "VirtualProtect") 23 | call api_call ; Get the VirtualProtect API address 24 | push dword 0x00 ; OldProtect 25 | push esp ; lpflOldProtect 26 | push dword 0x40 ; flNewProtect (PAGE_EXECUTE_READWRITE) 27 | push dword patch_size ; dwSize 28 | push esi ; lpAddress 29 | call eax ; VirtualProtect(lpAddress,5,PAGE_EXECUTE_READWRITE,&OldProtect) 30 | pop eax ; Clear stack 31 | call patch_end ; Get the address of patch code to stack 32 | patch_start: ; 33 | incbin "patch" ; Assembled (binary) patch code. This will be written at the start of hooked function 34 | patch_end: ; 35 | patch_size: equ $-patch_start; Size of the patch stored as patch_size 36 | pop edx ; Pop out the address of patch to EDX 37 | mov ecx,patch_size ; Move the patch size into ECX 38 | write: ; ... 39 | mov al,byte [edx] ; Move 1 byte from patch into AL 40 | mov byte [esi],al ; Write AL to ESI (function address) 41 | inc edx ; Increase patch index 42 | inc esi ; Increase function address index 43 | loop write ; Write until all written 44 | push ebx ; Push back the return address 45 | ret ; Return to caller 46 | api_call: 47 | pushad ; We preserve all the registers for the caller, bar EAX and ECX. 48 | mov ebp, esp ; Create a new stack frame 49 | xor eax, eax ; Zero EAX (upper 3 bytes will remain zero until function is found) 50 | mov edx, [fs:eax+48] ; Get a pointer to the PEB 51 | mov edx, [edx+12] ; Get PEB->Ldr 52 | mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list 53 | next_mod: ; 54 | mov esi, [edx+40] ; Get pointer to modules name (unicode string) 55 | movzx ecx, word [edx+38] ; Set ECX to the length we want to check 56 | xor edi, edi ; Clear EDI which will store the hash of the module name 57 | loop_modname: ; 58 | lodsb ; Read in the next byte of the name 59 | cmp al, 'a' ; Some versions of Windows use lower case module names 60 | jl not_lowercase ; 61 | sub al, 0x20 ; If so normalise to uppercase 62 | not_lowercase: ; 63 | ror edi, 13 ; Rotate right our hash value 64 | add edi, eax ; Add the next byte of the name 65 | loop loop_modname ; Loop until we have read enough 66 | 67 | ; We now have the module hash computed 68 | push edx ; Save the current position in the module list for later 69 | push edi ; Save the current module hash for later 70 | ; Proceed to iterate the export address table, 71 | mov edx, [edx+16] ; Get this modules base address 72 | mov ecx, [edx+60] ; Get PE header 73 | 74 | ; use ecx as our EAT pointer here so we can take advantage of jecxz. 75 | mov ecx, [ecx+edx+120] ; Get the EAT from the PE header 76 | jecxz get_next_mod1 ; If no EAT present, process the next module 77 | add ecx, edx ; Add the modules base address 78 | push ecx ; Save the current modules EAT 79 | mov ebx, [ecx+32] ; Get the rva of the function names 80 | add ebx, edx ; Add the modules base address 81 | mov ecx, [ecx+24] ; Get the number of function names 82 | ; now ecx returns to its regularly scheduled counter duties 83 | ; Computing the module hash + function hash 84 | get_next_func: ; 85 | jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module 86 | dec ecx ; Decrement the function name counter 87 | mov esi, [ebx+ecx*4] ; Get rva of next module name 88 | add esi, edx ; Add the modules base address 89 | xor edi, edi ; Clear EDI which will store the hash of the function name 90 | ; And compare it to the one we want 91 | loop_funcname: ; 92 | lodsb ; Read in the next byte of the ASCII function name 93 | ror edi, 13 ; Rotate right our hash value 94 | add edi, eax ; Add the next byte of the name 95 | cmp al, ah ; Compare AL (the next byte from the name) to AH (null) 96 | jne loop_funcname ; If we have not reached the null terminator, continue 97 | add edi, [ebp-8] ; Add the current module hash to the function hash 98 | cmp edi, [ebp+36] ; Compare the hash to the one we are searching for 99 | jnz get_next_func ; Go compute the next function hash if we have not found it 100 | 101 | ; If found, fix up stack, call the function and then value else compute the next one... 102 | pop eax ; Restore the current modules EAT 103 | mov ebx, [eax+36] ; Get the ordinal table rva 104 | add ebx, edx ; Add the modules base address 105 | mov cx, [ebx+2*ecx] ; Get the desired functions ordinal 106 | mov ebx, [eax+28] ; Get the function addresses table rva 107 | add ebx, edx ; Add the modules base address 108 | mov eax, [ebx+4*ecx] ; Get the desired functions RVA 109 | add eax, edx ; Add the modules base address to get the functions actual VA 110 | ; We now fix up the stack and perform the call to the desired function... 111 | finish: 112 | mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad 113 | pop ebx ; Clear off the current modules hash 114 | pop ebx ; Clear off the current position in the module list 115 | popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered 116 | pop ecx ; Pop off the origional return address our caller will have pushed 117 | pop edx ; Pop off the hash value our caller will have pushed 118 | push ecx ; Push back the correct return value 119 | ret ; Return the found API address 120 | ; We now automagically return to the correct caller... 121 | 122 | get_next_mod: ; 123 | pop edi ; Pop off the current (now the previous) modules EAT 124 | get_next_mod1: ; 125 | pop edi ; Pop off the current (now the previous) modules hash 126 | pop edx ; Restore our position in the module list 127 | mov edx, [edx] ; Get the next module 128 | jmp short next_mod ; Process this module 129 | -------------------------------------------------------------------------------- /x64/inline_hook.asm: -------------------------------------------------------------------------------- 1 | ;-----------------------------------------------------------------------------; 2 | ; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) 3 | ; Compatible: Windows 7, 2003 4 | ; Architecture: x64 5 | ; Size: 200 bytes 6 | ;-----------------------------------------------------------------------------; 7 | 8 | [BITS 64] 9 | 10 | ; Windows x64 calling convention: 11 | ; http://msdn.microsoft.com/en-us/library/9b372w95.aspx 12 | 13 | ; Input: The hash of the API to call in r10d and all its parameters (rcx/rdx/r8/r9/any stack params) 14 | ; Output: The return value from the API call will be in RAX. 15 | ; Clobbers: RAX, RCX, RDX, R8, R9, R10, R11 16 | ; Un-Clobbered: RBX, RSI, RDI, RBP, R12, R13, R14, R15. 17 | ; RSP will be off by -40 hence the 'add rsp, 40' after each call to this function 18 | ; Note: This function assumes the direction flag has allready been cleared via a CLD instruction. 19 | ; Note: This function is unable to call forwarded exports. 20 | 21 | inline_hook: 22 | pop rbx ; Save the return address 23 | call api_call ; Get the address of API that will be hooked 24 | add rsp,0x20 ; Clear stack 25 | mov rsi,rax ; Save API address to RSI 26 | mov r10d,0xC38AE110 ; hash("KERNEL32.dll", "VirtualProtect") 27 | call api_call ; Get the VirtualProtect API address 28 | add rsp,0x20 ; Clear stack 29 | push qword 0x00 ; OldProtect 30 | mov r9,rsp ; lpflOldProtect 31 | mov r8,0x40 ; flNewProtect (PAGE_EXECUTE_READWRITE) 32 | mov rdx,patch_size ; dwSize 33 | mov rcx,rsi ; lpAddress 34 | call rax ; VirtualProtect(lpAddress,12,PAGE_EXECUTE_READWRITE,&OldProtect) 35 | pop rax ; Clean stack 36 | call patch_end 37 | patch_start: 38 | incbin "patch" ; Patch code for the hooked API 39 | patch_end: 40 | patch_size: equ $-patch_start: 41 | pop rdx ; Pop out the address of patch code 42 | mov rcx,patch_size ; Move the size of the patch to RCX 43 | write: 44 | mov al,byte [rdx] ; Get one byte from patch code 45 | mov byte [rsi],al ; Move one byte to hooked function 46 | inc rsi 47 | inc rdx 48 | loop write 49 | push rbx ; Push back the return address 50 | ret ; Return to caller 51 | api_call: 52 | push r9 ; Save the 4th parameter 53 | push r8 ; Save the 3rd parameter 54 | push rdx ; Save the 2nd parameter 55 | push rcx ; Save the 1st parameter 56 | push rsi ; Save RSI 57 | xor rdx, rdx ; Zero rdx 58 | mov rdx, [gs:rdx+96] ; Get a pointer to the PEB 59 | mov rdx, [rdx+24] ; Get PEB->Ldr 60 | mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list 61 | next_mod: ; 62 | mov rsi, [rdx+80] ; Get pointer to modules name (unicode string) 63 | movzx rcx, word [rdx+74] ; Set rcx to the length we want to check 64 | xor r9, r9 ; Clear r9 which will store the hash of the module name 65 | loop_modname: ; 66 | xor rax, rax ; Clear rax 67 | lodsb ; Read in the next byte of the name 68 | cmp al, 'a' ; Some versions of Windows use lower case module names 69 | jl not_lowercase ; 70 | sub al, 0x20 ; If so normalise to uppercase 71 | not_lowercase: ; 72 | ror r9d, 13 ; Rotate right our hash value 73 | add r9d, eax ; Add the next byte of the name 74 | loop loop_modname ; Loop untill we have read enough 75 | ; We now have the module hash computed 76 | push rdx ; Save the current position in the module list for later 77 | push r9 ; Save the current module hash for later 78 | ; Proceed to itterate the export address table, 79 | mov rdx, [rdx+32] ; Get this modules base address 80 | mov eax, dword [rdx+60] ; Get PE header 81 | add rax, rdx ; Add the modules base address 82 | cmp word [rax+24], 0x020B ; is this module actually a PE64 executable? 83 | ; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and 84 | ; their may be a PE32 module present in the PEB's module list, (typicaly the main module). 85 | ; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48]) 86 | jne get_next_mod1 ; if not, proceed to the next module 87 | mov eax, dword [rax+136] ; Get export tables RVA 88 | test rax, rax ; Test if no export address table is present 89 | jz get_next_mod1 ; If no EAT present, process the next module 90 | add rax, rdx ; Add the modules base address 91 | push rax ; Save the current modules EAT 92 | mov ecx, dword [rax+24] ; Get the number of function names 93 | mov r8d, dword [rax+32] ; Get the rva of the function names 94 | add r8, rdx ; Add the modules base address 95 | ; Computing the module hash + function hash 96 | get_next_func: ; 97 | jrcxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module 98 | dec rcx ; Decrement the function name counter 99 | mov esi, dword [r8+rcx*4]; Get rva of next module name 100 | add rsi, rdx ; Add the modules base address 101 | xor r9, r9 ; Clear r9 which will store the hash of the function name 102 | ; And compare it to the one we want 103 | loop_funcname: ; 104 | xor rax, rax ; Clear rax 105 | lodsb ; Read in the next byte of the ASCII function name 106 | ror r9d, 13 ; Rotate right our hash value 107 | add r9d, eax ; Add the next byte of the name 108 | cmp al, ah ; Compare AL (the next byte from the name) to AH (null) 109 | jne loop_funcname ; If we have not reached the null terminator, continue 110 | add r9, [rsp+8] ; Add the current module hash to the function hash 111 | cmp r9d, r10d ; Compare the hash to the one we are searchnig for 112 | jnz get_next_func ; Go compute the next function hash if we have not found it 113 | ; If found, fix up stack, call the function and then value else compute the next one... 114 | pop rax ; Restore the current modules EAT 115 | mov r8d, dword [rax+36] ; Get the ordinal table rva 116 | add r8, rdx ; Add the modules base address 117 | mov cx, [r8+2*rcx] ; Get the desired functions ordinal 118 | mov r8d, dword [rax+28] ; Get the function addresses table rva 119 | add r8, rdx ; Add the modules base address 120 | mov eax, dword [r8+4*rcx]; Get the desired functions RVA 121 | add rax, rdx ; Add the modules base address to get the functions actual VA 122 | ; We now fix up the stack and perform the call to the drsired function... 123 | finish: 124 | pop r8 ; Clear off the current modules hash 125 | pop r8 ; Clear off the current position in the module list 126 | pop rsi ; Restore RSI 127 | pop rcx ; Restore the 1st parameter 128 | pop rdx ; Restore the 2nd parameter 129 | pop r8 ; Restore the 3rd parameter 130 | pop r9 ; Restore the 4th parameter 131 | pop r10 ; pop off the return address 132 | sub rsp, 32 ; reserve space for the four register params (4 * sizeof(QWORD) = 32) 133 | ; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP). 134 | push r10 ; push back the return address 135 | ret ; Return to caller 136 | ; We now automagically return to the correct caller... 137 | get_next_mod: ; 138 | pop rax ; Pop off the current (now the previous) modules EAT 139 | get_next_mod1: ; 140 | pop r9 ; Pop off the current (now the previous) modules hash 141 | pop rdx ; Restore our position in the module list 142 | mov rdx, [rdx] ; Get the next module 143 | jmp next_mod ; Process this module 144 | -------------------------------------------------------------------------------- /x86/iat_hook.asm: -------------------------------------------------------------------------------- 1 | ;-----------------------------------------------------------------------------; 2 | ; Author: Ege Balcı 3 | ; Compatible: Windows 10/8.1/8/7/2008/Vista/2003/XP/2000/NT4 4 | ; Version: 1.0 (25 January 2018) 5 | ; Size: 172 bytes 6 | ;-----------------------------------------------------------------------------; 7 | 8 | ; This block hooks the API functions by locating the addresses of API functions from import address table with given ror(13) hash value. 9 | ; Design is inpired from Stephen Fewer's hash api. 10 | 11 | [BITS 32] 12 | 13 | ; Input: The hash of the API to call and all its parameters must be pushed onto stack. 14 | ; Output: The return value from the API call will be in EAX. 15 | ; Clobbers: EAX, ECX and EDX (ala the normal stdcall calling convention) 16 | ; Un-Clobbered: EBX, ESI, EDI, ESP and EBP can be expected to remain un-clobbered. 17 | ; Note: This function assumes the direction flag has allready been cleared via a CLD instruction. 18 | ; Note: This function is unable to call forwarded exports. 19 | 20 | %define ROTATION 0x0D ; Rotation value for ROR hash 21 | 22 | set_essentials: 23 | pushad ; We preserve all the registers for the caller, bar EAX and ECX. 24 | xor eax,eax ; Zero EAX (upper 3 bytes will remain zero until function is found) 25 | mov edx,[fs:eax+0x30] ; Get a pointer to the PEB 26 | mov edx,[edx+0x0C] ; Get PEB->Ldr 27 | mov edx,[edx+0x14] ; Get the first module from the InMemoryOrder module list 28 | mov edx,[edx+0x10] ; Get this modules base address 29 | push edx ; Save the image base to stack (will use this alot) 30 | add edx,[edx+0x3C] ; "PE" Header 31 | mov edx,[edx+0x80] ; Import table RVA 32 | add edx,[esp] ; Address of Import Table 33 | push edx ; Save the &IT to stack (will use this alot) 34 | mov esi,[esp+0x04] ; Move image base to ESI 35 | sub esp,0x08 ; Allocate space for import desriptor & hash 36 | sub edx,0x14 ; Prepare the import descriptor pointer for processing 37 | next_desc: 38 | add edx,0x14 ; Get the next import descriptor 39 | cmp dword [edx],0x00 ; Check if import descriptor valid 40 | jz not_found ; If import name array RVA is zero finish parsing 41 | mov si,[edx+0x0C] ; Get pointer to module name string RVA 42 | xor edi, edi ; Clear EDI which will store the hash of the module name 43 | loop_modname: ; 44 | lodsb ; Read in the next byte of the name 45 | cmp al, 'a' ; Some versions of Windows use lower case module names 46 | jl not_lowercase ; 47 | sub al, 0x20 ; If so normalise to uppercase 48 | not_lowercase: ; 49 | ror edi,ROTATION ; Rotate right our hash value 50 | add edi,eax ; Add the next byte of the name 51 | ror edi,ROTATION ; In order to calculate the same hash values as Stephen Fewer's hash API we need to rotate one more and add a null byte. 52 | test al,al ; Check if we read all 53 | jnz loop_modname 54 | ; We now have the module hash computed 55 | mov [esp+4],edx ; Save the current position in the module list for later 56 | mov [esp],edi ; Save the current module hash for later 57 | ; Proceed to iterate the export address table, 58 | mov ecx,[edx] ; Get the RVA of import names table 59 | add ecx,[esp+0x0C] ; Add image base and get address of import names table 60 | sub ecx,0x04 ; Go 4 byte back 61 | get_next_func: 62 | ; use ecx as our EAT pointer here so we can take advantage of jecxz. 63 | add ecx,0x04 ; 4 byte forward 64 | cmp dword [ecx],0x00 ; Check if end of INT 65 | jz next_desc ; If no INT present, process the next import descriptor 66 | mov esi,[ecx] ; Get the RVA of func name hint 67 | cmp esi,0x80000000 ; Check if the high order bit is set 68 | jns get_next_func ; If not there is no function name string :( 69 | add esi,[esp+0x0C] ; Add the image base and get the address of function hint 70 | add dword esi,0x02 ; Move 2 bytes forward to asci function name 71 | ; now ecx returns to its regularly scheduled counter duties 72 | ; Computing the module hash + function hash 73 | xor edi,edi ; Clear EDI which will store the hash of the function name 74 | ; And compare it to the one we want 75 | loop_funcname: ; 76 | lodsb ; Read in the next byte of the ASCII function name 77 | ror edi,ROTATION ; Rotate right our hash value 78 | add edi,eax ; Add the next byte of the name 79 | cmp al,ah ; Compare AL (the next byte from the name) to AH (null) 80 | jne loop_funcname ; If we have not reached the null terminator, continue 81 | add edi,[esp] ; Add the current module hash to the function hash 82 | cmp edi,[esp+0x34] ; Compare the hash to the one we are searching for 83 | jnz get_next_func ; Go compute the next function hash if we have not found it 84 | 85 | ; If found, fix up stack, replace the function address and then value else compute the next one... 86 | mov eax,[edx+0x10] ; Get the RVA of current descriptor's IAT 87 | mov edx,[edx] ; Get the import names table RVA of current import descriptor 88 | add edx,[esp+0x0C] ; Get the address of import names table of current import descriptor 89 | sub ecx,edx ; Find the function array index ? 90 | add eax,[esp+0x0C] ; Add the image base to current descriptors IAT RVA 91 | add eax,ecx ; Add the function index 92 | ; Now we clean the stack 93 | push eax ; Save the function address to stack 94 | cld ; Clear direction flags 95 | call unprotect ; Get the address of block_api to stack 96 | _api_call: 97 | pushad ; We preserve all the registers for the caller, bar EAX and ECX. 98 | mov ebp, esp ; Create a new stack frame 99 | xor eax, eax ; Zero EAX (upper 3 bytes will remain zero until function is found) 100 | mov edx, [fs:eax+48] ; Get a pointer to the PEB 101 | mov edx, [edx+12] ; Get PEB->Ldr 102 | mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list 103 | _next_mod: ; 104 | mov esi, [edx+40] ; Get pointer to modules name (unicode string) 105 | movzx ecx, word [edx+38] ; Set ECX to the length we want to check 106 | xor edi, edi ; Clear EDI which will store the hash of the module name 107 | _loop_modname: ; 108 | lodsb ; Read in the next byte of the name 109 | cmp al, 'a' ; Some versions of Windows use lower case module names 110 | jl _not_lowercase ; 111 | sub al, 0x20 ; If so normalise to uppercase 112 | _not_lowercase: ; 113 | ror edi, 13 ; Rotate right our hash value 114 | add edi, eax ; Add the next byte of the name 115 | loop _loop_modname ; Loop until we have read enough 116 | 117 | ; We now have the module hash computed 118 | push edx ; Save the current position in the module list for later 119 | push edi ; Save the current module hash for later 120 | ; Proceed to iterate the export address table, 121 | mov edx, [edx+16] ; Get this modules base address 122 | mov ecx, [edx+60] ; Get PE header 123 | 124 | ; use ecx as our EAT pointer here so we can take advantage of jecxz. 125 | mov ecx, [ecx+edx+120] ; Get the EAT from the PE header 126 | jecxz _get_next_mod1 ; If no EAT present, process the next module 127 | add ecx, edx ; Add the modules base address 128 | push ecx ; Save the current modules EAT 129 | mov ebx, [ecx+32] ; Get the rva of the function names 130 | add ebx, edx ; Add the modules base address 131 | mov ecx, [ecx+24] ; Get the number of function names 132 | ; now ecx returns to its regularly scheduled counter duties 133 | 134 | ; Computing the module hash + function hash 135 | _get_next_func: ; 136 | jecxz _get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module 137 | dec ecx ; Decrement the function name counter 138 | mov esi, [ebx+ecx*4] ; Get rva of next module name 139 | add esi, edx ; Add the modules base address 140 | xor edi, edi ; Clear EDI which will store the hash of the function name 141 | ; And compare it to the one we want 142 | _loop_funcname: ; 143 | lodsb ; Read in the next byte of the ASCII function name 144 | ror edi, 13 ; Rotate right our hash value 145 | add edi, eax ; Add the next byte of the name 146 | cmp al, ah ; Compare AL (the next byte from the name) to AH (null) 147 | jne _loop_funcname ; If we have not reached the null terminator, continue 148 | add edi, [ebp-8] ; Add the current module hash to the function hash 149 | cmp edi, [ebp+36] ; Compare the hash to the one we are searching for 150 | jnz _get_next_func ; Go compute the next function hash if we have not found it 151 | 152 | ; If found, fix up stack, call the function and then value else compute the next one... 153 | pop eax ; Restore the current modules EAT 154 | mov ebx, [eax+36] ; Get the ordinal table rva 155 | add ebx, edx ; Add the modules base address 156 | mov cx, [ebx+2*ecx] ; Get the desired functions ordinal 157 | mov ebx, [eax+28] ; Get the function addresses table rva 158 | add ebx, edx ; Add the modules base address 159 | mov eax, [ebx+4*ecx] ; Get the desired functions RVA 160 | add eax, edx ; Add the modules base address to get the functions actual VA 161 | ; We now fix up the stack and perform the call to the desired function... 162 | _finish: 163 | mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad 164 | pop ebx ; Clear off the current modules hash 165 | pop ebx ; Clear off the current position in the module list 166 | popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered 167 | pop ecx ; Pop off the origional return address our caller will have pushed 168 | pop edx ; Pop off the hash value our caller will have pushed 169 | push ecx ; Push back the correct return value 170 | jmp eax ; Jump into the required function 171 | ; We now automagically return to the correct caller... 172 | 173 | _get_next_mod: ; 174 | pop edi ; Pop off the current (now the previous) modules EAT 175 | _get_next_mod1: ; 176 | pop edi ; Pop off the current (now the previous) modules hash 177 | pop edx ; Restore our position in the module list 178 | mov edx, [edx] ; Get the next module 179 | jmp _short next_mod ; Process this module 180 | unprotect: 181 | pop ebp ; Pop the address of block_api to EBP 182 | push eax ; Allocate space for lpflOldProtect 183 | push esp ; lpflOldProtect 184 | push 0x00000004 ; flNewProtect (PAGE_READWRITE) 185 | push 0x00000008 ; dwSize 186 | push eax ; lpAddress (IAT entry address) 187 | push 0xC38AE110 ; hash( "KERNEL32.dll", "VirtualProtect" ) 188 | call ebp ; VirtualProtect(&IAT,8,PAGE_READWRITE,&STACK) 189 | pop eax ; Deallocate lpflOldProtect 190 | pop eax ; Pop back the function address to be hooked 191 | finish: 192 | mov esi,[esp+0x38] ; Get the hooker fucntion address ;D 193 | mov [eax],esi ; Replace the IAT entry with hooker 194 | add esp,0x10 ; Deallocate saved module hash, import descriptor address, import table address 195 | popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered 196 | pop ecx ; Pop off the origional return address our caller will have pushed 197 | pop edx ; Pop off the hash value our caller will have pushed 198 | pop edx ; Pop off the hooker function address 199 | push ecx ; Push back the correct return value 200 | ret ; Return 201 | not_found: 202 | add esp,0x08 ; Fix the stack 203 | popad ; Restore all registers 204 | ret ; Return 205 | ; (API is not found) 206 | -------------------------------------------------------------------------------- /x64/iat_hook.asm: -------------------------------------------------------------------------------- 1 | ;-----------------------------------------------------------------------------; 2 | ; Author: Ege Balcı (ege.balci[at]invictuseurope[dot]com) 3 | ; Compatible: Windows 7, 2003 4 | ; Architecture: x64 5 | ; Size: 454 bytes 6 | ;-----------------------------------------------------------------------------; 7 | 8 | [BITS 64] 9 | 10 | ; Windows x64 calling convention: 11 | ; http://msdn.microsoft.com/en-us/library/9b372w95.aspx 12 | 13 | ; Input: The hash of the API to hook in r10d and the first stack parameter is the address to be replaced with. 14 | ; Output: Block do not give any output. 15 | ; Clobbers: RAX, R10 16 | ; Un-Clobbered: RBX, RDX, RSI, RDI, RBP, R11, R12, R13, R14, R15. 17 | ; Note: This function assumes the direction flag has allready been cleared via a CLD instruction. 18 | ; Note: This function is unable to call forwarded exports. 19 | ; Note: Caller needs to clean the stack parameter after block finishes. 20 | 21 | 22 | %define ROTATION 13 ; Rotation value for ROR hash 23 | 24 | api_call: 25 | push rdx ; Save RDX 26 | push rcx ; Save RCX 27 | push rsi ; Save RSI 28 | push rdi ; Save RDI 29 | xor rdx,rdx ; Zero RDX 30 | mov rdx,[gs:rdx+96] ; Get a pointer to the PEB 31 | mov rdx,[rdx+24] ; Get PEB->Ldr 32 | mov rdx,[rdx+32] ; Get the first module from the InMemoryOrder module list 33 | mov rdx,[rdx+32] ; Get this modules base address 34 | push rdx ; Save the image base to stack (will use this alot) 35 | add dx,word [rdx+60] ; "PE" Header 36 | mov edx,dword [rdx+144] ; Import table RVA 37 | add rdx,[rsp] ; Address of Import Table 38 | push rdx ; Save the &IT to stack (will use this alot)i 39 | mov rsi,[rsp+8] ; Move the image base to RSI 40 | sub rsp,16 ; Allocate space for import descriptor counter & hash 41 | sub rdx,20 ; Prepare import descriptor pointer for processing 42 | next_desc: 43 | add rdx,20 ; Get the next import descriptor 44 | cmp dword [rdx],0 ; Check if import descriptor is valid 45 | jz not_found ; If import name array RVA is zero finish parsing 46 | mov rsi,[rsp+16] ; Move import table address to RSI 47 | mov si,[rdx+12] ; Get pointer to module name string RVA 48 | xor rdi,rdi ; Clear RDI which will store the hash of the module name 49 | xor rax,rax ; Clear RAX for calculating the hash 50 | loop_modname: 51 | lodsb ; Read in the next byte of the name 52 | cmp al,'a' ; Some versions of windows use lower case module names 53 | jl not_lowercase ; 54 | sub al,32 ; If so normalize to uppercase 55 | not_lowercase: 56 | ror edi, ROTATION ; Rotate right our hash value 57 | add edi, eax ; Add the next byte of the name 58 | ror edi,ROTATION ; In order to calculate the same hash values as Stephen Fewer's hash API we need to rotate one more and add a null byte. 59 | test al,al ; Check if we read all 60 | jnz loop_modname ; 61 | ; We now have the module hash computed 62 | mov [rsp+8],rdx ; Save the current position in the module listfor later 63 | mov [rsp],edi ; Save the current module hash for later 64 | ; Proceed to itterate the export address table, 65 | mov ecx,dword [rdx] ; Get RVA of import names table 66 | add rcx,[rsp+24] ; Add the image base and get the address of import names table 67 | sub rcx,8 ; Go 4 bytes back 68 | get_next_func: ; 69 | add ecx,8 ; 4 byte forward 70 | cmp dword [rcx],0 ; Check if end of INT 71 | jz next_desc ; If no INT present, process the next import descriptor 72 | mov esi,dword [rcx] ; Get the RVA of func name hint 73 | cmp esi,0x80000000 ; Check if the high order bit is set 74 | jns get_next_func ; If not, there is no function name string :( 75 | add rsi,[rsp+24] ; Add the image base and get the address of function name hint 76 | add dword esi,2 ; Move 2 bytes forward to asci function name 77 | ; now ecx returns to its regularly scheduled counter duties 78 | ; Computing the module hash + function hash 79 | xor rdi,rdi 80 | xor rax,rax 81 | ; And compare it to the one we want 82 | loop_funcname: 83 | lodsb ; Read in the next byte of the ASCII function name 84 | ror edi,ROTATION ; Rotate right our hash value 85 | add edi,eax ; Add the next byte of the name 86 | cmp al,ah ; Compare AL (the next byte from the name) to AH (null) 87 | jne loop_funcname ; If we have not reached the null terminator, continue 88 | add edi,[rsp] ; Add the current module hash to the function hash 89 | cmp edi,r10d ; Compare the hash to the one we are searchnig for 90 | jnz get_next_func ; Go compute the next function hash if we have not found it 91 | ; If found, fix up stack, call the function and then value else compute the next one... 92 | mov eax,dword [rdx+16] ; Get the RVA of current descriptor's IAT 93 | mov edx,dword [rdx] ; Get the import names table RVA of current import descriptor 94 | add rdx,[rsp+24] ; Get the address of import names table of current import descriptor 95 | sub rcx,rdx ; Find the function array index ? 96 | add rax,[rsp+24] ; Add the image base to current descriptors IAT RVA 97 | add rax,rcx ; Add the function index 98 | ; Now clean the stack 99 | ; We now fix up the stack and perform the call to the drsired function... 100 | push rax ; Save the function address to stack 101 | cld ; Clear direction flags 102 | call unprotect ; Get the address of block_api to stack 103 | _api_call: 104 | push r9 ; Save the 4th parameter 105 | push r8 ; Save the 3rd parameter 106 | push rdx ; Save the 2nd parameter 107 | push rcx ; Save the 1st parameter 108 | push rsi ; Save RSI 109 | xor rdx, rdx ; Zero rdx 110 | mov rdx, [gs:rdx+96] ; Get a pointer to the PEB 111 | mov rdx, [rdx+24] ; Get PEB->Ldr 112 | mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list 113 | _next_mod: ; 114 | mov rsi, [rdx+80] ; Get pointer to modules name (unicode string) 115 | movzx rcx, word [rdx+74] ; Set rcx to the length we want to check 116 | xor r9, r9 ; Clear r9 which will store the hash of the module name 117 | _loop_modname: ; 118 | xor rax, rax ; Clear rax 119 | lodsb ; Read in the next byte of the name 120 | cmp al, 'a' ; Some versions of Windows use lower case module names 121 | jl _not_lowercase ; 122 | sub al, 0x20 ; If so normalise to uppercase 123 | _not_lowercase: ; 124 | ror r9d, ROTATION ; Rotate right our hash value 125 | add r9d, eax ; Add the next byte of the name 126 | loop _loop_modname ; Loop untill we have read enough 127 | ; We now have the module hash computed 128 | push rdx ; Save the current position in the module list for later 129 | push r9 ; Save the current module hash for later 130 | ; Proceed to itterate the export address table, 131 | mov rdx, [rdx+32] ; Get this modules base address 132 | mov eax, dword [rdx+60] ; Get PE header 133 | add rax, rdx ; Add the modules base address 134 | cmp word [rax+24], 0x020B ; is this module actually a PE64 executable? 135 | ; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and 136 | ; their may be a PE32 module present in the PEB's module list, (typicaly the main module). 137 | ; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48]) 138 | jne _get_next_mod1 ; if not, proceed to the next module 139 | mov eax, dword [rax+136] ; Get export tables RVA 140 | test rax, rax ; Test if no export address table is present 141 | jz _get_next_mod1 ; If no EAT present, process the next module 142 | add rax, rdx ; Add the modules base address 143 | push rax ; Save the current modules EAT 144 | mov ecx, dword [rax+24] ; Get the number of function names 145 | mov r8d, dword [rax+32] ; Get the rva of the function names 146 | add r8, rdx ; Add the modules base address 147 | ; Computing the module hash + function hash 148 | _get_next_func: ; 149 | jrcxz _get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module 150 | dec rcx ; Decrement the function name counter 151 | mov esi, dword [r8+rcx*4]; Get rva of next module name 152 | add rsi, rdx ; Add the modules base address 153 | xor r9, r9 ; Clear r9 which will store the hash of the function name 154 | ; And compare it to the one we want 155 | _loop_funcname: ; 156 | xor rax, rax ; Clear rax 157 | lodsb ; Read in the next byte of the ASCII function name 158 | ror r9d, ROTATION ; Rotate right our hash value 159 | add r9d, eax ; Add the next byte of the name 160 | cmp al, ah ; Compare AL (the next byte from the name) to AH (null) 161 | jne _loop_funcname ; If we have not reached the null terminator, continue 162 | add r9, [rsp+8] ; Add the current module hash to the function hash 163 | cmp r9d, r10d ; Compare the hash to the one we are searchnig for 164 | jnz _get_next_func ; Go compute the next function hash if we have not found it 165 | ; If found, fix up stack, call the function and then value else compute the next one... 166 | pop rax ; Restore the current modules EAT 167 | mov r8d, dword [rax+36] ; Get the ordinal table rva 168 | add r8, rdx ; Add the modules base address 169 | mov cx, [r8+2*rcx] ; Get the desired functions ordinal 170 | mov r8d, dword [rax+28] ; Get the function addresses table rva 171 | add r8, rdx ; Add the modules base address 172 | mov eax, dword [r8+4*rcx]; Get the desired functions RVA 173 | add rax, rdx ; Add the modules base address to get the functions actual VA 174 | ; We now fix up the stack and perform the call to the drsired function... 175 | _finish: 176 | pop r8 ; Clear off the current modules hash 177 | pop r8 ; Clear off the current position in the module list 178 | pop rsi ; Restore RSI 179 | pop rcx ; Restore the 1st parameter 180 | pop rdx ; Restore the 2nd parameter 181 | pop r8 ; Restore the 3rd parameter 182 | pop r9 ; Restore the 4th parameter 183 | pop r10 ; pop off the return address 184 | sub rsp, 32 ; reserve space for the four register params (4 * sizeof(QWORD) = 32) 185 | ; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP). 186 | push r10 ; push back the return address 187 | jmp rax ; Jump into the required function 188 | ; We now automagically return to the correct caller... 189 | _get_next_mod: ; 190 | pop rax ; Pop off the current (now the previous) modules EAT 191 | _get_next_mod1: ; 192 | pop r9 ; Pop off the current (now the previous) modules hash 193 | pop rdx ; Restore our position in the module list 194 | mov rdx, [rdx] ; Get the next module 195 | jmp _next_mod ; Process this module 196 | unprotect: 197 | pop rbp ; Pop the address of block_api to RBP 198 | mov rcx,[rsp] ; lpAddress (IAT entry address) 199 | mov rdx,8 ; dwSize 200 | mov r8,0x04 ; flNewProtect (PAGE_READWRITE)i 201 | push rax ; Allocate space for lpflOldProtect 202 | mov r9,rsp ; lpflOldProtecti 203 | mov r10d,0xC38AE110 ; hash( "KERNEL32.dll", "VirtualProtect" ) 204 | call rbp ; VirtualProtect(&IAT,8,PAGE_READWRITE,&STACK) 205 | add rsp,40 ; Clean out the stack 206 | pop rax ; Move the IAT entry address to RAX 207 | finish: 208 | add rsp,32 ; Clear off the module hash, module list index, &IT, image base 209 | pop rdi ; Restore RDI 210 | pop rsi ; Restore RSI 211 | pop rcx ; Restore RCX 212 | pop rdx ; Restore RDX 213 | mov r10,[rsp+8] ; Move the new IAT entry to R10 214 | mov [rax],r10 ; Change the IAT entry with the new function address 215 | ret ; Finito ! 216 | ; We now automagically return to the correct caller... 217 | not_found: 218 | add rsp,32 ; Clear off the module hash, module list index, &IT, image base 219 | pop rdi ; Restore RDI 220 | pop rsi ; Restore RSI 221 | pop rcx ; Restore RCX 222 | pop rdx ; Restore RDX 223 | ret ; Return to caller 224 | --------------------------------------------------------------------------------