【Frida Android】基础篇4:Java层Hook基础——调用静态方法

文章目录

  • [1 基本概念](#1 基本概念)
  • [2 Hook 语法](#2 Hook 语法)
  • [3 案例讲解](#3 案例讲解)
    • [3.1 核心源码分析](#3.1 核心源码分析)
    • [3.2 代码逻辑分析](#3.2 代码逻辑分析)
        • [1. 核心组件与流程](#1. 核心组件与流程)
        • [2. 验证逻辑(核心)](#2. 验证逻辑(核心))
        • [3. 解密细节(辅助理解)](#3. 解密细节(辅助理解))
    • [3.3 绕过思路](#3.3 绕过思路)
    • [3.4 Frida 脚本](#3.4 Frida 脚本)
  • [4 技术总结](#4 技术总结)

⚠️本博文所涉安全渗透测试技术、方法及案例,仅用于网络安全技术研究与合规性交流,旨在提升读者的安全防护意识与技术能力。任何个人或组织在使用相关内容前,必须获得目标网络 / 系统所有者的明确且书面授权,严禁用于未经授权的网络探测、漏洞利用、数据获取等非法行为。

1 基本概念

在Java中,静态方法是属于类本身的方法,而非类的实例对象。它可以直接通过类名调用(如ClassName.methodName()),无需创建类的实例。这种特性使得静态方法在工具类、工具函数等场景中被广泛使用。

在Hook技术中,静态方法的Hook与实例方法存在一定差异:

  • 定位方式:需通过类本身而非实例对象定位方法
  • 调用方式:Hook后的静态方法仍可通过类名直接调用
  • 参数处理:静态方法没有隐含的this参数,仅处理显式声明的参数

对于逆向分析而言,静态方法常作为核心逻辑的载体(如验证逻辑、加密解密等),因此掌握静态方法的Hook技巧是Java层逆向的重要基础。

2 Hook 语法

使用Frida对Java静态方法进行Hook的核心语法如下:

  1. 定位目标类

    通过Java.use('完整类名')获取目标类的引用(静态方法属于类,无需实例化):

    javascript 复制代码
    var TargetClass = Java.use('com.example.TargetClass');
  2. Hook静态方法

    通过类名.方法名.implementation重写静态方法实现:

    javascript 复制代码
    TargetClass.staticMethod.implementation = function(参数列表) {
        // 自定义逻辑(如打印参数、修改参数、跳过原逻辑等)
        console.log("原始参数:", 参数列表); // 打印原始参数
        var newParams = [修改后的参数]; // 构造新参数
        var result = this.staticMethod(...newParams); // 调用原始方法(this指向类本身)
        return result; // 返回结果(若有返回值)
    };
  3. 主动调用静态方法

    若需手动触发静态方法,可直接通过类名调用:

    javascript 复制代码
    TargetClass.staticMethod(参数); // 调用静态方法(会触发Hook逻辑)

核心特点:静态方法的Hook无需依赖类的实例,直接通过类引用操作,且this在静态方法Hook中指向类本身。

3 案例讲解

本章示例应用的链接:

https://pan.baidu.com/s/16EE2XE-OZS_xBRPlWUODbw?pwd=n2vb

提取码: n2vb

使用APK:Challenge 0x2.apk

打开示例应用:Challenge 0x2.apk,没有操作组件。

使用 JADX-GUI 反编译 APK,查看应用的核心类 MainActivity

3.1 核心源码分析

java 复制代码
/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
    static TextView t1;

    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        t1 = (TextView) findViewById(R.id.textview);
    }

    public static void get_flag(int a) throws BadPaddingException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
        if (a == 4919) {
            try {
                SecretKeySpec secretKeySpec = new SecretKeySpec("HILLBILLWILLBINN".getBytes(), "AES");
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                IvParameterSpec iv = new IvParameterSpec(new byte[16]);
                cipher.init(2, secretKeySpec, iv);
                byte[] decryptedBytes = cipher.doFinal(Base64.decode("q7mBQegjhpfIAr0OgfLvH0t/D0Xi0ieG0vd+8ZVW+b4=", 0));
                String decryptedText = new String(decryptedBytes);
                t1.setText(decryptedText);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

3.2 代码逻辑分析

这个APK的核心功能是通过验证后解密并显示flag,我们先拆解其逻辑:

1. 核心组件与流程
  • MainActivity:主界面活动,初始化时绑定了一个TextView (t1)用于显示结果。
  • 核心方法get_flag(int a):静态方法,接收一个整数参数a,是验证的关键。
2. 验证逻辑(核心)

get_flag方法的逻辑非常明确:

  • 只有当传入的参数a == 4919时,才会执行后续的AES解密操作;
  • 解密成功后,将结果显示在TextView t1上;
  • a != 4919,则不执行解密,自然也不会显示flag。
3. 解密细节(辅助理解)

当验证通过(a=4919)时,会执行AES解密:

  • 密钥:"HILLBILLWILLBINN".getBytes()(固定密钥);
  • 加密模式:AES/CBC/PKCS5Padding
  • IV向量:new byte[16](16个0字节);
  • 待解密数据:Base64编码的字符串"q7mBQegjhpfIAr0OgfLvH0t/D0Xi0ieG0vd+8ZVW+b4="

3.3 绕过思路

要让任意输入(或无需满足a=4919)都能显示flag,核心是get_flag方法中的验证条件a == 4919永远成立

具体方案:

  1. 通过Frida Hook get_flag方法,忽略原始参数a,强制传入4919,让解密逻辑必然执行。

  2. 原 APP 没有提供触发get_flag的入口 ,导致即使 Hook 了参数替换逻辑,也没机会执行。因此需要通过MainActivity.get_flag(xxx),直接 "手动触发" 这个方法,让 Hook 逻辑有机会生效。

3.4 Frida 脚本

javascript 复制代码
import Java from 'frida-java-bridge';

Java.perform(function () {
    // 定位目标类(静态方法属于类,直接通过类名获取)
    var MainActivity = Java.use('com.ad2001.frida0x2.MainActivity');
    
    // Hook静态方法get_flag
    MainActivity.get_flag.implementation = function (a) {
        console.log("原始参数a: " + a);
        // 忽略原始参数,强制传入4919,使验证通过
        this.get_flag(4919); // this指向MainActivity类本身
    };

    // 主动调用静态方法,传入任意参数(这里传1111)触发Hook逻辑
    MainActivity.get_flag(1111);
});

脚本说明

  1. 定位类与方法 :通过Java.use('com.ad2001.frida0x2.MainActivity')获取目标类,由于get_flag是静态方法,直接通过类名调用get_flag.implementation进行Hook。
  2. 修改参数 :Hook后,不管外部传入的a是什么值,都强制用4919调用原始get_flag方法,此时验证条件a == 4919必然成立,解密逻辑会执行并显示flag。
  3. 主动调用触发 Hook :当脚本执行MainActivity.get_flag(1111)时,因为我们已经 Hook 了get_flag的实现,所以会先进入 Hook 后的函数(而不是原始函数)。

启动脚本:仅修改PACKAGE_NAME包名变量的值
注意 "./js/compiled_hook.js"与你的本地代码路径一致,后续不再赘述。

python 复制代码
import frida
import sys
import time

def on_message(message, data):
    if message['type'] == 'send':
        print(f"[Hook 日志] {message['payload']}")
    elif message['type'] == 'error':
        print(f"[错误] {message['stack']}")

# 目标应用包名
PACKAGE_NAME = "com.ad2001.frida0x3"

def main():
    try:
        device = frida.get_usb_device(timeout=10)
        print(f"已连接设备:{device.name}")

        print(f"启动进程 {PACKAGE_NAME}...")
        pid = device.spawn([PACKAGE_NAME])
        device.resume(pid)
        time.sleep(2)

        process = device.attach(pid)
        print(f"已附加到进程 PID: {pid}")

        with open("./js/compiled_hook.js", "r", encoding="utf-8") as f:
            js_code = f.read()

        script = process.create_script(js_code)
        script.on('message', on_message)
        script.load()
        print("JS 脚本注入成功,开始监控...(按 Ctrl+C 退出)")

        sys.stdin.read()

    except frida.TimedOutError:
        print("未找到USB设备")
    except frida.ProcessNotFoundError:
        print(f"应用 {PACKAGE_NAME} 未安装")
    except FileNotFoundError:
        print("未找到 js 脚本,请检查路径")
    except Exception as e:
        print(f"异常:{str(e)}")
    finally:
        if 'process' in locals():
            process.detach()
        print("程序退出")

if __name__ == "__main__":
    main()

使用方法

代码结构和启动方式与上一章完全一致(本系列案例基本保持一致):启动 frida_server → 编译 hook.js 脚本 → 执行 run.py 启动 hook 注入。

shell 复制代码
npm run watch

效果

执行脚本后,get_flag方法的验证被绕过,实现了"任意条件下显示结果"的目标。

4 技术总结

  1. 静态方法Hook核心要点

    • 定位方式:通过Java.use('类名')直接获取类引用,无需实例化
    • 方法重写:使用类名.静态方法名.implementation修改实现逻辑
    • 调用特性:this在静态方法Hook中指向类本身,调用原始方法需用this.方法名(参数)
  2. 参数篡改技巧

    对于带验证逻辑的静态方法,可通过在Hook中替换参数(如案例中强制传入4919)绕过条件判断,直接执行核心逻辑。

  3. 主动调用的必要性

    当目标静态方法无自然触发入口时,需在Hook脚本中主动调用(类名.方法名(参数)),强制触发Hook逻辑并执行目标代码。

  4. Frida Java层Hook流程

    定位目标类 → 重写目标方法 → 自定义Hook逻辑(参数/返回值处理) → 触发目标方法(自然触发或主动调用),该流程适用于绝大多数Java层静态方法Hook场景。

相关推荐
怪兽20144 小时前
主线程 MainLooper 和一般 Looper 的异同?
android·面试
洋不写bug4 小时前
数据库的创建,查看,修改,删除,字符集编码和校验操作
android·数据库·adb
程序员三藏5 小时前
银行测试:第三方支付平台业务流,功能/性能/安全测试方法
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·安全性测试
2501_915909065 小时前
iOS App 上架全流程详解:证书配置、打包上传、审核技巧与跨平台上架工具 开心上架 实践
android·ios·小程序·https·uni-app·iphone·webview
2501_915106325 小时前
iOS 26 系统流畅度测试实战分享,多工具组合辅助策略
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_915918415 小时前
开发 iOS 应用全流程指南,环境搭建、证书配置与跨平台使用 开心上架 上架AppStore
android·ios·小程序·https·uni-app·iphone·webview
思想是一切事物的源头5 小时前
渗透测试所需域名和IP信息收集方法
网络·网络协议·tcp/ip·安全性测试
灵芸小骏5 小时前
Rokid应用实践:基于CXR-M与CXR-S SDK,打造眼镜与手机协同的‘智能随行记录仪’
android
奔跑中的蜗牛6665 小时前
直播 QoE 监控体系设计与落地(三):原生卡顿优化实践
android