版权归作者所有,如有转发,请注明文章出处: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 image.png

把 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

4. 枚举 Java 对象

Java.choose 函数是 Frida 提供的一个用于在 Android 应用程序中枚举 Java 对象的便捷函数。它允许你查找和选择特定类型的 Java 对象(如 Activity、Service、自定义类实例等)。

choose 方法会遍历 JVM 中的所有 Java 对象,并将符合条件的对象传递给回调函数。

Java.choose(className, {
    onMatch: function(instance) { 
        // 当找到符合条件的实例时调用
    },
    onComplete: function() { 
        // 遍历完成时调用
    }
});

附加到当前设备的前台应用

-F 表示附加到前台应用程序

frida -H 127.0.0.1:1234 -F -l hook_addr.js

此命令会自动找到当前设备上的前台应用并附加到它,无需手动指定应用程序包名。

附加到当前正在运行的进程

1. 通过 frida-ps 命令查看当前设备所有进程信息,并通过 findstr 过滤出指定 app 的进程信息

frida-ps  -H 127.0.0.1:1234 | findstr duapp

32347  com.shizhuang.duapp:pushservice

2. 通过 -p 参数指定进程id,附加到目标进程

frida -H 127.0.0.1:1234 -p 32347 

或者,附加到目标进程,并执行指定脚本

frida -H 127.0.0.1:1234  -l jni_tools.js  -p 32347

3. 调用 Frida JS 调试当前进程 image.png

绕过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,能正常运行。 image.png

参考:

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.189.2.2frida-server-14.0.0-android-arm64Android 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 image.png 下载完成后放到用户目录下,并重新执行 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