-
-
[原创]复习arm64指令架构上通过ptrace安装硬件断点的源码流程
-
发表于:
2025-8-11 11:08
7821
-
[原创]复习arm64指令架构上通过ptrace安装硬件断点的源码流程
前些天有些讨论提到了安装硬件断点的相关操作,会出现用户进程出错退出的情况,所以这里从源码里复习一下怎么在arm64机器上注册硬件断点。
用户态空间安装硬件断点最常使用的是PTRACE_SETREGSET,指定寄存器集合为NT_ARM_HW_BREAK的一系列读写,从源码kernel/ptrace.c中可以观察到,实际PTRACE_SETREGSET时候只是调用到了跨平台写法
ptrace_regset
其中task_user_regset_view会返回架构相关信息。这里内部使用的是arch/arm64/ptrace.c中定义的user_aarch64_view,内含aarch64_regsets
其中对dbg系列寄存器定义了hw_break_get/hw_break_set来注册断点回调,核心调用流程为
ptrace_hbp_set_addr
ptrace_hbp_set_ctrl
那么这样一个硬件断点在ptrace的流程中就安装完成了,实际用户进程触发时便会自动调用ptrace_hbptriggered处理信号,接下来看这个回调怎么触发
ptrace_hbptriggered回调的起始与完结点:
可以看到ptrace这套接口其实很方便地把指令集中的同步异常转化为了被trace进程中的信号,被tracer异步唤醒处理。挺优雅的,具备很强的跨平台通用性,就是整体效率上不算很高。如果直接在处理同步异常时刻不挂起信号,而是直接进入到同线程用户态或内核态的handler处理动作,可以有更好的效率。
目前为了解决这个handler的灵活度可以用内核lua接口绑定常用的寄存器读取与内核读取的c接口来实现,感兴趣的朋友可以关注更新。
const struct user_regset_view *view = task_user_regset_view(task);
const struct user_regset *regset = find_regset(view, type);
const struct user_regset_view *view = task_user_regset_view(task);
const struct user_regset *regset = find_regset(view, type);
1 2 | const struct user_regset_view *view = task_user_regset_view(task);
const struct user_regset *regset = find_regset(view, type);
|
ptrace_hbp_get_initialised_bp
ptrace_hbp_create
ptrace_breakpoint_init新建断点结构体
register_user_hw_breakpoint hw_breadkpoint.h 注册断点事件,包含回调为ptrace_hbptriggered
ptrace_hbp_set_event写入线程的调试寄存器
tsk->thread.debug.hbp_break[idx] = bp;
tsk->thread.debug.hbp_watch[idx] = bp;
modify_user_hw_breakpoint in hw_breadkpoint.h控制修改断点信息
ptrace_hbp_create
ptrace_breakpoint_init新建断点结构体
register_user_hw_breakpoint hw_breadkpoint.h 注册断点事件,包含回调为ptrace_hbptriggered
ptrace_hbp_set_event写入线程的调试寄存器
tsk->thread.debug.hbp_break[idx] = bp;
tsk->thread.debug.hbp_watch[idx] = bp;
ptrace_breakpoint_init新建断点结构体
register_user_hw_breakpoint hw_breadkpoint.h 注册断点事件,包含回调为ptrace_hbptriggered
ptrace_hbp_set_event写入线程的调试寄存器
tsk->thread.debug.hbp_break[idx] = bp;
tsk->thread.debug.hbp_watch[idx] = bp;
tsk->thread.debug.hbp_break[idx] = bp;
tsk->thread.debug.hbp_watch[idx] = bp;
- 由执行指令(breakpoint)或访问内存(watchpoint)踩到硬件断点异常入口点位于中断处理入口entry.S
el0_dbg位置,也即用户态调试异常,调用流程为
do_debug_exception,
- 关键1.fault信息的获取位置
const struct fault_info *inf = debug_fault_info + DBG_ESR_EVT(esr);
- 关键2.fault回调函数的入口
if (!inf->fn(addr_if_watchpoint, esr, regs)) 这个在arch/arm64/hw_breakpoint.c中被hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP,TRAP_HWBKPT, "hw-breakpoint handler");设置为了使用breakpoint_handler处理
perf_bp_event(bp, regs)为调用回调的perf事件入口点
- 兜兜转转会到达
__perf_event_overflow(event, throttle, data, regs))核心调用
READ_ONCE(event->overflow_handler)(event, data, regs);=(等效于)=>此处调用到了event->overflow_handler即注册的ptrace_hbptriggered函数,其中唯一的关键是:
- 为用户态进程安装一个trap信号,以供tracer捕获信息
force_sig_info(SIGTRAP, &info, current); info包含了断点踩踏的地址(void __user *)(bkpt->trigger),作为触发参数。
do_debug_exception,
- 关键1.fault信息的获取位置
const struct fault_info *inf = debug_fault_info + DBG_ESR_EVT(esr);
- 关键2.fault回调函数的入口
if (!inf->fn(addr_if_watchpoint, esr, regs)) 这个在arch/arm64/hw_breakpoint.c中被hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP,TRAP_HWBKPT, "hw-breakpoint handler");设置为了使用breakpoint_handler处理
perf_bp_event(bp, regs)为调用回调的perf事件入口点
- 兜兜转转会到达
__perf_event_overflow(event, throttle, data, regs))核心调用
READ_ONCE(event->overflow_handler)(event, data, regs);=(等效于)=>此处调用到了event->overflow_handler即注册的ptrace_hbptriggered函数,其中唯一的关键是:
- 为用户态进程安装一个trap信号,以供tracer捕获信息
force_sig_info(SIGTRAP, &info, current); info包含了断点踩踏的地址(void __user *)(bkpt->trigger),作为触发参数。
- 关键1.fault信息的获取位置
const struct fault_info *inf = debug_fault_info + DBG_ESR_EVT(esr);
- 关键2.fault回调函数的入口
if (!inf->fn(addr_if_watchpoint, esr, regs)) 这个在arch/arm64/hw_breakpoint.c中被hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP,TRAP_HWBKPT, "hw-breakpoint handler");设置为了使用breakpoint_handler处理
perf_bp_event(bp, regs)为调用回调的perf事件入口点
- 兜兜转转会到达
__perf_event_overflow(event, throttle, data, regs))核心调用
READ_ONCE(event->overflow_handler)(event, data, regs);=(等效于)=>此处调用到了event->overflow_handler即注册的ptrace_hbptriggered函数,其中唯一的关键是:
- 为用户态进程安装一个trap信号,以供tracer捕获信息
force_sig_info(SIGTRAP, &info, current); info包含了断点踩踏的地址(void __user *)(bkpt->trigger),作为触发参数。
perf_bp_event(bp, regs)为调用回调的perf事件入口点
- 兜兜转转会到达
__perf_event_overflow(event, throttle, data, regs))核心调用
READ_ONCE(event->overflow_handler)(event, data, regs);=(等效于)=>此处调用到了event->overflow_handler即注册的ptrace_hbptriggered函数,其中唯一的关键是:
- 为用户态进程安装一个trap信号,以供tracer捕获信息
force_sig_info(SIGTRAP, &info, current); info包含了断点踩踏的地址(void __user *)(bkpt->trigger),作为触发参数。
[招生]科锐逆向工程师培训(2026年7月3日实地,远程教学同时开班, 第56期)!