吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 10083|回复: 103
收起左侧

[Android 原创] Frida 脚本一键持久化方案:将 Frida 脚本打包至 Xposed 模块、直接注入APP、so、dll

    [复制链接]
MicroBlock 发表于 2025-11-12 01:18
本帖最后由 MicroBlock 于 2025-11-12 13:01 编辑

# 缘起

最近在开发某款 APP 的功能扩展时,我使用 Frida 编写了大量逻辑,但不想再逐行翻译成 Xposed 代码。于是决定将 Frida 直接固化到 APP 中。

最初尝试使用 frida-gadget,但在 Android 16 上遇到问题:脚本完全不执行,也没有任何错误提示,只好放弃。

在网上搜索后,没有找到便捷的持久化方案,于是决定自己动手实现。
我的目标是:只需运行一行命令,就能自动打包出 Xposed 模块、已固化 Frida 脚本的 APP 包,以及可注入运行 Frida 脚本的 .so 和 .dll 文件。

当然,最终这个目标顺利实现了。如果你只是想打包 Frida 脚本,而不关心具体原理,可以直接使用以下工具
https://github.com/std-microblock/fripack/


# 自编译 Frida

既然要自己动手,自然要做得尽善尽美。一方面需要掩盖 Frida 的某些特征,另一方面也要尽量减小体积。此外,我还希望将脚本直接嵌入二进制文件中,而不是作为独立文件存在。于是,我开始研究如何自行编译 Frida,类似于实现一个自定义的 frida-gadget。

首先,我们来看一下 Frida 的代码架构:
image.png
(注:此为简化示意图。实际上,frida-inject 和 frida-server 注入的是 frida-agent,但整体结构类似。)

可以看到,Frida Gadget 实际上是通过调用 Frida-GumJS 来执行我们的脚本。因此,我们也可以编写一个程序,调用 Frida-GumJS 来执行脚本。为了实现这一点,首先需要编译出 Frida-GumJS 库。

Frida-GumJS 通常包含两个引擎:V8 和 QuickJS。V8 执行效率高,但体积较大;QuickJS 体积较小。为了减小最终生成的二进制文件体积,我关闭了 V8 和内置的 Database,并参考 Florida 的 CI 流程,编写了一个 GitHub CI 来编译 Frida-GumJS:
https://github.com/FriRebuild/fripack-inject

接着,我创建了一个 xmake 项目,首先实现调用 GumJS 执行脚本的功能:

为了便于后续嵌入 JS 脚本,我设计了一个带有特定 Magic 标记的结构体:

这样,在生成 .so 文件时,就可以通过扫描 Magic 标记找到这个配置结构,然后通过设置 data_sizedata_offset 来指定脚本数据在二进制文件中的存储位置。

接下来,我们需要编写一个 CLI 工具,将脚本数据嵌入到二进制文件中。这里我选择了使用 Rust 来实现。


# 将数据嵌入二进制文件

## ELF 文件编辑

ELF 文件的主要结构包括 Section 和 Segment,其中数据通过 Segment 映射到内存中。我们只需新建一个 Segment,将脚本数据放入其中,并确保其加载到内存中。然后,计算出该数据映射的虚拟地址与 g_embedded_config 虚拟地址之间的偏移量,这样我们自制的 frida-gadget 就能正确加载数据了。

听起来很简单,我们来实现一下:

需要特别注意:对于需要加载到内存中的 Segment(PT_LOAD),其 vaddr 必须按 4K 或 16K 对齐,否则 dl 可能不会正确映射,且不会报错。

同时,由于新增了 Segment 和 Section,我们还需要扩展 PE 文件头的大小:

运行后却发现报错:.note sh_offset at 0x270, must be larger than 0x28a。这是怎么回事?原来是因为新增的 Segment 和 Section 导致 ELF 头变大,与原有的部分 Section 发生了重叠。我们需要将这些重叠的 Section 在文件中的数据及其 p_offset 移动到文件末尾:

再次运行,这次没有报错了。但将文件放到手机中加载时,又出现了新错误:PT_PHDR segment is not covered by a PT_LOAD segment。这个问题比较奇怪,网上资料也很少。我猜测 PT_PHDR Segment 本身不会被映射到内存中,它需要一个 PT_LOAD Segment 来帮助加载。于是,我们找到覆盖 PT_PHDR Segment 的那个 Segment,并将其扩展的大小同步到 PT_PHDR Segment:

接着,我们找到 Magic 标记在二进制文件中的位置,定位其所在的 Segment,并获取其 vaddr

计算出偏移量后,将其填入二进制文件中:

## PE 文件编辑

PE 文件的处理逻辑类似。虽然编辑 ELF 花了大半天时间,踩了不少坑,但编写 PE 编辑代码只用了十分钟。不得不说,PE 的设计比 ELF 更优秀(除了那个强制要求 ordinal 的 EAT),而且几乎没有遇到什么坑。以下是相关代码:

至此,我们得到了一个无需任何外部文件依赖、加载即自动执行脚本的二进制文件。


# 加载二进制文件到目标进程

接下来,只需要想办法将这个二进制文件加载到目标进程中。实现方式有很多,例如:

  • 重新打包 APK,对 APP 自带的 .so 文件使用 patchelf,将我们的 .so 加入其依赖项。
  • 重新打包 APK,在某个初始化类中添加静态初始化代码,调用 System.loadLibrary 加载我们的 .so。
  • 编写 Xposed 模块,利用其在目标进程中执行代码的特性来加载 .so。
  • 对于 Windows,使用多种 DLL 注入手段加载我们的 DLL。
  • 对于 Linux,使用 LD_PRELOAD 或其他映射方法加载我们的 .so。
  • 编写 Zygisk 模块,直接对特定 APP 加载我们的 .so。

