首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
5
1
[原创]安卓逆向:某加固 frida检测绕过方法
发表于: 2026-3-22 22:52
1870
[原创]安卓逆向:某加固 frida检测绕过方法
mb_hfjfruhl
2026-3-22 22:52
1870
本文为MzYw5Yqg5Zu6加固的frida检测绕过方法,纯个人分享,绕过是通过frida的native hook并结合ida pro分析绕过。 # hook dlopen 首先hook一下dlopen函数,检测一下该加固lib的加载: ``` javascript function hook_dlopen(target_module) { var funcPtr = Process.findModuleByName("libdl.so").findExportByName("android_dlopen_ext"); if (!funcPtr) { funcPtr = Process.findModuleByName("libdl.so").findExportByName("dlopen"); } if (!funcPtr) { console.log("[-] dlopen not found"); return; } console.log("[*] Hooking dlopen at: " + funcPtr); Interceptor.attach(funcPtr, { onEnter: function (args) { try { var path = args[0].readCString(); console.log("[dlopen] " + path); if (!is_lib_attached && path.indexOf(target_module) !== -1) { is_lib_attached = true; } } catch (e) { } }, onLeave: function (retval) { if (is_lib_attached && flag) { flag = 0; var target_base = Process.getModuleByName(target_module).base; console.log("[*] " + target_module + " loaded at: " + target_base); // hook_sub_47A50(target_base); } } }); } ``` 这里加入了防止重复检测的逻辑。因为在dlopen() onEnter和onLeave里进行判断,dlopen()需要调用很多次,我们需要的lib只加载一次就够了,不要每次都就检测。 还有一点需要注意,frida在17+的版本里更新了api,详情可以去查一下,这里用的是frida 17.5.2。 运行截图如下:  程序检测到了frida直接就挂掉了,我们需要检测一下在哪里导致我们的程序挂掉了。 我们使用了一个ida的插件: [stalker_trace_so](https://bbs.kanxue.com/elink@563K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6G2j5h3y4A6j5g2) 直接复制到ida的plugin目录下就行了,会在ida里输出对应的用法。这个frida脚本用的是旧版本的frida api,如果用的是frida 17+版本,需要把对应的api修改一下。 我们运行之后结果如下:  # sub_9740() 在调用sub_9740()的时候挂了,我们直接在ida里去搜索这个函数:  进一步查看:   这个地址直接就是0。这里通过对汇编的分析,再结合gemini,我怀疑这里是在调用got表里的东西。 如果直接到这里那肯定是个Invalid Address,但是不确定运行的时候会不会动态把这个值给填进来,所以写个frida脚本检测一下63910这个位置: ```javascript const hook_sub = (base) => { var gotEntryAddr = base.add(0x63910); console.log("[*] GOT Entry Address (X16): " + gotEntryAddr); var jumpTarget = gotEntryAddr.readPointer(); console.log("[*] Jump Target Address (X17): " + jumpTarget); console.log("[*] Target Symbol: " + DebugSymbol.fromAddress(jumpTarget)); } ``` 输出如下:  并没有填值进去,就是单纯的0,那很好判断,这就是检测到了frida之后干掉程序的地方。我们直接看一下刚刚sub_9740()的交叉引用就行。 # sub_3E15C() 通过交叉引用找到了sub_3E15C(),函数比较长,就不放了。我的思路就是看看程序是不是在这里挂掉的,于是直接写脚本,在onEnter里输出一下log,onLeave里log一下: ```js const hook_sub_3E15C = (base) => { var sub = base.add(0x3E15C); var counter = 0; Interceptor.attach(sub, { onEnter: function (args) { counter++; console.log("[*] sub_3E15C called " + counter + " times"); }, onLeave: function (retval) { console.log("[*] sub_3E15C returned: " + retval); } }); } ``` 运行了50次之后,发现:  只有call,没有return。猜测这个函数其实是递归调用的,调用了50次挂了,并没有return,说明就是在这个函数里结束了程序。 通过对sub_3E15C()的分析,发现sub_9740()就只调用了两次,那这样的sub_47A50()就非常可以了,高度怀疑程序在这里崩溃了。  # sub_94D0() 于是我修改了之前的代码,不断的检测sub_47A50(),果然发现程序就是崩溃在了这个函数里:  而且其返回值是有一定的规律的。 通过之前的分析,我将sub_94D0()函数名修改成了to_address_0()。ida同时自动调整了部分函数的命名,可以通过xref查看:  这样的话谁调用了to_address_0()一目了然,在sub_47A50()里果然有两处调用:  还有一个地方:  也就是说,在这个函数里,有两处逻辑,可能会导致程序挂掉。既然已经定位到了这里,我也不想关心正常的逻辑怎么走了,我直接在第50次调用这个函数的时候直接修改返回值就行了: ```js var counter = 0; const hook_sub_47A50 = (base) => { var sub = base.add(0x47A50); Interceptor.attach(sub, { onEnter: function (args) { counter++; console.log("[*] sub_47A50 called " + counter + " times"); if (counter == 50) { this.context.pc = this.context.lr; this.context.x0 = ptr("0x7ff09c17e0"); } }, onLeave: function (retval) { console.log("[*] sub_47A50 returned: " + retval); } }); } ``` 再次运行,程序就进入到了主页面中,没有被挂掉,到这里bypass应该就结束了。
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
收藏
・
5
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
Ally Switch
为你点赞!
2026-4-10 09:58
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_hfjfruhl
4
发帖
9
回帖
10
RANK
关注
私信
他的文章
[原创]某开源通讯软件view once图片的调用逻辑分析
49945
[原创]安卓逆向:某加固 frida检测绕过方法
1870
[原创]安卓逆向某加固 frida检测绕过方法
1953
[转帖]VEH硬件断点的Ldr劫持技术[一更]
2598
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部