基于HOOK的Anti-debug调用点trace和Anti-anti

本文介绍了一种基于HOOK的反调试技术,通过Hook关键API函数,实现了调用点追踪及多种反反调试方法,如重定向文件读取路径、修改监控事件等。

转:http://bbs.pediy.com/showthread.php?p=1365668#post1365668


发一个之前写的辅助调试SO的小东西,限于水平难免会有错误之处,请各位大大批评斧正,小弟感激不尽。

同时,也欢迎各位大大讨论下反调试的技术和Anti方法,让也小弟学学
-------------------------------------------------------------------------------
基于HOOK的Anti-debug调用点trace和Anti-anti
一、  概述
相信动态调试过SO的坛友对Anti并不陌生,比如读取/proc/self/status,/proc/self/task/xxx/status、stat文件查看状态TracePid和PPid,读取wchan查看进程等待,添加notify,模拟器检测等等。经过各种掉坑之后,虽然知道了这些检测方法,但如果elf文件被畸形不能静态分析(修复又是另外一回事了),也只能动态调试在这些关键API处下断点,也免不了掉坑调试崩溃,再来点指令混淆的话,调试起来确实比较恶心。虽然在Android平台同样也存在linux常用的ltrace(库函数trace)和strace(系统调用和信号trace)工具,但不能满足调试需求,还有检测ltrace的调试。废话不多说,直接进入正题(限于水平,难免会有疏漏和错误之处,请各位大大斧正,小弟感激不尽)。

二、  调用点trace
注入Hook API函数,当然首选zygote进程是最方便的了(如果zygote不能注入的话,那就关文档去修改ROM吧)。HooK住函数之后,通常采用下面这种模式添加代码:
Xxx new_fun(xxx){
  Before_call();
  Old_fun();
  After_call();
}
相信许多都知道ARM函数的调用流程,这里再啰嗦下 ARM汇编如何调用函数:
直接调用:BL/BLX  _xxfun
函数指针调用:BL/BLX  Rx
某些混淆代码:mov lr, [pc, #xx], 计算Rx,Ldr PC, Rx等
外部函数调用:BL/BLX _plt表项,plt表中load got表存在的函数地址到PC
被调函数一般模式:
Stmxx {Rx-Rx, lr}  /push

ldmxx {Rx-rx, pc}  /pop

函数的返回地址保存在lr寄存器中,之后被被调函数存在放栈上。只要lr的值未被修改,那么就保存着调用点的下一条指令地址!只要获取lr的值,那么就能跟踪到调用点。
知道了lr保存了调用点的值,获取lr的值也要注意时机。调用函数时lr的值已经被存在放栈上,修改lr也无关紧要。无法确定被调函数是否存在显式地给lr赋值或者函数调用,那要保证lr不被修改,故在函数入口点就保存lr的值是最佳也是最简单的选择。获取lr的值可以通过汇编来实现:
#define GETLR(store_lr)  \
  __asm__ __volatile__(  \
    "mov %0, lr\n\t"  \
    :  "=r"(store_lr)  \
  )
则Hook函数模式:
Xxx new_fun(xxx){
  Unsigned lr;
  GETLR(lr)
  Before_call();
  Old_fun();
  After_call();
}

来个实例,简单起见只Hook fopen函数:
FILE* new_fopen(const char *path,const char * mode){
  unsigned lr;
  GETLR(lr);

  if(strstr(path, "status") != NULL){
    LOGD("[*] Traced-fopen Call function: 0x%x\n", lr);
    if(strstr(path, "task") != NULL){
      LOGD("[*] Traced-anti-task/status");
    }else
      LOGD("[*] Traced-anti-status");
  }else if(strstr(path, "wchan") != NULL){
    LOGD("[*] Traced-fopen Call function: 0x%x\n", lr);
    LOGD("[*] Traced-anti-wchan");
  } 
  return old_fopen(path, mode);
}
注入后,某APK输出:
点击图片以查看大图图片名称:	1.png查看次数:	1文件大小:	5.4 KB文件 ID :	97242
图 1 调用点
Logcat:
 点击图片以查看大图图片名称:	2.png查看次数:	2文件大小:	3.9 KB文件 ID :	97243
图2  trace
Trace到了这个检测点,大概就知道这个函数想干什么了。动态调试时,也好准备”跨坑”而不是”掉坑”了。
除了直接获得这些调用点过坑外,还可以组合一些其他功能。比如监控APK启动时mmap分配内存,直接dd,便于大致分析某段代码做了什么,配合mprotect能起到一定的效果。当然,不必担心会监控到linker加载SO时mmap的无用信息,因为linker自身实现了mmap函数。
static void* new_mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset){
  unsigned lr;
  void* base = NULL;
  
  GETLR(lr);
  base = old_mmap(start, length, prot, flags, fd, offset);
  if((flags & MAP_ANONYMOUS) == 0){  //文件映射
    char file_name[256];
    char buf[256];
    memset(buf, 0, 256);
    sprintf(file_name, "/proc/self/fd/%d", fd);
    if(readlink(file_name, buf, 256) < 0){
      LOGD("[E] Traced-mmap --> readlink %s error\n", file_name);
      goto _done;
    }
    LOGD("[*] Traced-mmap --> [file] start = %p, length = 0x%x, filename = %s, offset = 0x%x", 
      start, length, buf, offset);
  }else{  //内存映射
    LOGD("[*] Traced-mmap --> [mem] start = %p, length = 0x%x", 
      start, length);
  }
  LOGD("[*] Traced-mmap Call function: 0x%x, Ret address: 0x%x\n", lr, (unsigned)base);
_done:
  return base;
}
点击图片以查看大图图片名称:	3.png查看次数:	0文件大小:	4.1 KB文件 ID :	97244
图 3
至于还能做什么,那就靠各位读者自行研究了吧。附上一些常见anti trace。

