IDA调试器hook

一,一些api

idaapi.DBG_Hooks

idaapi中用来调试器hook的类,

用来处理断点,这也是基础

可以这样用

tid是线程ip

ea是当前指令地址

calss aaa(ida_dbg.DBG_Hooks):
    #可选
    def __init__(self):
        super().__init__()
        ##一下是你自定义的变量
    #以下是这个类的内置函数,可以选择性重写
    def dbg_process_start(self, pid, tid, ea, name, base, size):
        """进程启动时调用"""
        print(f"Process started: {name}")
        return 0
        
    def dbg_process_exit(self, pid, tid, ea, code):
        """进程退出时调用"""
        print(f"Process exited with code: {code}")
        return 0
        
    def dbg_thread_start(self, pid, tid, ea):
        """线程启动时调用"""
        print(f"Thread started: {tid}")
        return 0
        
    def dbg_thread_exit(self, pid, tid, ea):
        """线程退出时调用"""
        print(f"Thread exited: {tid}")
        return 0
        
    def dbg_library_load(self, pid, tid, ea, name, base, size):
        """库加载时调用"""
        print(f"Library loaded: {name}")
        return 0
        
    def dbg_library_unload(self, pid, tid, ea, info):
        """库卸载时调用"""
        print(f"Library unloaded")
        return 0
        
    def dbg_bpt(self, tid, ea):###############################常用
        """断点命中时调用"""
        print(f"Breakpoint at: 0x{ea:X}")
        return 0
        
    def dbg_trace(self, tid, ea):
        """跟踪时调用"""
        return 0
        
    def dbg_step_into(self):
        """单步进入后调用"""
        return 0
        
    def dbg_step_over(self):
        """单步跳过后调用"""
        return 0
        
    def dbg_run_to(self, pid, tid, ea):
        """运行到指定地址后调用"""
        return 0
        
    def dbg_exception(self, pid, tid, ea, exc_code, exc_can_cont, exc_ea, exc_info):
        """异常发生时调用"""
        return 0

idc.get_name_ea_simple( "name")

获取名为name函数的地址,并返回

idc.get_func_attr(addr_start, idc.FUNCATTR_END)

这个函数用来获取函数的结束地址

addr_start是函数的起始地址,可以通过get_name_ea那个函数获得,或者自己从IDA中找

idc.FUNCATTR_END是个固定量,不用管

这个指令的返回值减一就是最后一条指令的地址,所以使用时一定要减一

idc.get_reg_value("name")

获取当前寄存器名为name的值

比如你要获取eax的值就改为eax

你要获取ebx就改为ebx

idc.add_bpt(addr, 0, idc.BPT_SOFT)

由于调试器只能对下断点的地方有用,所以一定要加上断点这个hook才能起作用

设置断点,在addr地址处,

0:断点大小,(0表示默认)

idc.BPT_SOFT:断点类型(软件断点)

二,模板

一,获取函数输入参数和返回值:

我用C写了这样的代码:

我想要在调用add函数时获取起输入参数和返回值

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int main(){
    printf("begin\n");
    printf("%d\n", add(1, 2));
    printf("%d\n", add(3, 4));
    printf("%d\n", add(5, 6));
    int a,b;
    printf(" first------     input two number:\n");
    scanf("%d %d", &a, &b);
    printf("%d\n", add(a, b));
    printf(" second------     input two number:\n");
    scanf("%d %d", &a, &b);
    printf("%d\n", add(a, b));
    int c,d;
    c=6;
    d=7;
    printf("%d\n", add(c, d));
    printf(" end");
return 0;
}

IDA显示的伪代码:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int v4; // eax
  int v5; // eax
  int v6; // eax
  int v7; // eax
  int v8; // eax
  unsigned int v10; // [rsp+20h] [rbp-10h] BYREF
  _DWORD v11[3]; // [rsp+24h] [rbp-Ch] BYREF

  _main(argc, argv, envp);
  puts_0("begin");
  v3 = add(1, 2);
  printf("%d\n", v3);
  v4 = add(3, 4);
  printf("%d\n", v4);
  v5 = add(5, 6);
  printf("%d\n", v5);
  puts_0(" first------     input two number:");
  scanf("%d %d", v11, &v10);
  v6 = add(v11[0], v10);
  printf("%d\n", v6);
  puts_0(" second------     input two number:");
  scanf("%d %d", v11, &v10);
  v7 = add(v11[0], v10);
  printf("%d\n", v7);
  v11[2] = 6;
  v11[1] = 7;
  v8 = add(6, 7);
  printf("%d\n", v8);
  printf(" end");
  return 0;
}

看一下add函数的汇编

; Attributes: bp-based frame

; __int64 __fastcall add(int, int)
public add
add proc near

arg_0= dword ptr  10h
arg_8= dword ptr  18h

push    rbp
mov     rbp, rsp
mov     [rbp+arg_0], ecx
mov     [rbp+arg_8], edx
mov     edx, [rbp+arg_0]
mov     eax, [rbp+arg_8]
add     eax, edx
pop     rbp
retn
add endp

得知其使用ecx,和edx寄存器传入参数,用eax返回结果,所以我们要收集这些内容

脚本

