之前在帖子《古墓派反native混淆方案:实机调试》里提到的一套用于trace代码调用流程的一套算法,基于ptrace接口。
曾在各种BR跳转混淆库,以及臭名昭著某n_o_r_msg上跑出完整调用流程,在此开源下部分demo代码。
以下c代码组合一把梭哈编译成可执行文件便可实现一个HWBP-trace-server
核心trace逻辑代码:后面看情况补充注释更新一下
以下为包含主次进程生命流程代码,较为简单
最后写文件生成的trace文件示例,用以结合ida静态分析原始so库与内存dump文件
// ---------- ↓↓↓↓ 需要自己去实现的逻辑,暂不开源 ↓↓↓↓------------------
//内核接口:标志当前线程为server线程
void xxxx_setselfengine(void);
//内核接口:获取当前标记uid的、调用完ptrace_me陷入睡眠的线程
void xxxx_fetchOne(struct trace_info* &info);
//内核接口:声明当前线程为 线程[pid] 的专属tracer线程
void xxxx_markSelfTracer(int pid);
// 解析当前进程pc 地址后 2048长度内的指令(512条指令),获取下一条 跳转类 指令
void do_find_next_jump(unsigned long* next_pc);
// ---------- ↑↑↑↑ 需要自己去实现的逻辑,暂不开源 ↑↑↑↑ ------------------
// 进程静态变量
// 是否是 bl blr类link跳转
static bool wrap_call=false;
// 是否是ret类型的跳转
static bool ret_call=false;
// 上一个安装的断点地址
static unsigned long last_hw_bp=0;
// 上一个安装 ret的断点地址
static unsigned long last_hw_bp_ret=0;
// 调试线程号
static int traced_tid;
// trace最大深度
// link调用深度
static int call_depth = -MAX_TRACE_DEPTH;
// 入口点处的初始调用深度记录值,不变
static int orig_depth = -MAX_TRACE_DEPTH;
// ---------- ↓↓↓↓ 需要自己去实现的逻辑,暂不开源 ↓↓↓↓------------------
//内核接口:标志当前线程为server线程
void xxxx_setselfengine(void);
//内核接口:获取当前标记uid的、调用完ptrace_me陷入睡眠的线程
void xxxx_fetchOne(struct trace_info* &info);
//内核接口:声明当前线程为 线程[pid] 的专属tracer线程
void xxxx_markSelfTracer(int pid);
// 解析当前进程pc 地址后 2048长度内的指令(512条指令),获取下一条 跳转类 指令
void do_find_next_jump(unsigned long* next_pc);
// ---------- ↑↑↑↑ 需要自己去实现的逻辑,暂不开源 ↑↑↑↑ ------------------
// 进程静态变量
// 是否是 bl blr类link跳转
static bool wrap_call=false;
// 是否是ret类型的跳转
static bool ret_call=false;
// 上一个安装的断点地址
static unsigned long last_hw_bp=0;
// 上一个安装 ret的断点地址
static unsigned long last_hw_bp_ret=0;
// 调试线程号
static int traced_tid;
// trace最大深度
// link调用深度
static int call_depth = -MAX_TRACE_DEPTH;
// 入口点处的初始调用深度记录值,不变
static int orig_depth = -MAX_TRACE_DEPTH;
// ---------- ↓↓↓↓ 待补充和整理注释,这会有点写不动了 ↓↓↓↓------------------
int call_flow_analyze_do(bool norange){
unsigned long pc=0;
//获取目标线程当前pc后第一个跳转类指令的地址
unsigned long next_j_addr = do_find_next_jump(&pc);
// why do we stop in last phase?
//
//reasons:
if(pc==last_hw_bp){
// reason 1: hit at setted jump bp
if(PRINT_CMDLINE)printf("\t\t------hbp stop: 0x%lx\n",pc);
ptrace_remove_single_hwbp_at(traced_tid,pc);
//未设置返回暂停,返回了
if(ret_call&&last_hw_bp_ret==0){
printf("\t\t------no hbp ret: 0x%lx\n",pc);
printf("dep-1\n");
call_depth-=1;
}
if(wrap_call){
call_depth+=1;
printf("dep+1\n");
}
//单步执行
goto singlestep;
}else if(pc==last_hw_bp_ret&&last_hw_bp_ret!=0){
// reason 2: hit at a ret bp
printf("\t\t------hbp ret: 0x%lx\n",pc);
ptrace_remove_single_hwbp_at2(traced_tid,pc);
last_hw_bp_ret=0;
printf("dep-1\n");
call_depth-=1;
if(pc==next_j_addr){
if(wrap_call){
printf("dep+1\n");
call_depth+=1;
}
if(ret_call){
//next
printf("dep-1\n");
call_depth-=1;
}
goto singlestep;
}
goto docont_wbp;
}else{
// reason 3: single stepped
if(PRINT_CMDLINE)printf("\t\t------not a hbp, maybe step\n");
// now we check deep call after singlestep
if(call_depth >=0 && last_hw_bp_ret>0){
//too deep
doLogPcDeep(pc - so_range_start );//just log offset
if(!do_cont(traced_tid)){
printf("ptrace_cont fail\n");
}
printf("\t\t---wait from sea---\n\n");
return 0;
}
// check
// we are out of range and we can trace link back
if(last_hw_bp_ret>0&&(pc>so_range_end||pc<so_range_start)){\
doLogPcOut(pc);
//just continue it
if(!do_cont(traced_tid))
if(PRINT_CMDLINE)printf("ptrace_cont fail\n");
printf("\t\t---wait for comingback---\n\n");
return 0;
}else if((pc>so_range_end||pc<so_range_start) && last_hw_bp_ret==0){
if(!do_cont(traced_tid))
if(PRINT_CMDLINE)printf("ptrace_cont fail\n");
printf("\t\t---trace must be ended---\n\n");
return 1;
}
// we steped on a call
if(pc==next_j_addr){
if(wrap_call){
printf("dep+1\n");
call_depth+=1;
}
if(ret_call&&last_hw_bp_ret==0){
//next
printf("dep-1\n");
call_depth-=1;
}
goto singlestep;
}
goto docont_wbp;
}
// next phase
singlestep:
if(call_depth<=orig_depth){
//we are out of trace reange
doLogPc(pc - so_range_start );
printf("\t\t---out of functions---\n\n");
return 1;
}
if(pc>so_range_start&&pc<so_range_end&&!norange)
doLogPc(pc - so_range_start );
if(norange)
doLogPc(pc);
if(PRINT_CMDLINE)printf("\t\tdocont------wrap:%d ret:%d \n",wrap_call?1:0,ret_call?1:0);
if(wrap_call){
// just set return bp
if(ptrace_install_single_hwbp_at3(traced_tid,next_j_addr)){
last_hw_bp_ret=next_j_addr+4;// update
if(PRINT_CMDLINE)printf("\t\t------set hbp ret before: 0x%lx\n",last_hw_bp_ret);
}else
printf("hbp set fail!!!\n");
}
// at branch point
printf("\t\t------single-step\n");
// do single step
if(!do_single_step(traced_tid))
printf("ptrace_single fail\n");
return 0;
// do continue with bp
docont_wbp:
// logpc
if(pc>so_range_start&&pc<so_range_end&&!norange)
doLogPc(pc - so_range_start );
if(norange)
doLogPc(pc);
if(PRINT_CMDLINE)printf("\t\tdocont------wrap:%d ret:%d \n",wrap_call?1:0,ret_call?1:0);
if(wrap_call){
if(ptrace_install_single_hwbp_at2(traced_tid,next_j_addr)){
last_hw_bp=next_j_addr;
last_hw_bp_ret=next_j_addr+4;// update
if(PRINT_CMDLINE)printf("\t\t------set hbp ret before: 0x%lx\n",last_hw_bp_ret);
}
else
printf("hbp set fail!!!\n");
}else{
if(PRINT_CMDLINE)printf("\t\t------set hbp before: 0x%lx\n",next_j_addr);
if(ptrace_install_single_hwbp_at(traced_tid,next_j_addr))
last_hw_bp=next_j_addr;// update
else
printf("hbp set fail!!!\n");
}
if(!do_cont(traced_tid))
printf("ptrace_cont fail\n");
printf("\t\t------cont\n");
return 0;
}
// ---------- ↑↑↑↑ 待补充和整理注释,这会有点写不动了 ↑↑↑↑ ------------------
// ---------- ↓↓↓↓ 待补充和整理注释,这会有点写不动了 ↓↓↓↓------------------
int call_flow_analyze_do(bool norange){
unsigned long pc=0;
//获取目标线程当前pc后第一个跳转类指令的地址
unsigned long next_j_addr = do_find_next_jump(&pc);
// why do we stop in last phase?
//
//reasons:
if(pc==last_hw_bp){
// reason 1: hit at setted jump bp
if(PRINT_CMDLINE)printf("\t\t------hbp stop: 0x%lx\n",pc);
ptrace_remove_single_hwbp_at(traced_tid,pc);
//未设置返回暂停,返回了
if(ret_call&&last_hw_bp_ret==0){
printf("\t\t------no hbp ret: 0x%lx\n",pc);
printf("dep-1\n");
call_depth-=1;
}
if(wrap_call){
call_depth+=1;
printf("dep+1\n");
}
//单步执行
goto singlestep;
}else if(pc==last_hw_bp_ret&&last_hw_bp_ret!=0){
// reason 2: hit at a ret bp
printf("\t\t------hbp ret: 0x%lx\n",pc);
ptrace_remove_single_hwbp_at2(traced_tid,pc);
last_hw_bp_ret=0;
printf("dep-1\n");
call_depth-=1;
if(pc==next_j_addr){
if(wrap_call){
printf("dep+1\n");
call_depth+=1;
}
if(ret_call){
//next
printf("dep-1\n");
call_depth-=1;
}
goto singlestep;
}
goto docont_wbp;
}else{
// reason 3: single stepped
if(PRINT_CMDLINE)printf("\t\t------not a hbp, maybe step\n");
// now we check deep call after singlestep
if(call_depth >=0 && last_hw_bp_ret>0){
//too deep
doLogPcDeep(pc - so_range_start );//just log offset
if(!do_cont(traced_tid)){
printf("ptrace_cont fail\n");
}
printf("\t\t---wait from sea---\n\n");
return 0;
}
// check
// we are out of range and we can trace link back
if(last_hw_bp_ret>0&&(pc>so_range_end||pc<so_range_start)){\
doLogPcOut(pc);
//just continue it
if(!do_cont(traced_tid))
if(PRINT_CMDLINE)printf("ptrace_cont fail\n");
printf("\t\t---wait for comingback---\n\n");
return 0;
}else if((pc>so_range_end||pc<so_range_start) && last_hw_bp_ret==0){
if(!do_cont(traced_tid))
if(PRINT_CMDLINE)printf("ptrace_cont fail\n");
printf("\t\t---trace must be ended---\n\n");
return 1;
}
// we steped on a call
if(pc==next_j_addr){
if(wrap_call){
printf("dep+1\n");
call_depth+=1;
}
if(ret_call&&last_hw_bp_ret==0){
//next
printf("dep-1\n");
call_depth-=1;
}
goto singlestep;
}
goto docont_wbp;
}
// next phase
singlestep:
if(call_depth<=orig_depth){
//we are out of trace reange
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!