目前,我已实现了前五种加载方式。受篇幅所限,这里仅介绍 Xposed 的实现方式。


# 生成 Xposed 模块来加载 .so

Xposed 模块会在目标进程中执行代码,因此我们只需在 xposed_init 类的 initZygote 方法中获取模块的 APK 路径,然后使用 System.load 加载其中的 .so 文件即可。

但问题在于,我们需要生成的是一个 APK 文件。我不想依赖过于笨重的 gradlew,也不想手动处理安卓特有的压缩格式、aapt2 编码、zipalign 等步骤。因此,我选择直接生成 apktool 工程,然后使用 apktool 构建 Xposed 模块的 APK。这样只需安装 apktool,即可方便地完成生成。

Xposed 模块的主要特征包括:AndroidManifest 中的几个特殊 metadata,以及 /assets/ 文件夹中的 xposed_initnative_init 文件。我们直接手动构建 apktool 的文件结构,并编写一段 Smali 代码,继承 IXposedHookZygoteInitIXposedHookLoadPackage,以实现加载 .so 的逻辑:

最后使用 apktool b 命令,即可构建出 Xposed 模块。


至此,我们已经实现了从 Frida 脚本一键打包生成 Xposed 模块的大部分逻辑。相关代码已经发布在 https://github.com/std-microblock/fripack/https://github.com/FriRebuild/fripack-inject

免费评分

参与人数 51威望 +2 吾爱币 +146 热心值 +40 收起 理由
R8848 + 1 我很赞同!
junjia215 + 1 + 1 谢谢@Thanks!
lin5789 + 1 热心回复!
asmzhang + 1 + 1 谢谢@Thanks!
leger1210 + 1 谢谢@Thanks!
rnb3ds + 1 + 1 用心讨论,共获提升!
zyq0811 + 1 + 1 我很赞同!
Visional丶羽 + 1 用心讨论,共获提升!
hjw01 + 1 + 1 我很赞同!
L2Z2Z2 + 1 我很赞同!
无问且问 + 2 + 1 我很赞同!
zhufengwan + 1 + 1 热心回复!
zhczf + 1 + 1 我很赞同!
wzvideni + 1 + 1 我很赞同!
imokk + 1 + 1 热心回复!
isffan + 1 谢谢@Thanks!
Tonyha7 + 2 + 1 用心讨论,共获提升!
chuxin2835 + 1 + 1 用心讨论,共获提升!
sjdhfj32323 + 1 用心讨论,共获提升!
q25l + 1 + 1 谢谢@Thanks!
fansxie + 1 + 1 我很赞同!
keulraesik + 1 谢谢@Thanks!
taylorchen + 1 用心讨论,共获提升!
snowfox + 1 谢谢@Thanks!
jdchl + 1 + 1 优秀
ioyr5995 + 1 + 1 我很赞同!
radargo + 1 + 1 我很赞同!
whereismy + 1 谢谢@Thanks!
devop + 1 + 1 我很赞同!
ko4625 + 1 我很赞同!
NOOB + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
heinv123 + 1 我很赞同!
fengbolee + 1 + 1 用心讨论,共获提升!
lclp1314 + 1 + 1 谢谢@Thanks!
Akihi6 + 1 + 1 谢谢@Thanks!
Tomlls + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
allspark + 1 + 1 用心讨论,共获提升!
liyicha + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Qiaoyuexuan + 1 + 1 谢谢@Thanks!
hdjkksj3366 + 1 谢谢@Thanks!
tonyYzh + 1 谢谢@Thanks!
zhou2957 + 1 + 1 谢谢@Thanks!
SharsDela + 1 + 1 谢谢@Thanks!
yiqibufenli + 1 + 1 我很赞同!
Discuz163 + 1 + 1 热心回复!
ZSDXAS + 1 + 1 用心讨论,共获提升!
shuisu + 1 + 1 用心讨论,共获提升!
hualy + 1 + 1 用心讨论,共获提升!
正己 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
5omggx + 1 + 1 用心讨论,共获提升!
白猫i- + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

  • · 好帖|主题: 598, 订阅: 89

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| MicroBlock 发表于 2025-11-12 19:08
mlgmxyysd 发表于 2025-11-12 16:38
Frida 可以完全代替 Xposed,用 Xposed 来加载 Frida 是不是有点本末倒置

这里 xposed 的作用只是作为注入器,将 .so 注入到目标进程中。不喜欢xposed的话也可以用生成zygisk模块,或者patch apk之类的方式。
wulincheng 发表于 2025-11-12 08:16
现在好多APP都开始验证签名了  重新打包有点不现实
ydydq 发表于 2025-11-12 09:54
mrcatt 发表于 2025-11-12 05:14
明白了很多
无名 发表于 2025-11-12 07:01
首先,我们来看一下 Frida 的代码架构:

(注:此为简化示意图。实际上,frida-inject 和 frida-server 注入的是 frida-agent,但整体结构类似。)


这里之间有张图吧,该图404了
jfj820712 发表于 2025-11-12 07:02
收藏 学习 谢谢!!
hunfeifei 发表于 2025-11-12 07:14
挺好的。又多了一个选择。jshook 貌似也可以。之前一直在用这个。magisk + jshook的组合。算法助手也行。
xiaolinzzz 发表于 2025-11-12 07:20
不错不错
gulu999 发表于 2025-11-12 07:35
大佬,好好看好好学
蛋蛋侠 发表于 2025-11-12 08:02
不错,学习了,赶紧试试看。
JinWay 发表于 2025-11-12 08:26
牛啊,受教了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-9-15 13:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表