# 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 the image name) # we will retrieve the user32.dll library mapped, and hook every exported function. # each hook will redirect the code flow to our shellcode, which will display the hooked function # name in a messagebox. # The hook is this time a real hook: we overwrite the first instructions with a jump to our code, # and run those overwritten instruction again before giving control back to original function. # # usage: ruby w32hook-advance.rb notepad # use ruby -d to impress your friends :) # require 'metasm' include Metasm include WinAPI # open target WinAPI.get_debug_privilege if not pr = WinAPI.find_process((Integer(ARGV.first) rescue ARGV.first)) # display list of running processes and exit puts WinAPI.list_processes.sort_by { |pr| pr.pid }.map { |pr| "#{pr.pid}: #{File.basename(pr.modules.first.path) rescue nil}" } exit end raise 'cannot open target process' if not handle = WinAPI.openprocess(PROCESS_ALL_ACCESS, 0, pr.pid) # virtual mapping of remote process memory remote_mem = WindowsRemoteString.new(handle) # the main shellcode sc = Shellcode.assemble Ia32.new, <= our hook length mpe.encoded.ptr = export.target sz = 0 overwritten = [] while sz < hooks[target].length di = sc.cpu.decode_instruction mpe.encoded, target if not di or not di.opcode or not di.instruction puts "W: unknown instruction in #{export.name} !" break end overwritten << di.instruction sz += di.bin_length end puts "overwritten at #{export.name}:", overwritten, '' if $DEBUG resumeaddr = target + sz # append the call-specific shellcode to the main hook code sc.cursource << Label.new(hooklabel) sc.parse <= text.virtaddr + text.virtsize # prepare the hook prepare_hook[mpe, m.addr, e] } } raise 'Did not find MessageBoxW !' if not msgboxw puts 'linking...' sc.assemble puts 'done' # allocate memory for our code raise 'remote allocation failed' if not injected_addr = WinAPI.virtualallocex(handle, 0, sc.encoded.length, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) puts "Injecting hooks at #{'%x' % injected_addr}" # fixup & inject our code binding = { 'messageboxw' => msgboxw } hooks.each { |addr, edata| binding.update edata.binding(addr) } binding.update sc.encoded.binding(injected_addr) # fixup sc.encoded.fixup(binding) # inject remote_mem[injected_addr, sc.encoded.data.length] = sc.encoded.data # now overwrite entry points hooks.each { |addr, edata| edata.fixup(binding) remote_mem[addr, edata.data.length] = edata.data } puts 'done' WinAPI.closehandle(handle)