mirror of
https://github.com/EgeBalci/crc32_api
synced 2026-06-08 10:55:29 +00:00
First commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Ege Balcı
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,2 @@
|
||||
# CRC32_API
|
||||
Assembly API block that uses CRC32 for resolving Windows API function addresses
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python2.7
|
||||
# -*- coding: utf-8 -*-
|
||||
#=============================================================================#
|
||||
# Example: Simply print the correct hash value for the function kernel32.dll!WinExec
|
||||
# >hash.py kernel32.dll WinExec
|
||||
#
|
||||
# Author: Ege Balcı (ege.balci[at]pm[dot]me)
|
||||
#=============================================================================#
|
||||
from sys import path
|
||||
import os, time, sys, crcmod
|
||||
|
||||
def unicode( string, uppercase=True ):
|
||||
result = "";
|
||||
if uppercase:
|
||||
string = string.upper()+"\x00"
|
||||
for c in string:
|
||||
result += c + "\x00"
|
||||
return result
|
||||
#=============================================================================#
|
||||
def hash( module, function, bits=13, print_hash=True ):
|
||||
crc32_func = crcmod.mkCrcFun(0x11EDC6F41, initCrc=0, xorOut=0)
|
||||
h = crc32_func(unicode(module)+function+"\x00")
|
||||
print "[+] 0x%08X = %s!%s" % ( h, module.lower(), function )
|
||||
return h
|
||||
|
||||
#=============================================================================#
|
||||
def main( argv=None ):
|
||||
if not argv:
|
||||
argv = sys.argv
|
||||
try:
|
||||
if len( argv ) == 1:
|
||||
print "Usage: crc32_hash.py [<module.dll> <function>]"
|
||||
else:
|
||||
print "[+] Ran on %s\n" % ( time.asctime( time.localtime() ) )
|
||||
hash( argv[1], argv[2] )
|
||||
except Exception, e:
|
||||
print "[-] ", e
|
||||
#=============================================================================#
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
#=============================================================================#
|
||||
@@ -0,0 +1,111 @@
|
||||
;-----------------------------------------------------------------------------;
|
||||
; Author: Stephen Fewer, Ege Balcı (stephen_fewer[at]harmonysecurity[dot]com, egebalci[at]pm[dot]me)
|
||||
; Compatible: Windows 10, 8.1, 8, 7, 2008, Vista, 2003, XP, 2000, NT4
|
||||
; Version: 1.0 (18 May 2020)
|
||||
; Architecture: x64
|
||||
; Size: 196 bytes
|
||||
;-----------------------------------------------------------------------------;
|
||||
|
||||
[BITS 64]
|
||||
|
||||
; Windows x64 calling convention:
|
||||
; http://msdn.microsoft.com/en-us/library/9b372w95.aspx
|
||||
|
||||
; Input: The hash of the API to call in r10d and all its parameters (rcx/rdx/r8/r9/any stack params)
|
||||
; Output: The return value from the API call will be in RAX.
|
||||
; Clobbers: RAX, RCX, RDX, R8, R9, R10, R11
|
||||
; Un-Clobbered: RBX, RSI, RDI, RBP, R12, R13, R14, R15.
|
||||
; RSP will be off by -40 hence the 'add rsp, 40' after each call to this function
|
||||
; Note: This function assumes the direction flag has allready been cleared via a CLD instruction.
|
||||
; Note: This function is unable to call forwarded exports.
|
||||
|
||||
api_call:
|
||||
push r9 ; Save the 4th parameter
|
||||
push r8 ; Save the 3rd parameter
|
||||
push rdx ; Save the 2nd parameter
|
||||
push rcx ; Save the 1st parameter
|
||||
push rsi ; Save RSI
|
||||
xor rdx, rdx ; Zero rdx
|
||||
mov rdx, [gs:rdx+96] ; Get a pointer to the PEB
|
||||
mov rdx, [rdx+24] ; Get PEB->Ldr
|
||||
mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list
|
||||
next_mod: ;
|
||||
mov rsi, [rdx+80] ; Get pointer to modules name (unicode string)
|
||||
movzx rcx, word [rdx+74] ; Set rcx to the length we want to check
|
||||
xor r9, r9 ; Clear r9 which will store the hash of the module name
|
||||
loop_modname: ;
|
||||
xor rax, rax ; Clear RAX
|
||||
lodsb ; Read in the next byte of the name
|
||||
cmp al, 'a' ; Some versions of Windows use lower case module names
|
||||
jl not_lowercase ;
|
||||
sub al, 0x20 ; If so normalise to uppercase
|
||||
not_lowercase: ;
|
||||
crc32 r9d,al ; Calculate CRC32 of module name
|
||||
loop loop_modname ; Loop untill we have read enough
|
||||
; We now have the module hash computed
|
||||
push rdx ; Save the current position in the module list for later
|
||||
push r9 ; Save the current module hash for later
|
||||
; Proceed to itterate the export address table,
|
||||
mov rdx, [rdx+32] ; Get this modules base address
|
||||
mov eax, dword [rdx+60] ; Get PE header
|
||||
add rax, rdx ; Add the modules base address
|
||||
cmp word [rax+24], 0x020B ; is this module actually a PE64 executable?
|
||||
; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and
|
||||
; their may be a PE32 module present in the PEB's module list, (typicaly the main module).
|
||||
; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48])
|
||||
jne get_next_mod1 ; if not, proceed to the next module
|
||||
mov eax, dword [rax+136] ; Get export tables RVA
|
||||
test rax, rax ; Test if no export address table is present
|
||||
jz get_next_mod1 ; If no EAT present, process the next module
|
||||
add rax, rdx ; Add the modules base address
|
||||
push rax ; Save the current modules EAT
|
||||
mov ecx, dword [rax+24] ; Get the number of function names
|
||||
mov r8d, dword [rax+32] ; Get the rva of the function names
|
||||
add r8, rdx ; Add the modules base address
|
||||
; Computing the module hash + function hash
|
||||
get_next_func: ;
|
||||
jrcxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
|
||||
mov r9, [rsp+8] ; Reset the current module hash
|
||||
dec rcx ; Decrement the function name counter
|
||||
mov esi, dword [r8+rcx*4]; Get rva of next module name
|
||||
add rsi, rdx ; Add the modules base address
|
||||
; And compare it to the one we want
|
||||
loop_funcname: ;
|
||||
xor rax, rax ; Clear RAX
|
||||
lodsb ; Read in the next byte of the ASCII function name
|
||||
crc32 r9d,al ; Calculate CRC32 of function name
|
||||
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
|
||||
jne loop_funcname ; If we have not reached the null terminator, continue
|
||||
cmp r9d, r10d ; Compare the hash to the one we are searchnig for
|
||||
jnz get_next_func ; Go compute the next function hash if we have not found it
|
||||
; If found, fix up stack, call the function and then value else compute the next one...
|
||||
pop rax ; Restore the current modules EAT
|
||||
mov r8d, dword [rax+36] ; Get the ordinal table rva
|
||||
add r8, rdx ; Add the modules base address
|
||||
mov cx, [r8+2*rcx] ; Get the desired functions ordinal
|
||||
mov r8d, dword [rax+28] ; Get the function addresses table rva
|
||||
add r8, rdx ; Add the modules base address
|
||||
mov eax, dword [r8+4*rcx]; Get the desired functions RVA
|
||||
add rax, rdx ; Add the modules base address to get the functions actual VA
|
||||
; We now fix up the stack and perform the call to the drsired function...
|
||||
finish:
|
||||
pop r8 ; Clear off the current modules hash
|
||||
pop r8 ; Clear off the current position in the module list
|
||||
pop rsi ; Restore RSI
|
||||
pop rcx ; Restore the 1st parameter
|
||||
pop rdx ; Restore the 2nd parameter
|
||||
pop r8 ; Restore the 3rd parameter
|
||||
pop r9 ; Restore the 4th parameter
|
||||
pop r10 ; Pop off the return address
|
||||
sub rsp, 32 ; Reserve space for the four register params (4 * sizeof(QWORD) = 32)
|
||||
; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP).
|
||||
push r10 ; Push back the return address
|
||||
jmp rax ; Jump to required function
|
||||
; We now automagically return to the correct caller...
|
||||
get_next_mod: ;
|
||||
pop rax ; Pop off the current (now the previous) modules EAT
|
||||
get_next_mod1: ;
|
||||
pop r9 ; Pop off the current (now the previous) modules hash
|
||||
pop rdx ; Restore our position in the module list
|
||||
mov rdx, [rdx] ; Get the next module
|
||||
jmp next_mod ; Process this module
|
||||
@@ -0,0 +1,98 @@
|
||||
;-----------------------------------------------------------------------------;
|
||||
; Author: Stephen Fewer, Ege Balcı (stephen_fewer[at]harmonysecurity[dot]com, egebalci[at]pm[dot]me)
|
||||
; Compatible: Windows 10, 8.1, 8, 7, 2008, Vista, 2003, XP, 2000, NT4
|
||||
; Version: 1.0 (18 May 2020)
|
||||
; Size: 129 bytes
|
||||
;-----------------------------------------------------------------------------;
|
||||
|
||||
[BITS 32]
|
||||
|
||||
; Input: The hash of the API to call and all its parameters must be pushed onto stack.
|
||||
; Output: The return value from the API call will be in EAX.
|
||||
; Clobbers: EAX, ECX and EDX (ala the normal stdcall calling convention)
|
||||
; Un-Clobbered: EBX, ESI, EDI, ESP and EBP can be expected to remain un-clobbered.
|
||||
; Note: This function assumes the direction flag has allready been cleared via a CLD instruction.
|
||||
; Note: This function is unable to call forwarded exports.
|
||||
|
||||
api_call:
|
||||
pushad ; We preserve all the registers for the caller, bar EAX and ECX.
|
||||
mov ebp, esp ; Create a new stack frame
|
||||
xor eax, eax ; Zero EAX (upper 3 bytes will remain zero until function is found)
|
||||
mov edx, [fs:eax+48] ; Get a pointer to the PEB
|
||||
mov edx, [edx+12] ; Get PEB->Ldr
|
||||
mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
|
||||
next_mod: ;
|
||||
mov esi, [edx+40] ; Get pointer to modules name (unicode string)
|
||||
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
|
||||
xor edi, edi ; Clear EDI which will store the hash of the module name
|
||||
loop_modname: ;
|
||||
lodsb ; Read in the next byte of the name
|
||||
cmp al, 'a' ; Some versions of Windows use lower case module names
|
||||
jl not_lowercase ;
|
||||
sub al, 0x20 ; If so normalise to uppercase
|
||||
not_lowercase: ;
|
||||
crc32 edi,al ; Calculate CRC32 value
|
||||
loop loop_modname ; Loop until we have read enough
|
||||
|
||||
; We now have the module hash computed
|
||||
push edx ; Save the current position in the module list for later
|
||||
push edi ; Save the current module hash for later
|
||||
; Proceed to iterate the export address table,
|
||||
mov edx, [edx+16] ; Get this modules base address
|
||||
mov ecx, [edx+60] ; Get PE header
|
||||
|
||||
; use ecx as our EAT pointer here so we can take advantage of jecxz.
|
||||
mov ecx, [ecx+edx+120] ; Get the EAT from the PE header
|
||||
jecxz get_next_mod1 ; If no EAT present, process the next module
|
||||
add ecx, edx ; Add the modules base address
|
||||
push ecx ; Save the current modules EAT
|
||||
mov ebx, [ecx+32] ; Get the rva of the function names
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov ecx, [ecx+24] ; Get the number of function names
|
||||
; now ecx returns to its regularly scheduled counter duties
|
||||
|
||||
; Computing the module hash + function hash
|
||||
get_next_func: ;
|
||||
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
|
||||
mov edi, [ebp-8] ; Reset the current module hash
|
||||
dec ecx ; Decrement the function name counter
|
||||
mov esi, [ebx+ecx*4] ; Get rva of next module name
|
||||
add esi, edx ; Add the modules base address
|
||||
; And compare it to the one we want
|
||||
loop_funcname: ;
|
||||
lodsb ; Read in the next byte of the ASCII function name
|
||||
crc32 edi,al ; Calculate CRC32
|
||||
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
|
||||
jne loop_funcname ; If we have not reached the null terminator, continue
|
||||
cmp edi, [ebp+36] ; Compare the hash to the one we are searching for
|
||||
jnz get_next_func ; Go compute the next function hash if we have not found it
|
||||
|
||||
; If found, fix up stack, call the function and then value else compute the next one...
|
||||
pop eax ; Restore the current modules EAT
|
||||
mov ebx, [eax+36] ; Get the ordinal table rva
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov cx, [ebx+2*ecx] ; Get the desired functions ordinal
|
||||
mov ebx, [eax+28] ; Get the function addresses table rva
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov eax, [ebx+4*ecx] ; Get the desired functions RVA
|
||||
add eax, edx ; Add the modules base address to get the functions actual VA
|
||||
; We now fix up the stack and perform the call to the desired function...
|
||||
finish:
|
||||
mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad
|
||||
pop ebx ; Clear off the current modules hash
|
||||
pop ebx ; Clear off the current position in the module list
|
||||
popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered
|
||||
pop ecx ; Pop off the origional return address our caller will have pushed
|
||||
pop edx ; Pop off the hash value our caller will have pushed
|
||||
push ecx ; Puh back the return address
|
||||
jmp eax ; Properly call the required function for EAF bypass
|
||||
ret
|
||||
; We now automagically return to the correct caller...
|
||||
|
||||
get_next_mod: ;
|
||||
pop edi ; Pop off the current (now the previous) modules EAT
|
||||
get_next_mod1: ;
|
||||
pop edi ; Pop off the current (now the previous) modules hash
|
||||
pop edx ; Restore our position in the module list
|
||||
mov edx, [edx] ; Get the next module
|
||||
jmp short next_mod ; Process this module
|
||||
Reference in New Issue
Block a user