不写教程,直接实例

环境准备

jadx 反编译 https://github.com/skylot/jadx/releases 如果没有JDK环境建议选择with-jre-win

ida pro 强大的反汇编 这个可以在网上找找

frida 今天的主角,python绑定:pip install frida frida-tools, Java桥:npm install -g frida-java-bridge(是否-g这个看个人选择),官网:https://frida.re/ , github:https://github.com/frida/frida/releases

frida frida-tools

查看架构:adb shell getprop ro.product.cpu.abi

cpu abi

所以选第三个server

模拟器(因为要开root,如果有root实体机也行)这个看个人,我用的MuMu

反编译

我们以官网的一道题为例:https://frida.re/docs/examples/android/ ,apk地址也在里面

先将apk下载到本地,拖到jadx:

jadx

这个好像就是flag了:

if (1000 == MainActivity.this.cnt) {
    tv3.setText("SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}");
}

但是这里面并没有明确的函数内容

calc

只好用上ida pro了

反汇编

先将apk复制一份,将后缀名改为zip/rar并解压,找到这个so文件并在ida pro打开:

so ida

经过问ai:


这个函数是 JNI 导出函数,对应 Java 层 com.example.seccon2015.rock_paper_scissors.MainActivity 类中的 calc 方法。

从反汇编看,它只有两条指令:

mov eax, 7
retn

含义

  • 该函数直接将返回值 eax 寄存器设为 7,然后返回。
  • 无论传入什么参数,它都固定返回整数 7

可能的情景

  • 这是一个被简化的 native 实现(可能是 CTF 题目中的故意修改或占位)。
  • 在 SECCON 2015 的“石头剪刀布”题目中,原本的逻辑可能是根据某种计算返回 0、1、2 代表石头、布、剪刀,但此处被硬编码为 7,说明计算被移除或题目要求直接分析出这个固定返回值。
  • 也可能是题目被 patch 过,或者用于快速验证、绕过某些检查。

实际意义

  • 如果在 Java 层调用 calc(),得到的结果总是 7,可以据此猜测程序的行为或作为解题线索(例如 7 可能对应“剪刀”以外的特殊状态,或者用作 flag 的一部分)。

按理说结果已经出来了:SECCON{107749}

但是为了更直观,接下来要用frida了

frida hook

将apk安装到模拟器中,然后启动。

将前面下载的frida-server加压,然后将内容push到手机的 data/local/tmp 目录,这里参考《软件逆向分析技术及应用》里面的内容:

在Android系统中,使用 adb push 命令推送文件到 data 目录一般需要 root 权限,但是这里有一个例外,那就是/data/local/tmp 目录。所以,frida-server一般会被存放在测试机的/data/local/tmp目录下;在将frida-server存放到测试机目录下后,使用chmod命令赋予frida-server充分的权限,这样frida-server就可以执行了。

frida-server

现在正在运行frida-server

然后补充一些其他常用知识:

用frida查看设备:

frida-ls-devices

frida-ls-devices

用frida查看包:

frida-ps -D <设备id>

frida-ps

用frida-ps查看应用:

frida-ps -D <设备id> -ai

frida-ps-ai

当然还有其他,可以:where frida查看frida在哪个文件夹,然后dir那个文件夹,找到frida开头的应用,用命令行逐一查看帮助信息:

frida-help

python的frida也是一样:

import frida
dir(frida)等等,看名字就能知道干什么的

最后结合ai的帮助

然后我们有两种方法进行hook,python和命令行

python

先说python版

将官网的hook代码复制下来,然后保存到 index.ts 中:

index.ts

有两个点需要注意:

  • 在使用 Frida 连接 Android 应用程序时,可能会遇到“ReferenceError: 'Java' 未定义”的错误,尤其是在 Frida 17.x 及更高版本中。这是因为较新的版本在通过 Python API 运行脚本时不再自动包含 Java 桥接模块,所以第一行需要导入桥。
  • 因为是ts文件,所以注意第11行

然后用frida-compile进行编译,输出js文件:

frida-compile

然后写py脚本test.py:

import frida, sys,os
os.chdir(r'D:\temp\temp_env\frida_test')  # 这个路径需要改
with open('index.js','r',encoding='utf-8') as d:
   script_code=d.read()


