├── 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 | 
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 |
--------------------------------------------------------------------------------