首页
社区
课程
招聘
[原创]Frida 17.6.2 魔改 rusda:编译实践与反检测 Patch 的技术剖析
发表于: 1天前 803

[原创]Frida 17.6.2 魔改 rusda:编译实践与反检测 Patch 的技术剖析

1天前
803

本文以 Frida 17.6.2、父仓库基线提交 1ce4396d6e43d93c8604c4cd4578ff84871bcfa1(见 patches/deliver/README.txt)上的 rusda 定制为主线。

开源和编译产物地址:140K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6@1j5h3W2K6N6h3W2A6i4K6u0r3M7Y4g2K6k6r3p5`.

Frida 17.6 发布没多久的时候,这边其实就已经开干了,只是前段时间一直比较忙,没来得及整理出来分享。

编译相关的 skill 和 patch 基本都已经放到 GitHub 里了。基础编译流程这里就不再展开,一方面 Frida 官网本身已有教程,另一方面也可以参考看雪这篇文章:

https://bbs.kanxue.com/thread-284739.htm

现阶段魔改 Frida 的项目其实已经不少了,之前 16.2.1 版本也有过一轮比较集中的改动。这次重新折腾 17.6.2,主要还是因为 Frida 17.6 之后和以前确实有一些比较明显的区别,不少旧思路不能直接平移。

另外现在工具链迭代很快,很多东西更新也很快,继续盯着老版本做文章,意义没有以前那么大。所以与其反复围绕旧版本打补丁,不如多看看新版本到底改了什么、哪些地方值得跟进。

常见的 Frida 检测面,大致可以分成下面几类:

这些检测项看起来都像“字符串问题”,但实际上并不在一个层面上。

有些只是暴露名字,改掉就行;有些则直接参与运行时逻辑,尤其是协议相关字符串,这种东西不能简单全局替换,否则表面上看是把特征抹掉了,实际上是把功能也一起改坏了。

rusda 的处理思路并不是把 frida 两个字全局抹掉,而是分层来做。

frida:rpc 这种字符串,不只是“好扫出来”那么简单,它本身就在 RPC JSON 里参与协议。

这类字符串如果直接在源码里替换成其它常量,协议就变了。要想既保留官方协议,又尽量不在磁盘和静态反汇编里直接暴露原串,就得换一种方式处理。

这里采用的是 XOR + 十六进制字面量

这样处理之后,磁盘文件和静态分析阶段看不到原始明文,但运行时内存里仍然会恢复成和官方一致的协议内容。

实现位置:

subprojects/frida-core/lib/base/obfuscate.vala

subprojects/frida-core/lib/base/rpc.vala

这里顺带提一句,Frida-gum 里内嵌的一些 JS 资源如果仍然带着 frida:rpc,那么只改 core 这一层并不能完全覆盖。所以这种处理方式更准确地说,是把核心路径先处理掉,而不是宣称“全仓库所有相关明文都已经消失”。

FridaScriptEngineGLib-GIOGDBusProxyGumScript 这一类字符串,更多属于类型注册名、组件名或者调试可见名,常见于只读段,比较适合做静态处理。

这部分采用的是 等长反转写回 .rodata。思路比较直接:不改长度,只把原字符串字节序反过来,这样既不会引入额外长度问题,也能避免被简单的固定模式直接命中。

subprojects/frida-core/src/topatch.py

这种方式的优点是实现简单、对布局影响小,缺点也很明显:可读性会下降,而且它只适合处理这种“能改外观、不影响语义约定”的字符串,不适合拿去处理协议 token。

符号表同样是很常见的一层特征,尤其是导出符号或者内存导出之后,很多名字是比较显眼的。

这里的处理主要有两类:

对应实现:

这里有一个比较容易忽略的点:符号改名是有联动影响的

比如 core 侧把 frida_agent_main 改成了 main,那么 gum 或测试代码里凡是仍然引用旧名字的地方,也要同步跟上。否则有可能出现一种比较烦的情况:构建阶段没报错,结果运行阶段符号解析失败。

线程名这层的检测很常见,很多检测脚本就是直接读 /proc/pid/task/*/comm

典型特征包括:

对于前面几类,直接做二进制级替换问题不大,例如:

但是 frida-gadget 不能简单全文替换。这点在实操里很关键,因为它不只是一个线程名字面量,还是 frida-gadget-tcp-<port> 的前缀子串,injector 那边往往会按这个格式去匹配。

如果这里粗暴做全局替换,就很容易把原本依赖 ^frida-gadget-tcp-(\d+)$ 这一类规则的逻辑给破坏掉。

所以 topatch 里不会对 frida-gadget 做无差别 sed。Gadget 相关前缀更适合在源码层通过约定方式处理,而不是在最终产物上全局替换。

这一点其实也能说明一个问题:不是所有暴露出来的字符串都能按同一种方式“抹掉”。只要某个字符串还参与匹配、协商或者逻辑分支,就必须按语义来处理,而不是只看它长得像不像特征。

Frida 17 开始,后端已经更多尝试通过 memfd 加载。这样做之后,maps 里原本为空的 Pathname,往往会变成类似:

它确实不再是传统意义上的“匿名映射”了,这能规避掉一部分专门盯着 00:00 0 节点号的简易检测脚本;但问题在于,名字本身还是很显眼

因此这部分也可以继续往前走一步,把 memfd 显示名改成和 frida 无关的常量,比如 jit-cache 之类,以减少基于 maps 路径前缀的直接命中。

还有一层最直接的检测面,就是文件名和进程名本身。

这一层虽然最浅,但也确实最容易被命中,所以 Meson 产出的二进制名称也做了统一调整,例如:

它不能解决深层问题,但能有效降低文件系统、进程列表这类最表层扫描的命中率。

这里放一下几个核心片段,基本就能对应上前面提到的几层处理。

subprojects/frida-core/lib/base/obfuscate.vala

subprojects/frida-core/lib/base/rpc.vala

subprojects/frida-core/src/topatch.py

这次重新看 17.6.2,还有一个比较直观的感受:不少以前很敏感的暴露点,官方自己也在往收敛的方向做。

Frida 17 在后端加载链路里已经开始尝试通过 memfd 装载,更新记录里也能看到类似 Load backend through memfd with fallback 的描述。

原理上,就是用 memfd_create 在 RAM 中创建一个虚拟文件,再对该文件执行 mmap

效果是,maps 里原本可能为空的 Pathname,会显示成 /memfd:frida-agent (deleted) 这一类形式。虽然仍然有痕迹,但它已经不属于传统的匿名映射,对于那种专门扫 00:00 0 的极简脚本来说,确实能避开一部分。

以前很多人的习惯是 Memory.alloc 之后直接配合 Memory.protect(..., 'rwx'),或者自己长期维护一块可写可执行内存。这种方式在现在这个环境下其实是比较显眼的。

Frida 17 对代码修补路径做了一些收敛,官方更倾向于在真正写入 trampoline 的瞬间才短暂打开写权限,而不是长时间维持 rwx

在实践里,如果直接走 InterceptorProcess.setExceptionHandler 这类官方路径,很多时候权限切换会由 Frida 自己处理,大多数时间内相关页面仍然可以保持为 r-x。这一点虽然谈不上“隐身”,但相比老习惯,暴露面确实小一些。

再往前走一步,就是尽量不申请新的可疑映射,而是去找已有合法映射中的 padding、对齐空洞或者可利用区域,把跳板直接写进文件支撑的映射空间。

这样 maps 里看到的还是正常库路径,例如 /system/lib/libc.so,而不是一块新出现的可疑匿名页或临时页。

不过这一层已经超出本文仓库当前 patch 的范围了。更准确地说,它属于更深层的注入落点设计问题,可以作为后续方向讨论,但不应和本文这套字符串、符号、线程名层面的处理混在一起。

这个问题其实反而是最值得说清楚的。

魔改能做的,主要还是处理一部分比较显式、比较浅层的特征,比如:

这些东西处理完之后,确实能降低被简单规则直接命中的概率;但它解决不了所有问题,尤其解决不了更深层的完整性校验。

比如有些检测并不关心你是不是叫 Frida,而是直接检查:

这类检测关注的是“有没有被动过”,而不是“名字有没有改干净”。


[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 34
支持
分享
最新回复 (18)
雪    币: 682
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
tql
1天前
0
雪    币: 272
活跃值: (313)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
1天前
0
雪    币: 104
活跃值: (8122)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
rbq
1天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
666
1天前
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
感谢分享
1天前
0
雪    币: 1723
活跃值: (1943)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
 魔改 rusda
1天前
0
雪    币: 8586
活跃值: (5278)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
666
1天前
0
雪    币: 2432
活跃值: (4046)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
666
1天前
0
雪    币: 1010
活跃值: (1490)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
6666
1天前
0
雪    币: 4327
活跃值: (4980)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
6
23小时前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
666
20小时前
0
雪    币: 607
活跃值: (6480)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
13
謝謝分享。
20小时前
0
雪    币: 447
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
6+666
15小时前
0
雪    币: 403
活跃值: (1133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15

666666

最后于 15小时前 被Kill_Log编辑 ,原因:
15小时前
0
雪    币: 211
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
牛逼啊
14小时前
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
666
12小时前
0
雪    币: 714
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
看隐藏学习一下
2小时前
0
雪    币: 43
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
看看
3分钟前
0
游客
登录 | 注册 方可回帖
返回