metasploit-framework/lib/metasm/samples/win32hooker.rb

98 lines
2.6 KiB
Ruby
Raw Normal View History

# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2007 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
#
# in this exemple we will patch a process specified on the commandline (pid or part of image name)
# the IAT entry matching /WriteFile/ will be replaced by a pointer to a malicious code we inject,
# which calls back the original function.
# Our shellcode will display the first bytes of the data to be written, using MessageBoxW (whose
# pointer is also retrieved from the target IAT)
#
# usage: ruby w32hook.rb notepad ; then go in notepad, type some words and save to a file
#
require 'metasm'
include Metasm
include WinAPI
# open target
WinOS.get_debug_privilege
if not pr = WinOS.find_process(ARGV.first)
# display list of running processes if no target found
puts WinOS.list_processes.sort_by { |pr_| pr_.pid }
exit
end
raise 'cannot open target process' if not pr.handle
# read the target PE structure
pe = LoadedPE.load pr.memory[pr.modules[0].addr, 0x1000000]
pe.decode_header
pe.decode_imports
# find iat entries
target = nil
target_p = nil
msgboxw_p = nil
iat_entry_len = pe.encode_xword(0).length # 64bits portable ! (shellcode probably won't work)
pe.imports.each { |id|
id.imports.each_with_index { |i, idx|
case i.name
when 'MessageBoxW'
msgboxw_p = pr.modules[0].addr + id.iat_p + iat_entry_len * idx
when /WriteFile/
target_p = pr.modules[0].addr + id.iat_p + iat_entry_len * idx
target = id.iat[idx]
end
}
}
raise "iat entries not found" if not target or not msgboxw_p
# here we write our shellcode (no need to code position-independant)
sc = Shellcode.assemble(Ia32.new, <<EOS)
pushad
mov esi, dword ptr [esp+20h+8] ; 2nd arg = buffer
mov edi, message
mov ecx, 19
xor eax, eax
copy_again:
lodsb
stosw
loop copy_again
push 0
push title
push message
push 0
call [msgboxw]
popad
jmp target
.align 4
; strings to display
message dw 20 dup(?)
title dw 'I see what you did there...', 0
EOS
# alloc some space in the remote process to put our shellcode
raise 'remote allocation failed' if not injected = WinAPI.virtualallocex(pr.handle, 0, sc.encoded.length, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
puts "injected malicous code at %x" % injected
# fixup the shellcode with its known base address, and with the addresses it will need from the IAT
sc.base_addr = injected
sc.encoded.fixup! 'msgboxw' => msgboxw_p, 'target' => target
raw = sc.encode_string
# inject the shellcode
pr.memory[injected, raw.length] = raw
# rewrite iat entry
iat_h = pe.encode_xword(injected).data
pr.memory[target_p, iat_h.length] = iat_h
# done
WinAPI.closehandle(pr.handle)