import idaapi
import idc
class Addhook(idaapi.DBG_Hooks):
    def __init__(self):
        super().__init__()
        self.count=0#调用次数
        self.input=[]#输入参数的地方
        self.ret=[]#返回值的地方
        self.add_start_addr=idc.get_name_ea_simple( "add")#获取add函数的起始地址
        self.add_end_addr=idc.get_func_attr(self.add_start_addr, idc.FUNCATTR_END)-1#获取add函数的结束地址
    def dbg_bpt(self, tid,ea):#断点命中
        if ea==self.add_start_addr:#命中add函数的起始地址
            self.count +=1#调用次数加1
            arg1=idc.get_reg_value("ECX")#获取参数1
            arg2=idc.get_reg_value("EDX")#获取参数2
            self.input.append((self.count,arg1,arg2))#添加次数,参数1和参数2
            print("第%d次调用add函数,参数为%d,%d"%(self.count,arg1,arg2))
        if ea==self.add_end_addr:
            ret=idc.get_reg_value("EAX")#获取返回值
            self.ret.append((self.count,ret))#添加次数,返回值
            print("第%d次调用add函数,返回值为%d"%(self.count,ret))
        return 0
    def dbg_process_exit(self, pid,tid,ea,code):#进程退出时除非
        print("进程退出")
        print("\n"+"="*80)
        print("结果")
        print("\n"+"="*80)
        
        for item in self.input:#遍历输入参数
            count,arg1,arg2=item
            print("第%d次调用add函数,参数为%d,%d"%(count,arg1,arg2,))
        print("\n"+"="*80)
        for item in self.ret:#遍历返回值
            count,ret=item
            print("第%d次调用add函数,返回值为%d"%(count,ret,))
        return 0
        
debug_hook=Addhook()#创建hook对象
debug_hook.hook()#安装hook
add_start=idc.get_name_ea_simple("add")#获取add函数的起始地址
add_end=idc.get_func_attr(add_start, idc.FUNCATTR_END)-1#获取add函数的结束地址
idc.add_bpt(add_start,0,idc.BPT_SOFT)#添加断点
idc.add_bpt(add_end,0,idc.BPT_SOFT)#添加断点
print("添加断点成功,hook启动成功")#启动成功

给每一个地方添加了注释,没什么难以理解的

就是在最后一定要创建对象,然后对对象使用hook()方法,然后下断点,我这个是使用指令下断点了,如果不下断点这个hook是不能触发的

output输出

第1次调用add函数,返回值为3
第2次调用add函数,参数为3,4
第2次调用add函数,返回值为7
第3次调用add函数,参数为5,6
第3次调用add函数,返回值为11
第4次调用add函数,参数为123,456
第4次调用add函数,返回值为579
第5次调用add函数,参数为48961,49416
第5次调用add函数,返回值为98377
第6次调用add函数,参数为6,7
第6次调用add函数,返回值为13
7FFC48F80000: loaded C:\WINDOWS\SYSTEM32\kernel.appcore.dll
7FFC4ADF0000: loaded C:\WINDOWS\System32\msvcrt.dll
Debugger: thread 40516 has exited (code 0)
Debugger: process has exited (exit code 0)
进程退出

================================================================================
结果

================================================================================
第1次调用add函数,参数为1,2
第2次调用add函数,参数为3,4
第3次调用add函数,参数为5,6
第4次调用add函数,参数为123,456
第5次调用add函数,参数为48961,49416
第6次调用add函数,参数为6,7
第1次调用add函数,返回值为3
第2次调用add函数,返回值为7
第3次调用add函数,返回值为11
第4次调用add函数,返回值为579
第5次调用add函数,返回值为98377
第6次调用add函数,返回值为13

二,修改输入和返回值

添加这个函数就行了

set_reg_value(number value, string name);

但是我每次用都报错,虽然结果是我想要的

三获取寄存器的值

例如下面这个脚本,我们要获取地址为0x08049356处地址中寄存器的值,那我们直接让ea ==那个地址就行了,其他操作和之前一样

import ida_dbg
import idc

class MyDbgHook(ida_dbg.DBG_Hooks):
    """
    一个自定义的调试器事件处理钩子,用于在断点触发时执行操作。
    """
    def dbg_bpt(self, thread, ea):
        """
        当断点命中时,此函数会被自动调用。
        :param ea: 断点所在的地址
        """
        # 检查断点是否在我们关心的地址
        if ea == 0x08049356: # 请替换为您关注的具体地址
            print(f">>>>> 断点命中在地址 {hex(ea)}")
            
            # 获取当前线程的寄存器值
            eax_value = idc.get_reg_value("EAX")
            ebx_value = idc.get_reg_value("EBX")
            
            print(f">>>>> 此时,EAX = 0x{eax_value:x}")
            print(f">>>>> 此时,EBX = 0x{ebx_value:x}")
            
            # 您可以在这里添加更多逻辑,比如记录、条件判断等
            
        # 继续执行父类的方法
        return super().dbg_bpt(thread, ea)

# 注册调试钩子
try:
    my_dbg_hook = MyDbgHook()
    my_dbg_hook.hook()
    print("调试钩子安装成功。")
except Exception as e:
    print(f"安装调试钩子时出错: {e}")

posted @ 2025-12-01 15:03  漫宿骄盛  阅读(16)  评论(0)    收藏  举报