-
-
[原创]Frida检测以及绕过
-
发表于: 2025-3-29 15:01 1217
-
Frida检测点
检测frida的监听端口
Frida
在运行时通常会开启特定的监听端口(默认 27042
和 27043
)。可以通过扫描本地端口来检测是否存在这些端口
1 | 解决办法:frida - server - l 127.0 . 0.1 : 2333 |
ptrace占坑
一些app防止注入就会在app里用ptrace(0,0,0,0)
自己附加自己
1 | 解决办法:用spawn方式注入,只是在app启动前就注入这种方式注入只用一瞬间的时间,注入成功后就退出 |
检测 D-Bus
协议通信
Frida
使用D-Bus
协议通信,D-Bus是一种进程间通信(IPC)和远程过程调用(RPC)机制,最初是为Linux开发的,目的是用一个统一的协议替代现有的和竞争的IPC解决方案。
通过检测内存中是否存在字符串frida:rpc
,遍历连接手机所有端口发送D-bus消息,如果返回REJECT
则存在frida-server
1 | 解决办法:hook 字符串比较函数将返回结果替换或者替换字符串 |
检测/proc/pid/fd目录
/proc/pid/fd
目录的作用在于提供了一种方便的方式来查看进程的文件描述符信息,这对于调试和监控进程非常有用。通过查看文件描述符信息,可以了解进程打开了哪些文件、网络连接等,帮助开发者和系统管理员进行问题排查和分析工作。
注入frida
后,fd目录下就会出现magisk
,frida-helper
,frida-agent
等标识符
检测/proc/pid/task/status下的线程名
/proc/pid/task
下存放的是当前进程下的线程,每个线程内有一个目录,进入线程目录的status
文件夹下可以看到当前线程的名字,例如gmain
,gdbus
,gum-js-loop
、pool-frida
等
检测/proc/self/maps
/proc/self/maps
是一个特殊的文件,它包含了当前进程的内存映射信息。当你打开这个文件时,它会显示一个列表,其中包含了进程中每个内存区域的详细信息。这些信息通常包括:
- 起始地址(Start Address)
- 结束地址(End Address)
- 权限(如可读、可写、可执行)
- 共享/私有标志(Shared or Private)
- 关联的文件或设备(如果内存区域是文件映射的)
- 内存区域的偏移量
- 内存区域的类型(如匿名映射、文件映射、设备映射等)
当注入frida后,在maps文件中就会存在frida-agent-64.so
、frida-agent-32.so
等文件。
1 | 以上的三个检测手段,都是可以通过io重定向文件解决 |
以maps为例,如何过掉检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | / / 定义一个函数,用于重定向并修改maps文件内容,以隐藏特定的库和路径信息 function mapsRedirect() { / / 定义伪造的maps文件路径 var FakeMaps = "/data/data/com.zj.wuaipojie/maps" ; / / 获取libc.so库中 'open' 函数的地址 const openPtr = Module.getExportByName( 'libc.so' , 'open' ); / / 根据地址创建一个新的NativeFunction对象,表示原生的 'open' 函数 const open = new NativeFunction(openPtr, 'int' , [ 'pointer' , 'int' ]); / / 查找并获取libc.so库中 'read' 函数的地址 var readPtr = Module.findExportByName( "libc.so" , "read" ); / / 创建新的NativeFunction对象表示原生的 'read' 函数 var read = new NativeFunction(readPtr, 'int' , [ 'int' , 'pointer' , "int" ]); / / 分配 512 字节的内存空间,用于临时存储从maps文件读取的内容 var MapsBuffer = Memory.alloc( 512 ); / / 创建一个伪造的maps文件,用于写入修改后的内容,模式为 "w" (写入) var MapsFile = new File (FakeMaps, "w" ); / / 使用Interceptor替换原有的 'open' 函数,注入自定义逻辑 Interceptor.replace(openPtr, new NativeCallback(function(pathname, flag) { / / 调用原始的 'open' 函数,并获取文件描述符(FD) var FD = open (pathname, flag); / / 读取并打印尝试打开的文件路径 var ch = pathname.readCString(); if (ch.indexOf( "/proc/" ) > = 0 && ch.indexOf( "maps" ) > = 0 ) { console.log( "open : " , pathname.readCString()); / / 循环读取maps内容,并写入伪造的maps文件中,同时进行字符串替换以隐藏特定信息 while (parseInt(read(FD, MapsBuffer, 512 )) ! = = 0 ) { var MBuffer = MapsBuffer.readCString(); MBuffer = MBuffer.replaceAll( "/data/local/tmp/re.frida.server/frida-agent-64.so" , "FakingMaps" ); MBuffer = MBuffer.replaceAll( "re.frida.server" , "FakingMaps" ); MBuffer = MBuffer.replaceAll( "frida-agent-64.so" , "FakingMaps" ); MBuffer = MBuffer.replaceAll( "frida-agent-32.so" , "FakingMaps" ); MBuffer = MBuffer.replaceAll( "frida" , "FakingMaps" ); MBuffer = MBuffer.replaceAll( "/data/local/tmp" , "/data" ); / / 将修改后的内容写入伪造的maps文件 MapsFile.write(MBuffer); } / / 为返回伪造maps文件的打开操作,分配UTF8编码的文件名字符串 var filename = Memory.allocUtf8String(FakeMaps); / / 返回打开伪造maps文件的文件描述符 return open (filename, flag); } / / 如果不是目标maps文件,则直接返回原 open 调用的结果 return FD; }, 'int' , [ 'pointer' , 'int' ])); } |
检测InlineHook
frida
的hook
原理就是通过修改函数的开头,写一个跳板,实现定向跳转,通过检测当前函数的前16个字节来判断是否被hook
1 2 | hook前 ff c3 04 d1 fd 7b 0e a9 fc 7b 00 f9 f8 5f 10 a9 hook后 50 00 00 58 00 02 1f d6 00 6e 3e 7d 74 00 00 00 |
从java hook检测frida
Java hook
的做法是将Java函数转化为Native函数,可以在关键函数上检测是否是Native的方法来检测frida,
CRC检测
frida
注入时会去主动hook libc.so
等库,hook后的crc校验值会与未hook的crc校验值不同
参考文章
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1921073&highlight=frida
个人公众号
谢谢大家关注
赞赏
- [原创]Frida检测以及绕过 1218