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