首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
50
14
[原创]frida反调试总结+一把梭
发表于: 2024-12-23 10:16
40222
[原创]frida反调试总结+一把梭
霸王茶几
2024-12-23 10:16
40222
# Frida反调试 ## 目录 - [检测](#检测) - [普通Frida检测](#普通Frida检测) - [自实现Frida检测](#自实现Frida检测) - [绕过](#绕过) - [普通检测绕过](#普通检测绕过) - [自实现检测绕过](#自实现检测绕过) # 检测 ## 普通Frida检测 普通的检测总结以下几种手段: 1. 检测/data/local/tmp下的frida特征文件,frida默认端口27042 2. 双进程检测 3. 检测/proc/pid/maps、/proc/pid/task/tid/stat、/proc/pid/fd中的frida特征 4. 检测D-BUS 安卓系统使用Binder机制来实现进程间通信(IPC),而不是使用D-Bus。检测函数向所有端口发送d-bus消息,如果返回reject就说明fridaserver开启。 ## 自实现Frida检测 我们可以看到,上面使用的绕过技巧,大多都和系统函数有关系,那么如果app中不调用这些系统函数,而是用自实现的函数来进行操作,不就很难hook了吗? 这里就有一种自实现函数的技术:`svc`。 通过安卓架构的学习,我们知道了安卓从上到下也是由层来分隔的,而层与层之间不能直接交互,而是需要一个中间层来进行操作。我们常见的`jni`就是`Java`层和`native`层的交互。而`syscall`就是`kernel`和`native`之间的中间层。 `svc`是x86架构中的一个指令,用于在用户模式下发起系统调用。当执行`svc`指令时,处理器会从用户态转换为内核态,执行内核级别的命令。  开发者可以通过`syscall`来执行内核函数,而不是直接使用系统函数,下面介绍几种防护手段: 1. 直接使用syscall替代libc函数: 不使用标准C库函数,而是直接调用系统调用。这样可以绕过常见的hook点,因为大多数hook工具主要针对libc函数。 ```c #include <sys/syscall.h> //SYS_open SYS_read SYS_close都是syscall.h中的常量 //代表系统调用的编号。在Linux系统中,每个系统调用都有一个唯一的编号 #include <unistd.h> #include <fcntl.h> int my_open(const char *pathname, int flags) { return syscall(SYS_open, pathname, flags); } ssize_t my_read(int fd, void *buf, size_t count) { return syscall(SYS_read, fd, buf, count); } int my_close(int fd) { return syscall(SYS_close, fd); } // 使用示例 int main() { int fd = my_open("/path/to/file", O_RDONLY); //fd代表文件标识符,代表打开的是哪个文件 if (fd != -1) { char buffer[100]; ssize_t bytes_read = my_read(fd, buffer, sizeof(buffer)); my_close(fd); } return 0; } ``` 2. 实现关键功能的自定义syscall wrapper: 为关键的系统调用创建自己的包装函数 ```c #include <sys/syscall.h> #include <unistd.h> #include <fcntl.h> #include <string.h> ssize_t secure_read(int fd, void *buf, size_t count) { // 完整性检查 if (syscall(SYS_gettid) != syscall(SYS_getpid)) { // 可能正在被调试,终止操作 return -1; } // 执行实际的读取操作 ssize_t bytes_read = syscall(SYS_read, fd, buf, count); // 数据校验 (简单示例,实际应用中可能需要更复杂的校验) if (bytes_read > 0) { for (ssize_t i = 0; i < bytes_read; i++) { ((char*)buf)[i] ^= 0x55; // 简单的XOR操作 } } return bytes_read; } ``` 3. 动态生成syscall: 在运行时动态生成syscall指令(直接使用机器码,和下面的汇编差不多) ```c #include <sys/mman.h> #include <string.h> typedef long (*syscall_fn)(long, ...); syscall_fn generate_write_syscall() { // 分配可执行内存 void* mem = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // x86-64 架构的 write syscall 机器码 unsigned char code[] = { 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 (write syscall number) 0x0f, 0x05, // syscall 0xc3 // ret }; // 复制代码到可执行内存 memcpy(mem, code, sizeof(code)); return (syscall_fn)mem; } // 使用示例 int main() { syscall_fn my_write = generate_write_syscall(); const char *msg = "Hello, World!\n"; my_write(1, msg, strlen(msg)); return 0; } ``` 4. 使用汇编实现`syscall`: 直接使用汇编语言实现系统调用 ```6502 assembly .global my_write my_write: mov x8, #64 // write syscall number for ARM64 svc #0 // trigger syscall ret // return to caller // C代码调用示例 // extern ssize_t my_write(int fd, const void *buf, size_t count); // // int main() { // const char *msg = "Hello, World!\n"; // my_write(1, msg, strlen(msg)); // return 0; // } ``` 最后再举一个实际检测frida的`syscall`例子: ```c #include <jni.h> #include <string.h> #include <fcntl.h> #include <unistd.h> JNIEXPORT jboolean JNICALL Java_com_example_SecurityCheck_detectFrida(JNIEnv *env, jobject thiz) { char line[256]; int fd = syscall(SYS_open, "/proc/self/maps", O_RDONLY); if (fd != -1) { while (syscall(SYS_read, fd, line, sizeof(line)) > 0) { if (strstr(line, "frida") || strstr(line, "gum-js-loop")) { syscall(SYS_close, fd); return JNI_TRUE; } } syscall(SYS_close, fd); } return JNI_FALSE; } ``` # 绕过 ## 普通检测绕过 网上对frida的检测通常会使用openat、open、strstr、pthread\_create、snprintf、sprintf、readlinkat等一系列函数,这里hook了strstr和strcmp函数。 ```javascript function replace_str() { var pt_strstr = Module.findExportByName("libc.so", 'strstr'); var pt_strcmp = Module.findExportByName("libc.so", 'strcmp'); Interceptor.attach(pt_strstr, { onEnter: function (args) { var str1 = args[0].readCString(); var str2 = args[1].readCString(); if (str2.indexOf("tmp") !== -1 || str2.indexOf("frida") !== -1 || str2.indexOf("gum-js-loop") !== -1 || str2.indexOf("gmain") !== -1 || str2.indexOf("gdbus") !== -1 || str2.indexOf("pool-frida") !== -1|| str2.indexOf("linjector") !== -1) { //console.log("strcmp-->", str1, str2); this.hook = true; } }, onLeave: function (retval) { if (this.hook) { retval.replace(0); } } }); Interceptor.attach(pt_strcmp, { onEnter: function (args) { var str1 = args[0].readCString(); var str2 = args[1].readCString(); if (str2.indexOf("tmp") !== -1 || str2.indexOf("frida") !== -1 || str2.indexOf("gum-js-loop") !== -1 || str2.indexOf("gmain") !== -1 || str2.indexOf("gdbus") !== -1 || str2.indexOf("pool-frida") !== -1|| str2.indexOf("linjector") !== -1) { //console.log("strcmp-->", str1, str2); this.hook = true; } }, onLeave: function (retval) { if (this.hook) { retval.replace(0); } } }) } replace_str(); 一把梭方案 ``` 检测点说明: 1. gmain:Frida 使用 Glib 库,其中的主事件循环被称为 GMainLoop。在 Frida 中,gmain 表示 GMainLoop 的线程。 2. gdbus:GDBus 是 Glib 提供的一个用于 D-Bus 通信的库。在 Frida 中,gdbus 表示 GDBus 相关的线程。 3. gum-js-loop:Gum 是 Frida 的运行时引擎,用于执行注入的 JavaScript 代码。gum-js-loop 表示 Gum 引擎执行 JavaScript 代码的线程。 4. pool-frida:Frida 中的某些功能可能会使用线程池来处理任务,pool-frida 表示 Frida 中的线程池。 5. linjector 是一种用于 Android 设备的开源工具,它允许用户在运行时向 Android 应用程序注入动态链接库(DLL)文件。通过注入 DLL 文件,用户可以修改应用程序的行为、调试应用程序、监视函数调用等,这在逆向工程、安全研究和动态分析中是非常有用的。 ## 自实现检测绕过 我们通过上面的学习看到,自实现系统函数一个重要的前提就是它们都有标准的系统调用号,标准的机器码。所以我们绕过的时候也可以用同样的思路。 Frida的Memory API可以直接查找整个系统的内存内容,我们直接搜索对应函数的特征码,定位到之后再使用Interceptor进行Hook。(要注意每个架构对应的特征可能不一样) ```c# function hookSysOpen() { let SYS_OPEN; let SVC_INSTRUCTION_HEX; const arch = Process.arch; if (arch === "arm64") { SYS_OPEN = 56; // ARM64架构下open系统调用的编号 SVC_INSTRUCTION_HEX = "01 00 00 D4"; // ARM64架构下svc指令的十六进制表示 } else if (arch === "arm") { SYS_OPEN = 5; // ARM架构下open系统调用的编号 SVC_INSTRUCTION_HEX = "00 00 00 EF"; // ARM架构下svc指令的十六进制表示 } else { console.log("不支持的架构: " + arch); return; } console.log("当前架构: " + arch); console.log("开始搜索SYS_OPEN系统调用..."); //系统调用指令(如svc)通常位于可执行代码段中(r-x) Process.enumerateRanges('r-x').forEach(function(range) { if (range.file && range.file.path && range.file.path.endsWith(".so")) { console.log("搜索模块: " + range.file.path); Memory.scan(range.base, range.size, SVC_INSTRUCTION_HEX, { onMatch: function(address) { let sysCallNumber; if (arch === "arm64") { // 在ARM64中,系统调用号在svc指令之前的指令中 sysCallNumber = address.sub(4).readU32() & 0xFFFF; } else if (arch === "arm") { // 在ARM中,系统调用号通常在r7寄存器中,这里我们只能近似处理 sysCallNumber = address.sub(4).readU16() & 0xFF; } if (sysCallNumber === SYS_OPEN) { console.log("找到SYS_OPEN调用,地址: " + address); Interceptor.attach(address, { onEnter: function(args) { let fileName; if (arch === "arm64") { fileName = args[1].readUtf8String(); } else if (arch === "arm") { fileName = args[0].readUtf8String(); } console.log("SYS_OPEN被调用,文件名: " + fileName); }, onLeave: function(retval) { console.log("SYS_OPEN返回值: " + retval); } }); } }, onComplete: function() { console.log("搜索完成"); } }); } }); } hookSysOpen(); ```
登录后可查看完整内容
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
#HOOK注入
#基础理论
收藏
・
50
点赞
・
14
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
hexdump
感谢你的贡献,论坛因你而更加精彩!
2026-7-30 11:57
syc
为你点赞!
2026-7-8 11:17
wx_funcrever
这个讨论对我很有帮助,谢谢!
2026-5-28 10:38
PLEBFE
感谢你的贡献,论坛因你而更加精彩!
2025-7-26 05:41
vVv一
期待更多优质内容的分享,论坛有你更精彩!
2025-4-12 09:55
IndexGuc
+1
这个讨论对我很有帮助,谢谢!
2025-1-2 21:47
mb_zfqvurgb
感谢你的贡献,论坛因你而更加精彩!
2024-12-30 12:34
mb_hgrbqfun
+1
感谢你的积极参与,期待更多精彩内容!
2024-12-27 22:22
你瞒我瞒
非常支持你的观点!
2024-12-25 09:19
nevinhappy
这个讨论对我很有帮助,谢谢!
2024-12-24 09:58
mychanges
为你点赞!
2024-12-24 09:42
养只猫不好么
感谢你分享这么好的资源!
2024-12-23 19:03
huangyalei
感谢你分享这么好的资源!
2024-12-23 17:47
W4VE丶
感谢你的贡献,论坛因你而更加精彩!
2024-12-23 11:04
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
5
)
养只猫不好么
雪 币:
1643
活跃值:
(4626)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
99
粉丝
1
关注
私信
养只猫不好么
2
楼
感谢分享
2024-12-23 19:04
0
mb_ldbucrik
雪 币:
6
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
691
粉丝
7
关注
私信
mb_ldbucrik
3
楼
谢谢分享
2024-12-24 10:01
0
iBa0
雪 币:
1560
活跃值:
(5833)
能力值:
( LV4,RANK:40 )
在线值:
发帖
6
回帖
482
粉丝
37
关注
私信
iBa0
4
楼
现在哥们分析的都是自实现字符串比较函数,套一大堆混淆www我也要一把梭
2025-3-23 17:45
0
kx_小龙人
雪 币:
1001
活跃值:
(241)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
7
粉丝
0
关注
私信
kx_小龙人
5
楼
学习了
2025-3-25 11:22
0
yyyyy———
雪 币:
215
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
27
粉丝
0
关注
私信
yyyyy———
6
楼
66
2026-2-27 14:10
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
霸王茶几
2
发帖
8
回帖
0
RANK
关注
私信
他的文章
[原创]frida反调试总结+一把梭
40222
Frida过反调试实战
16402
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部