def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)


device=frida.get_device("192.168.137.8:5555")  # 结合实际情况
print(device)
# pid = device.spawn("com.example.seccon2015.rock_paper_scissors")
# session = device.attach(pid)
process=device.attach("rock_paper_scissors")
script = process.create_script(script_code)
script.on("message", on_message)
print('[*] Running CTF')
script.load()
# device.resume(pid)
sys.stdin.read()

我们前面的frida-server已经运行了,然后用命令行运行这个py脚本:

test.py

随便点个按钮:

button

运行成功

命令行

命令行的话不需要导入桥,直接复制官网的代码就行

cli

随便点个按钮:

cli button

也是成功运行

接下来让我们的ai补充一下知识:


1. Frida Hook 原理 (Java 层 vs Native 层)

  • 注入与通信:Frida通过 ptrace 系统调用将自身动态库注入目标进程,并建立一个管道与你的电脑通信。你在电脑端写的JavaScript代码会通过这个管道传给注入的Agent,由它执行具体操作。
  • Java 层 Hook:主要用于修改Java方法参数返回值打印调用栈等,Xposed能做到的它基本都能做到。Frida通过一个“Java桥”与Dalvik/ART虚拟机交互,用 Java.use() 获取类,再用 .implementation 替换方法实现。
  • Native 层 Hook:主要用于跟踪C/C++函数的调用,常用于分析被保护的核心算法。它直接操作内存,通过 Interceptor.attach() 劫持函数入口和出口,性能更高。像文章中的 calc() 函数就是一个Native方法。

2. frida-java-bridge 是什么?

它就是一个Frida脚本里的Java运行时互操作层,让你能在JS里和Java虚拟机对话。

从Frida 17.0开始,桥接器不再默认集成,需要手动导入。在 *.ts 脚本中这样写:

import Java from "frida-java-bridge";

然后在Python里用 frida-compile 编译。

3. frida-compile 如何用?

它是个将TypeScript/ES6代码打包成单个JS文件的构建工具,方便你进行模块化开发和代码压缩。

基本用法:先在项目目录初始化并安装它:

npm init -y
npm install --save-dev frida-compile

然后在 package.json 中添加脚本:

"scripts": {
  "build": "frida-compile index.ts -o index.js",
  "watch": "frida-compile index.ts -o index.js -w"
}

之后运行 npm run build 编译,或用 npm run watch 实现自动编译。

4. frida-server 常见问题及解决

  • Permission denied / No such file or directory:通常是架构不匹配,或文件没有执行权限。执行 adb shell chmod 755 /data/local/tmp/frida-server 赋予执行权限。若路径正确但提示找不到文件,检查是否使用了正确的命令:adb shell "su -c /data/local/tmp/frida-server &"
  • SELinux 策略阻止:可尝试临时关闭SELinux的强制模式:adb shell su -c "setenforce 0"
  • 版本不匹配:必须确保 frida-server 版本与电脑端的Frida版本一致,使用 pip show frida 查看版本后,去GitHub Releases下载对应版本的Server。
  • 端口冲突:确保设备上没有多个 frida-server 实例在运行,先用 ps -ef | grep frida 查找PID,再用 kill -9 <PID> 结束。

5. 模拟器连接失败怎么办?

在模拟器上连接失败很常见,推荐使用TCP/IP方式连接,比USB模式更稳定。

  1. 确保模拟器已开启root权限和网络ADB调试。
  2. 执行 adb devices 确认设备已连接,并转发端口:adb forward tcp:27042 tcp:27042adb forward tcp:27043 tcp:27043
  3. 最后用命令连接:frida-ps -H 127.0.0.1:27042

如果还不行,检查模拟器CPU架构与 frida-server 是否匹配(例如 x86_64 vs x86),以及本机Python环境是否为3.8+。


免责声明
本文仅供技术学习与研究目的,请勿用于任何商业用途或非法行为。本文所涉及的技术分析基于公开的前端代码,旨在帮助开发者理解Web安全与逆向工程技术。使用本文所述技术时,请确保遵守相关法律法规和目标网站的服务条款。作者不对因使用本文内容而产生的任何后果承担责任。

全文完