147 lines
2.9 KiB
Ruby
147 lines
2.9 KiB
Ruby
|
# This file is part of Metasm, the Ruby assembly manipulation suite
|
||
|
# Copyright (C) 2006-2009 Yoann GUILLOT
|
||
|
#
|
||
|
# Licence is LGPL, see LICENCE in the top-level directory
|
||
|
|
||
|
# this script compiles a source file (asm or C) into a shellcode that will
|
||
|
# dynamically resolve the address of functions it uses
|
||
|
# windows only, supposes the shellcode is run in the address space of a process
|
||
|
# whose PEB allows to find all required libraries.
|
||
|
|
||
|
require 'metasm'
|
||
|
|
||
|
sc = Metasm::Shellcode.new(Metasm::Ia32.new)
|
||
|
|
||
|
case ARGV[0]
|
||
|
when /\.c(pp)?$/i
|
||
|
src_c = File.read(ARGV[0])
|
||
|
sc.assemble 'jmp main'
|
||
|
sc.compile_c src_c
|
||
|
when /\.asm$/i
|
||
|
src = File.read(ARGV[0])
|
||
|
sc.assemble src
|
||
|
when nil; abort "need sourcefile"
|
||
|
else abort "unknown srcfile extension"
|
||
|
end
|
||
|
|
||
|
# find external symbols needed by the shellcode
|
||
|
ext_syms = sc.encoded.reloc_externals
|
||
|
|
||
|
# resolver code
|
||
|
sc.parse <<EOS
|
||
|
get_libbase:
|
||
|
mov eax, fs:[0x30] // peb
|
||
|
mov eax, [eax+12] // peb_ldr
|
||
|
add eax, 12 // &inloadorder
|
||
|
libbase_loop:
|
||
|
mov eax, [eax] // next
|
||
|
mov edx, [eax+12*4] // basename ptr
|
||
|
mov cl, [edx+6]
|
||
|
shl ecx, 8
|
||
|
mov cl, [edx+4]
|
||
|
shl ecx, 8
|
||
|
mov cl, [edx+2]
|
||
|
shl ecx, 8
|
||
|
mov cl, [edx]
|
||
|
or ecx, 0x20202020 // downcase
|
||
|
cmp ecx, [esp+4]
|
||
|
jnz libbase_loop
|
||
|
mov eax, [eax+6*4] // baseaddr
|
||
|
ret 4
|
||
|
|
||
|
hash_name:
|
||
|
push ecx
|
||
|
push edx
|
||
|
xor eax, eax
|
||
|
xor ecx, ecx
|
||
|
mov edx, [esp+12]
|
||
|
dec edx
|
||
|
hash_loop:
|
||
|
ror eax, 0dh
|
||
|
add eax, ecx
|
||
|
inc edx
|
||
|
mov cl, [edx]
|
||
|
test cl, cl
|
||
|
jnz hash_loop
|
||
|
pop edx
|
||
|
pop ecx
|
||
|
ret 4
|
||
|
|
||
|
resolve_proc:
|
||
|
push ecx
|
||
|
push edx
|
||
|
push ebp
|
||
|
push dword ptr [esp+0x14] // arg_2
|
||
|
call get_libbase
|
||
|
mov ebp, eax // imagebase
|
||
|
add eax, [eax+0x3c] // coffhdr
|
||
|
mov edx, [eax+0x78] // exportdirectory
|
||
|
add edx, ebp
|
||
|
or ecx, -1
|
||
|
resolve_loop:
|
||
|
inc ecx
|
||
|
mov eax, [edx+0x20] // name
|
||
|
add eax, ebp
|
||
|
mov eax, [eax+4*ecx]
|
||
|
add eax, ebp
|
||
|
push eax
|
||
|
call hash_name
|
||
|
cmp eax, [esp+0x10] // cmp hash(name[i]), arg_1
|
||
|
jnz resolve_loop
|
||
|
mov eax, [edx+0x24] // ord
|
||
|
add eax, ebp
|
||
|
movzx ecx, word ptr [eax+2*ecx]
|
||
|
mov eax, [edx+0x1c] // func
|
||
|
add eax, ebp
|
||
|
mov eax, [eax+4*ecx] // func[ord[i]]
|
||
|
add eax, ebp
|
||
|
pop ebp
|
||
|
pop edx
|
||
|
pop ecx
|
||
|
ret 8
|
||
|
EOS
|
||
|
|
||
|
def hash_name(sym)
|
||
|
hash = 0
|
||
|
sym.each_byte { |char|
|
||
|
hash = (((hash >> 0xd) | (hash << (32-0xd))) + char) & 0xffff_ffff
|
||
|
}
|
||
|
hash
|
||
|
end
|
||
|
|
||
|
def lib_name(sym)
|
||
|
raise "unknown libname for #{sym}" if not lib = Metasm::WindowsExports::EXPORT[sym]
|
||
|
n = lib.downcase[0, 4].unpack('C*')
|
||
|
n[0] + (n[1]<<8) + (n[2] << 16) + (n[3] << 24)
|
||
|
end
|
||
|
|
||
|
# encode stub for each symbol
|
||
|
ext_syms.uniq.each { |sym|
|
||
|
next if sym == 'next_payload'
|
||
|
sc.parse <<EOS
|
||
|
#{sym}:
|
||
|
push #{lib_name(sym)}
|
||
|
push #{hash_name(sym)}
|
||
|
call resolve_proc
|
||
|
jmp eax
|
||
|
EOS
|
||
|
}
|
||
|
|
||
|
# marker to the next payload if the payload is a stager
|
||
|
sc.assemble "next_payload:"
|
||
|
|
||
|
# output to a file
|
||
|
sc.encode_file 'shellcode-dynlink.raw'
|
||
|
|
||
|
__END__
|
||
|
// sample payload
|
||
|
|
||
|
extern __stdcall int MessageBoxA(int, char*, char*, int);
|
||
|
extern void next_payload(void);
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
MessageBoxA(0, "Hello, world !", "Hi", 0);
|
||
|
next_payload();
|
||
|
}
|