使用 Frida Hook Android App
版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
Frida
Frida 通过注入自定义 JavaScript 代码,可以 Hook 函数、修改参数、监控函数调用和拦截返回值,适用于逆向工程、调试和安全分析等场景。
使用 Frida 前需要先下载和安装包括:
-
Frida 是核心库,提供 API 和功能。
-
Frida-Tool 是命令行工具,通常与 Frida 版本相对应。
-
Frida-Server 是运行在 Android 设备上的服务器端组件,允许 Frida 客户端与设备进行通信。
环境准备
1. 安装 Frida 和 Frida-tools
pip install frida-tools
2. Frida-server
获取设备CPU架构
adb shell getprop ro.product.cpu.abi
下载与设备对应架构的 frida-server:https://github.com/frida/frida/releases
把 frida-server 推送到设备 /data/local/tmp 目录下
adb push D:\app逆向\Frida\frida-server-16.5.2-android-arm64 /data/local/tmp/fs
启动 frida-server
# 启用超级管理员
adb root
# 进入命令行
adb shell
# 添加可执行权限
chmod +x /data/local/tmp/fs
# 启动frida-server
/data/local/tmp/fs
自定义Frida端口
frida-server 默认端口为 27042,如果想自定义端口可以通过下面的命令实现
# 启动frida-server,并指定端口
/data/local/tmp/fs -l 0.0.0.0:1234
# 端口转发到本地
adb forward tcp:1234 tcp:1234
Frida相关命令
1. frida-ps
打印设备上的进程
# 列出本地设备所有进程
frida-ps
# 列出USB设备上所有进程
frida-ps -U
# 列出远程设备上的进程
frida-ps -H 127.0.0.1:1234
2. frida-ls-devices
列出可用的设备
frida-ls-devices
3. frida
启动 frida 并执行脚本
# 启动frida并执行脚本
# 连接到 USB 设备
# -U 表示连接到 USB 设备
# -f 指定应用包名,Frida 会启动该应用
# -l 脚本路径
frida -U -l your_script.js -f target_app
# 连接到远程设备
# -H:远程设备地址和端口
# -l:脚本路径
# -n:指定应用包名,Frida 会启动该应用
frida -H 127.0.0.1:1234 -n com.shizhuang.duapp -l hook.js
示例
1. Hook dlopen & android_dlopen_ext
创建 hook_so_load.js,调用 Frida JS 库,Hook dlopen 和 android_dlopen_ext 函数并打印加载的 so 文件路径。
Frida JS库官方文档:https://frida.re/docs/javascript-api
function hook_so_load(package_name) {
// Hook dlopen 函数
const dlopenAddr = Module.findExportByName(null, "dlopen");
const dlopen = new NativeFunction(dlopenAddr, 'pointer', ['pointer', 'int']);
Interceptor.attach(dlopen, {
onEnter(args) {
// 获取传递给 dlopen 的参数(SO 文件路径)
const soPath = Memory.readUtf8String(args[0]);
// 如果是目标app SO 文件,则打印路径
if (soPath.includes(package_name)) {
// 打印信息
console.log("dlopen() - Loaded SO file:", soPath);
}
// console.log("dlopen() - Loaded SO file:", soPath);
}, onLeave(retval) {
}
});
// Hook android_dlopen_ext 函数
const android_dlopen_extAddr = Module.findExportByName(null, "android_dlopen_ext");
const android_dlopen_ext = new NativeFunction(android_dlopen_extAddr, 'pointer', ['pointer', 'int', 'pointer']);
Interceptor.attach(android_dlopen_ext, {
onEnter(args) {
// 获取传递给 android_dlopen_ext 的参数(SO 文件路径)
const soPath = Memory.readUtf8String(args[0]);
// 如果是目标app SO 文件,则打印路径
if (soPath.includes(package_name)) {
// 打印信息
console.log("android_dlopen_ext() - Loaded SO file:", soPath);
}
// console.log("android_dlopen_ext() - Loaded SO file:", soPath);
}, onLeave(retval) {
}
});
}
hook_so_load("com.shizhuang.duapp")
运行脚本
frida -H 127.0.0.1:1234 -l hook_so_load.js -f com.shizhuang.duapp
日志输出如下
____
/ _ | Frida 14.2.18 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://www.frida.re/docs/home/
Spawned `com.shizhuang.duapp`. Use %resume to let the main thread start executing!
[Remote::com.shizhuang.duapp]-> %resume
[Remote::com.shizhuang.duapp]-> android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/oat/arm64/base.odex
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libGameVMP.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libmmkv.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libxcrash.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libdulog.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libduhook.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libheif.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libdewuffmpeg.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libduplayer.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libstatic-webp.so
android_dlopen_ext() - Loaded SO file: /data/user/0/com.shizhuang.duapp/files/soloader_x64/libxyvodsdk.so
android_dlopen_ext() - Loaded SO file: /data/user/0/com.shizhuang.duapp/files/soloader_x64/libijmdetect_drisk.so
android_dlopen_ext() - Loaded SO file: /data/user/0/com.shizhuang.duapp/files/soloader_x64/libdu_security.so
2. 运行中调试
在 Frida 运行中可以调用 Frida JS 库,可以方便地 Hook 方法、修改数据、跟踪执行流等
比如,调用 console.log 打印日志
[Remote::com.shizhuang.duapp]-> console.log("hello")
hello
获取当前 app 进程id
[Remote::com.shizhuang.duapp]-> Process.id
7098
hook java.util.HashMap 的 put 方法并打印 key 和 value
[Remote::com.shizhuang.duapp]-> Java.perform(function() {
// 获取 HashMap 类引用
var HashMap = Java.use('java.util.HashMap');
// Hook HashMap.put() 方法
HashMap.put.implementation = function (key, value) {
// 打印 HashMap 的 key 和 value
console.log("HashMap.put() called:[", key.toString(), "][", value.toString(), "]");
// 调用原始的 put() 方法
return this.put(key, value);
};
});
重新加载脚本文件
[Remote::com.shizhuang.duapp]-> %reload
退出 Frida
[Remote::com.shizhuang.duapp]-> exit
3. Hook HashMap
创建 hook_hashmap.js,Hook java 标准库中的 HashMap 并打印 key 和 value。
function hook_hashmap() {
// 获取 HashMap 类引用
var HashMap = Java.use('java.util.HashMap');
// Hook HashMap.put() 方法
HashMap.put.implementation = function (key, value) {
// 打印 HashMap 的 key 和 value
console.log("HashMap.put() called:[", key.toString(), "][", value.toString(), "]");
// 调用原始的 put() 方法
return this.put(key, value);
};
}
Java.perform(function () {
hook_hashmap()
})
运行脚本
frida -H 127.0.0.1:1234 -l hook_hashmap.js -f com.shizhuang.duapp
绕过Frida反调试
启动 frida,但 app 启动不久 frida 就直接被杀掉了。
通过 hook dlopen 函数打印加载的 so 文件,发现当加载到 libmsaoaidsec.so 时,frida 就停止了,日志信息如下
Spawned `com.shizhuang.duapp`. Resuming main thread!
[Remote::com.shizhuang.duapp ]-> android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/oat/arm64/base.odex
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libGameVMP.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libmmkv.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libxcrash.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libdulog.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libduhook.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libszstone.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libdewuhelper.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libdusanwa.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libc++_shared.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libdu_mediacache.so
android_dlopen_ext() - Loaded SO file: /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libmsaoaidsec.so
Process terminated
直接删除 libmsaoaidsec.so 试试看
adb shell rm /data/app/com.shizhuang.duapp-fTxemmnM8l6298xbBELksQ==/lib/arm64/libmsaoaidsec.so
重新启动 frida,能正常运行。
参考:
- 绕过最新版bilibili app反frida机制
Failed to spawn: timeout was reached
执行 frida 命令时,提示 Failed to spawn: timeout was reached。
这是因为 frida 版本与 android 版本不匹配导致的。
根据自己的 android 版本,降级或升级 frida、frida-tool、frida-server的版本即可,比如 android10 实测可用的版本如下
Frida 版本 | Frida-Tool 版本 | Frida-Server 版本 | Android 版本 |
---|---|---|---|
14.2.18 | 9.2.2 | frida-server-14.0.0-android-arm64 | Android 10 |
卸载当前的 frida 和 frida-tools
pip uninstall frida
pip uninstall frida-tools
创建 python3.8 环境
conda create -n anti-app python=3.8.20
关于 python 环境管理可以参考这篇文章【使用Miniconda管理Python环境】
重新安装对应版本的 frida 和 frida-tools。
# 安装 frida 和 frida-tools
pip install frida==14.2.18
pip install frida-tools==9.2.2
在安装 frida 时候提示找不到 frida-14.2.18-py3.8-win-amd64.egg
pip install frida==14.2.18
Collecting frida==14.2.18
Using cached frida-14.2.18.tar.gz (7.7 kB)
Preparing metadata (setup.py) ... done
Requirement already satisfied: setuptools in d:\app\miniconda3\envs\anti-app\lib\site-packages (from frida==14.2.18) (75.1.0)
Building wheels for collected packages: frida
Building wheel for frida (setup.py) ... error
error: subprocess-exited-with-error
× python setup.py bdist_wheel did not run successfully.
│ exit code: 1
╰─> [71 lines of output]
running bdist_wheel
running build
running build_py
creating build\lib.win-amd64-cpython-38\frida
copying frida\core.py -> build\lib.win-amd64-cpython-38\frida
copying frida\__init__.py -> build\lib.win-amd64-cpython-38\frida
running build_ext
looking for prebuilt extension in home directory, i.e. C:\Users\cyrus/frida-14.2.18-py3.8-win-amd64.egg
prebuilt extension not found in home directory, will try downloading it
querying pypi for available prebuilds
Traceback (most recent call last):
File "C:\Users\cyrus\AppData\Local\Temp\pip-install-udcju8m2\frida_46b2b9f55dbd4291a67681f098292389\setup.py", line 101, in build_extension
with open(egg_path, "rb") as cache:
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\cyrus/frida-14.2.18-py3.8-win-amd64.egg'
note: This error originates from a subprocess, and is likely not a problem with pip.
ERROR: Failed building wheel for frida
Running setup.py clean for frida
Failed to build frida
ERROR: ERROR: Failed to build installable wheels for some pyproject.toml based projects (frida)
直接从 https://pypi.org/simple/frida/ 上下载 frida-14.2.18-py3.8-win-amd64.egg
下载完成后放到用户目录下,并重新执行 pip install frida==14.2.18 即可。
下载对应版本的 frida-server 并更新到设备上:https://github.com/frida/frida/releases?page=14
自动化脚本
frida-server-upload.bat
创建 frida-server-upload.bat,实现拖入 frida-server 直接推送到设备并添加执行权限。
@echo off
setlocal
REM 提示用户输入文件路径
set /p filepath=请输入frida-server文件路径(可直接拖入):
REM 检查输入是否为空
if "%filepath%"=="" (
echo 你没有输入文件路径.
pause
exit /b
)
REM 检查文件是否存在
if not exist "%filepath%" (
echo 文件不存在,请检查路径.
pause
exit /b
)
REM 把 frida-server 推送到设备 /data/local/tmp 目录下
adb push "%filepath%" /data/local/tmp/fs
REM 启用超级管理员
adb root
REM 添加可执行权限
adb shell chmod +x /data/local/tmp/fs
pause
frida-server-start.bat
创建 frida-server-start.bat,实现一键启动 frida-server 并转发端口
@echo off
REM 启用超级管理员权限
adb root
REM 启动frida-server
adb shell "/data/local/tmp/fs -l 0.0.0.0:1234 > /dev/null 2>&1 &"
REM 等待 3 秒
timeout /t 3
REM 查看frida-server进程是否启动成功
adb shell "lsof | grep 1234"
REM 转发到本地1234端口
adb forward tcp:1234 tcp:1234
pause