class ApiHook
Attributes
dbg[RW]
Public Class Methods
new(dbg)
click to toggle source
initialized from a Debugger or a process description that will be debugged sets the hooks up, then run_forever
# File samples/dbg-apihook.rb, line 32 def initialize(dbg) if not dbg.kind_of? Metasm::Debugger process = Metasm::OS.current.find_process(dbg) raise 'no such process' if not process dbg = process.debugger end @dbg = dbg begin setup.each { |h| setup_hook(h) } init_prerun if respond_to?(:init_prerun) # allow subclass to do stuff before main loop @dbg.run_forever rescue Interrupt @dbg.detach #rescue nil end end
Public Instance Methods
finish(retval)
click to toggle source
skip the function call only valid in pre_hook
# File samples/dbg-apihook.rb, line 162 def finish(retval) patch_ret(retval) @dbg.ip = @dbg.func_retaddr case @cur_abi when :fastcall @dbg[:esp] += 4*(@nargs-2) if @nargs > 2 when :thiscall @dbg[:esp] += 4*(@nargs-1) if @nargs > 1 when :stdcall @dbg[:esp] += 4*@nargs end @dbg.sp += @dbg.cpu.size/8 throw :finish end
patch_arg(nr, value)
click to toggle source
patch the value of an argument only valid in pre_hook nr starts at 0
# File samples/dbg-apihook.rb, line 124 def patch_arg(nr, value) case @cur_abi when :fastcall case nr when 0 @dbg.set_reg_value(:ecx, value) return when 1 @dbg.set_reg_value(:edx, value) return else nr -= 2 end when :thiscall case nr when 0 @dbg.set_reg_value(:ecx, value) return else nr -= 1 end end @dbg.func_arg_set(nr, value) end
patch_ret(val)
click to toggle source
patch the function return value only valid post_hook
# File samples/dbg-apihook.rb, line 152 def patch_ret(val) if @ret_longlong @dbg.set_reg_value(:edx, (val >> 32) & 0xffffffff) val &= 0xffffffff end @dbg.func_retval_set(val) end
read_arglist()
click to toggle source
retrieve the arglist at func entry, from @nargs & @cur_abi
# File samples/dbg-apihook.rb, line 93 def read_arglist nr = @nargs args = [] if (@cur_abi == :fastcall or @cur_abi == :thiscall) and nr > 0 args << @dbg.get_reg_value(:ecx) nr -= 1 end if @cur_abi == :fastcall and nr > 0 args << @dbg.get_reg_value(:edx) nr -= 1 end nr.times { |i| args << @dbg.func_arg(i) } args end
read_ret()
click to toggle source
retrieve the function returned value
# File samples/dbg-apihook.rb, line 113 def read_ret ret = @dbg.func_retval if @ret_longlong ret = (ret & 0xffffffff) | (@dbg[:edx] << 32) end ret end
setup()
click to toggle source
rewrite this function to list the hooks you want return an array of hashes
# File samples/dbg-apihook.rb, line 24 def setup #[{ :function => 'WriteFile', :abi => :stdcall }, # standard function hook # { :module => 'Foo.dll', :rva => 0x2433, # arbitrary code hook # :abi => :fastcall, :hookname => 'myhook' }] # hooks named pre_myhook/post_myhook end
setup_hook(h)
click to toggle source
setup one function hook
# File samples/dbg-apihook.rb, line 49 def setup_hook(h) @las ||= false if not h[:lib] and not @las @dbg.loadallsyms @las = false elsif h[:lib] # avoid loadallsyms if specified (regexp against pathname, not exported lib name) @dbg.loadsyms(h[:lib]) end pre = "pre_#{h[:hookname] || h[:function]}" post = "post_#{h[:hookname] || h[:function]}" nargs = h[:nargs] || method(pre).arity if respond_to?(pre) if target = h[:address] elsif target = h[:rva] modbase = @dbg.modulemap[h[:module]] raise "cant find module #{h[:module]} in #{@dbg.modulemap.join(', ')}" if not modbase target += modbase[0] else target = h[:function] end @dbg.bpx(target, false, h[:condition]) { @nargs = nargs catch(:finish) { @cur_abi = h[:abi] @ret_longlong = h[:ret_longlong] if respond_to? pre args = read_arglist send pre, *args end if respond_to? post @dbg.bpx(@dbg.func_retaddr, true) { retval = read_ret send post, retval, args } end } } end