三、  Anti-anti
讨论一些常见的基于Hook的Anti-anti方法,欢迎讨论。
1、  status和stat
status和stat的Anti-anti方式类似,通过Hook fopen实现重定向到/data/local/tmp目录下:
sprintf(re_path, "/data/local/tmp/status");
if(!HasGenFile(re_path)){
  char buffer[BUFFERSIZE];
  FILE *fpr, *fpw;
  fpr = old_fopen(path, "r");
  fpw = old_fopen(re_path, "w");
  if(fpr == NULL || fpw == NULL){
    LOGD("[E] re-path [%s]failed", path);
    return old_fopen(path, mode);
  }
  while(fgets(buffer, BUFFERSIZE, fpr) != NULL){
    if(strstr(buffer, "State") != NULL){
      fputs("State:\tS (sleeping)\n", fpw);
    }
    if(strstr(buffer, "TracerPid") != NULL){
      fputs("TracerPid:\t0\n", fpw);
    }else{
      fputs(buffer, fpw);
    }
  }
  fclose(fpr);
  fclose(fpw);
}
2、  wchan
和status类似,只是重定向时,将等待事件设置为sys_epoll_wait:
if(strstr(path, "wchan") != NULL){
  LOGD("[*] Anti-anti-wchan!");
  strcpy(re_path, "data/local/tmp/wchan");
  if(!HasGenFile(re_path)){
    FILE *fpw;
    fpw = old_fopen(re_path, "w");
    if(fpw == NULL){
      LOGE("[E] re-path wchan failed!");
      goto __normal;
    }
    fputs("sys_epoll_wait", fpw);
    fclose(fpw);
  }
  return old_fopen(re_path, mode);

3、  inotify_add_watch
检测mem、pagemem、task等读取事件。可以直接让其返回-1,但不推荐这么做。如果检测代码并未对返回值做判断,直接使用,这样Anti-anti会导致程序崩溃,我的做法是改变mask的值:

//监控打开和读事件
if(strstr(pathname, "mem") != NULL){
  LOGD("[*] inotify_add_watch --> patch mem");
  return old_inotify_add_watch(fd, pathname, 0x00000200);    //mem永远不会被删除,改为0也可以
//监控打开和读事件,防获取反调试线程信息
}else if(strstr(pathname, "task") != NULL){
  LOGD("[*] inotify_add_watch --> patch task");
  return old_inotify_add_watch(fd, pathname, 0x00000200);  
4、  ptrace
这个函数算是用得比较多的吧,简单的检测代码是调用PTRACE_TRACEME,直接返回0就可以,不过现在这种方式很少了吧。某加固fork了进程去作ptrace,并写入一些数据。通过对ptrace hook,可以监控到写入数据。模拟这个通信过程就可以Anti-anti。具体就不展开了吧,相信各位读者可以做到了。
当然,还有一些Anti-anti方法,限于篇幅,就不展开了吧(让小弟学学各位大牛的Anti-anti方法吧)。

四、总结
做好Anti-trace和Anti-anti有些时候能大大节省时间,将精力专注于算法或者其他逻辑上。但也存在一些问题:
1、  HOOK检测
导出表HOOK检测:读取/system/lib/libc.so特定函数偏移,再获取本进程libc.so的基地址来检测是否有HOOK。(Patch方法:Hook fopen、dlopen和open函数)
Inline HooK检测:Inline Hook时要替换函数起始的字节码,可以检测一些比较奇怪的二进制码(BX pc等字节码)。
2、  LOGCAT
许多反调试都会起一个进程或者线程循环监控,此时anti-trace的输出会比较多,比如:
点击图片以查看大图图片名称:	4.png查看次数:	1文件大小:	39.8 KB文件 ID :	97245
图 4
这看起来比较烦,而且很多都是相同的。可以利用调用点trace地址的唯一性进行过滤,让同一信息只输出一次即可,也可以写个apk,将信息通过AF_UNIX本地套接字发送给apk,利用数据库的优势来存储等等。
3、  SVC
有些关键性的API,为了隐藏,直接通过汇编实现系统调用。ARM系统调用时的调用号通过r7寄存器来传递的。如果编写时,没有手写花指令,直接通过扫描SVC字节码和附近的关于r7的寄存器赋值操作,可以获得一些搜索结果。不过为了隐藏,手写点花指令也是可以的。
比如:常用来刷cache的代码
static void clearcache(char* begin, char *end)
{
  const int syscall = 0xf0002;
  __asm __volatile (
    "mov   r0, %0\n"
    "mov   r1, %1\n"
    "mov   r7, %2\n"
    "mov     r2, #0x0\n"
    "svc     0x00000000\n"
    :
    :  "r" (begin), "r" (end), "r" (syscall)
    :  "r0", "r1", "r7"
    );
}

上传的附件
文件类型: pdf基于HOOK的Anti-debug callee检测和Anti-anti.pdf (359.8 KB, 52 次下载)
文件类型: ziptrace_anti_debug.zip (1.71 MB, 66 次下载)

<think>首先,用户的问题是:“我想检测某书应用中的反Frida机制 请问anti-frida detection in 《某书》 app”。我需要理解用户的需求:用户想检测某个应用(称为“某书”应用)中的反Frida机制,即应用如何检测Frida工具的使用。 根据系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法必须正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考站内引用:用户提供了几个引用,我需要参考这些引用,但用户说“上述引用仅供参考”,所以我应该基于这些引用构建回答,但不要直接复制。 提供的引用: - [^1]: 关于Frida绕过检测反调试的技术。 - [^2]: Frida在设备上创建的文件检测方法。 - [^3]: 一个Frida工具frida-dexdump,用于dump dex文件。 - [^4]: 对抗策略,如插入伪装系统调用。 用户上一次的问题似乎是关于检测反Frida机制,所以我需要构建一个结构化的回答。 回答结构: 1. **理解问题**:先解释什么是反Frida机制。 2. **检测方法**:提供如何检测应用中的反Frida机制。 3. **步骤指南**:给出逐步的检测方法。 4. **相关工具**:提到工具如frida-dexdump或其他。 5. **生成相关问题**:在最后添加相关问题。 使用中文回答。 生成相关问题:在回答结束时,添加几个相关问题,如示例中所示。 现在,基于引用构建内容: - 从[^2]:检测Frida文件,如检查/data/local/tmp目录下的frida-server、frida-agent等。 - 从[^1]:Frida可以伪装或hook检测函数。 - 从[^3]:使用frida-dexdump需要先绕过检测。 - 从[^4]:插入伪装系统调用以对抗检测。 所以,回答应该包括: - 如何检测应用是否在检测Frida。 - 常见检测方法:文件检查、内存检查、系统调用监控等。 - 如何应对:使用Frida自身来hook检测函数或修改行为。 逐步回答: 1. **介绍反Frida机制**:解释应用如何检测Frida。 2. **检测反Frida机制的方法**: - 静态分析:检查应用代码。 - 动态分析:使用Frida来监控应用行为。 3. **具体步骤**: - 步骤1:设置环境。 - 步骤2:使用Frida hook应用函数。 - 步骤3:监控文件访问或系统调用。 4. **工具推荐**:如frida-dexdump或其他。 5. **总结**。 最后,添加相关问题。 确保LaTeX格式正确:如果有数学表达式,使用$...$或$$...$$。在这个上下文中,可能没有太多数学,但如果有,比如概率或算法,我会使用。 在回答中,自然地添加引用标识。</think>### 检测某书应用中的反Frida机制 反Frida机制是指应用通过各种技术检测Frida工具的存在,以阻止动态分析调试。某书应用(假设为类似小红书或类似App)可能采用多种反Frida策略,包括文件检测、内存扫描、系统调用监控等。以下我将逐步指导您如何检测这些机制,确保回答基于可靠的安全工程实践。参考了Frida绕过检测的相关技术[^1],并结合实际分析步骤。 #### 1. **理解反Frida机制的常见类型** - **文件检测**:应用检查设备上Frida的残留文件,如`/data/local/tmp/frida-server`或`frida-agent.so`。如果存在这些文件,应用可能触发反调试行为[^2]。 - **内存特征扫描**:应用扫描进程内存中的Frida特征字符串(如`frida-agent`或`gum-js`),以识别注入。 - **系统调用监控**:应用监控异常系统调用序列(如频繁的`ptrace`或`openat`),这些可能是Frida操作的痕迹[^4]。 - **行为检测**:应用检测Frida的运行时行为,如动态代码加载或线程创建。 要检测这些机制,您需要结合静态动态分析。Frida本身是强大的检测工具,因为它能hook应用函数来观察检测逻辑。 #### 2. **检测反Frida机制的步骤** 以下是逐步检测方法。假设您已配置好Frida环境(Frida Server运行在Android设备上,并与PC客户端连接)。如果某书应用有加固(如混淆或加壳),需先脱壳处理。 **步骤1: 静态分析应用代码(初步识别检测)** - 使用工具(如Jadx或Apktool)反编译某书APK,搜索可疑字符串: - 关键词:`frida`、`/data/local/tmp`、`ptrace`、`anti-debug`。 - 示例:在反编译代码中,查找如`if (file.exists("/data/local/tmp/frida-server"))`的语句,这表示文件检测机制[^2]。 - 如果发现检测逻辑,记录相关类方法名(如`com.xiaohongshu.anti.FridaDetector`)。 **步骤2: 动态Hook检测函数(使用Frida监控行为)** - 编写Frida脚本,hook应用中的可疑函数。以下是一个Python示例脚本,监控文件访问内存操作: ```javascript // frida_detect_anti.js Java.perform(function() { // Hook文件检测函数(示例类名,需根据实际反编译结果修改) var fileClass = Java.use("java.io.File"); fileClass.exists.implementation = function() { var path = this.getPath(); console.log("[文件检测] 访问路径: " + path); if (path.includes("frida")) { console.log("[反Frida触发] 检测到Frida文件检查!"); } return this.exists.call(this); }; // Hook内存扫描函数(如System.loadLibrary) var systemClass = Java.use("java.lang.System"); systemClass.loadLibrary.implementation = function(libName) { console.log("[内存检测] 加载库: " + libName); if (libName.includes("frida")) { console.log("[反Frida触发] 检测到内存特征扫描!"); } return this.loadLibrary.call(this, libName); }; }); ``` - 运行脚本:`frida -U -l frida_detect_anti.js -f com.xiaohongshu.app`(替换为某书包名)。 - 观察日志:如果应用触发检测,控制台会输出反Frida行为,如文件访问或库加载警告。 **步骤3: 监控系统调用内存特征** - Frida可以注入代码来监控底层系统调用。使用`Stalker`模块跟踪异常调用序列: ```javascript // stalker_monitor.js Interceptor.attach(Module.findExportByName(null, "ptrace"), { onEnter: function(args) { console.log("[系统调用] ptrace 调用, 可能被用于反调试"); // 如果调用频繁,可能是检测Frida[^4] } }); ``` - 结合内存扫描工具(如frida-dexdump)dump内存DEX文件,检查是否有检测代码注入[^3]。 - 命令:`frida-dexdump -U -p com.xiaohongshu.app`(需先确保Frida检测已绕过)。 **步骤4: 验证绕过检测** - 如果检测到反Frida机制,应用可能崩溃或输出错误日志。此时,使用伪装技术: - 重命名Frida文件(如将`frida-server`改为`myserver`)[^2]。 - Hook检测函数并返回虚假值(如强制`file.exists()`返回`false`)。 - 插入伪装系统调用以混淆监控(例如,在敏感操作前添加无害调用如`SYS_getrandom`)[^4]。 - 测试绕过:修改Frida脚本后重新运行应用,确认检测不再触发。 #### 3. **工具最佳实践** - **推荐工具**: - **Frida**:核心工具,用于动态注入监控。 - **frida-dexdump**:dump内存DEX,帮助分析检测逻辑[^3]。 - **Ghidra/IDA**:静态分析辅助。 - **注意事项**: - 某书应用可能更新检测机制,需动态调整脚本。 - 在root设备上操作,避免权限问题。 - 参考Frida社区脚本(如GitHub仓库)获取针对特定App的示例。 通过以上步骤,您可以系统地检测某书应用中的反Frida机制。Frida的动态注入特性使其成为“以毒攻毒”的理想工具,帮助灵活应对防护机制[^1]。如果遇到具体错误,请提供更多细节以进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值