【Frida Android】基础篇6:Java层Hook基础——创建类实例、方法重载、搜索运行时实例

文章目录

    • 一、基础概念
      • [1. 创建类实例](#1. 创建类实例)
      • [2. 方法重载](#2. 方法重载)
      • [3. 搜索运行时实例](#3. 搜索运行时实例)
    • 二、Hook语法
      • [1. 定位目标类](#1. 定位目标类)
      • [2. 替换方法实现](#2. 替换方法实现)
      • [3. 创建类实例](#3. 创建类实例)
      • [4. 搜索运行时实例](#4. 搜索运行时实例)
      • [5. 处理方法重载](#5. 处理方法重载)
    • 三、应用源码分析
      • [1. MainActivity类](#1. MainActivity类)
      • [2. Check类](#2. Check类)
    • 四、案例解析
      • [1. 界面说明](#1. 界面说明)
      • [2. hook.js脚本(核心Hook逻辑)](#2. hook.js脚本(核心Hook逻辑))
      • [3. run.py脚本(Frida注入工具)](#3. run.py脚本(Frida注入工具))
      • [4. 执行效果](#4. 执行效果)
    • 五、技术总结

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

一、基础概念

1. 创建类实例

指在Hook过程中,通过动态方式生成目标类的对象实例,用于调用类中的非静态方法(非静态方法需依赖实例才能执行)。在本案例中,需创建Check类的实例以调用其get_flag方法(非静态方法),从而获取flag数据。

2. 方法重载

当类中存在多个同名但参数列表(参数类型、数量、顺序)不同的方法时,称为方法重载。Hook时需通过指定参数类型精确匹配目标重载版本,否则可能调用错误的方法。本案例中TextViewsetText方法存在多个重载版本,需明确指定CharSequence参数类型的版本以设置文本。

3. 搜索运行时实例

指在应用运行过程中,查找已被实例化的目标类对象(如ActivityView等动态创建的实例)。由于部分对象(如MainActivity实例)并非通过静态方式创建,需通过运行时搜索获取其实例,进而操作其成员变量(如本案例中的TextView控件t1)。

二、Hook语法

1. 定位目标类

通过Java.use(className)获取目标类的JavaScript包装对象,用于后续操作:

javascript 复制代码
// 定位Check类和String类
var Check = Java.use('com.ad2001.frida0x4.Check');
var JavaString = Java.use('java.lang.String');

2. 替换方法实现

通过类名.方法名.implementation重写目标方法的逻辑,可在其中调用原方法或修改行为:

javascript 复制代码
// 重写Check类的get_flag方法
Check.get_flag.implementation = function (a) {
    // 强制传入参数1337,忽略原始输入
    return this.get_flag(1337); 
};

3. 创建类实例

通过类名.$new()动态创建类的实例,用于调用非静态方法:

javascript 复制代码
// 创建Check类实例并调用get_flag方法
var flag = Check.$new().get_flag(0); 

4. 搜索运行时实例

通过Java.choose(className, options)搜索应用中已实例化的目标类对象,常用于获取Activity、View等动态创建的实例:

javascript 复制代码
// 搜索MainActivity的运行时实例
Java.choose('com.ad2001.frida0x4.MainActivity', {
    onMatch: function (activity) { // 找到实例时触发
        console.log("找到MainActivity实例");
    },
    onComplete: function () { // 搜索完成时触发
        console.log("搜索结束");
    }
});

5. 处理方法重载

当方法存在多个重载版本(参数不同)时,通过方法名.overload(参数类型列表)指定目标重载版本:

javascript 复制代码
// 调用TextView的setText(CharSequence)重载方法
// 通过call执行这个重载方法,第一个参数是 "谁来调用",第二个参数是方法的输入参数。
tv.setText.overload('java.lang.CharSequence').call(tv, JavaString.$new(flag));

三、应用源码分析

本章示例应用的链接:

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

提取码: n2vb

使用APK:Challenge 0x4.apk

1. MainActivity类

功能:应用主界面逻辑,负责初始化UI组件(TextView):

java 复制代码
public class MainActivity extends AppCompatActivity {
    TextView t1; // 声明TextView成员变量

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); // 加载布局
        this.t1 = (TextView) findViewById(R.id.txtview); // 绑定布局中的TextView
    }
}

关键分析:t1是显示文本的核心控件,Hook的目标是通过修改其内容展示flag。

2. Check类

功能:包含get_flag方法,根据输入参数返回flag(核心逻辑):

java 复制代码
public class Check {
    public String get_flag(int a) {
        if (a == 1337) { // 仅当参数为1337时返回flag
            byte[] decoded = new byte["I]FKNtW@]JKPFA\\[NALJr".getBytes().length];
            // 对字符串每个字节异或15解密
            for (int i = 0; i < "I]FKNtW@]JKPFA\\[NALJr".getBytes().length; i++) {
                decoded[i] = (byte) ("I]FKNtW@]JKPFA\\[NALJr".getBytes()[i] ^ 15);
            }
            return new String(decoded); // 返回解密后的flag
        }
        return ""; // 参数不正确时返回空
    }
}

关键分析:get_flag是flag的生成逻辑,仅当输入a=1337时通过异或运算解密字符串,否则返回空。这是Hook的核心目标方法。

四、案例解析

1. 界面说明

应用默认界面无主动触发逻辑,仅初始化TextView但未设置内容,需通过Hook强制展示flag:

2. hook.js脚本(核心Hook逻辑)

代码结构与之前保持一致,参考"基础篇2"内容 4.1 章节:【Frida Android】基础篇2:Frida基础操作模式详解

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

Java.perform(function () { // 进入Java环境执行Hook逻辑
    // 定位目标类
    var Check = Java.use('com.ad2001.frida0x4.Check');
    var JavaString = Java.use('java.lang.String');
    var MainActivityClassName = 'com.ad2001.frida0x4.MainActivity';

    // Hook get_flag方法:强制返回正确flag
    Check.get_flag.implementation = function (a) {
        return this.get_flag(1337); // 忽略原始参数,强制传入1337
    };
    // 创建Check实例并获取flag
    var flag = Check.$new().get_flag(0); // 此时调用已被Hook,实际传入1337
    console.log("获取到flag:" + flag);

    // 延迟搜索MainActivity实例(等待界面初始化完成)
    setTimeout(function () {
        console.log("开始搜索MainActivity实例...");
        Java.choose(MainActivityClassName, {
            onMatch: function (activity) { // 找到实例后操作TextView
                console.log("找到MainActivity实例");
                var tv = activity.t1.value; // 获取t1成员变量(TextView实例)
                if (tv) {
                    // 调用TextView的setText(CharSequence)重载方法设置flag
                    tv.setText.overload('java.lang.CharSequence').call(tv, JavaString.$new(flag));
                    console.log("Flag已显示");
                } else {
                    console.log("未找到TextView(变量名可能错误)");
                }
            },
            onComplete: function () {
                console.log("MainActivity实例搜索完成");
            }
        });
    }, 1000); // 延迟1秒,确保Activity已初始化
});

这里说明下为什么调用TextViewsetText方法时必须指定CharSequence类型的重载版本。

Android 中的TextView类为了支持不同类型的输入(如字符串、资源 ID、字符序列等),设计了多个setText重载方法,例如:

  • setText(String text):直接接收String类型参数
  • setText(CharSequence text):接收CharSequence接口类型参数(StringCharSequence的实现类)
  • setText(int resId):接收资源 ID(如R.string.app_name
  • 其他重载(如带参数BufferType的版本)

当通过 Frida 调用方法时,必须明确指定重载版本:

  1. 本案例中用于设置文本的flag是通过JavaString.$new(flag)创建的,其中JavaStringjava.lang.String的包装类。
  2. String类本质上是CharSequence接口的实现类(public final class String implements CharSequence),因此String对象可以直接作为CharSequence类型的参数传入。
  3. 通过overload('java.lang.CharSequence')明确指定重载版本后,Frida 能精确匹配到setText(CharSequence)方法,确保传入的String对象被正确接收,避免因类型匹配错误导致的调用失败。

3. run.py脚本(Frida注入工具)

python 复制代码
import frida
import sys
import time

def on_message(message, data):
    # 处理Hook脚本发送的日志
    if message['type'] == 'send':
        print(f"[Hook 日志] {message['payload']}")
    elif message['type'] == 'error':
        print(f"[错误] {message['stack']}")

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

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

        # 启动目标应用并获取进程ID
        print(f"启动进程 {PACKAGE_NAME}...")
        pid = device.spawn([PACKAGE_NAME])
        device.resume(pid)
        time.sleep(2) # 等待应用启动

        # 附加到进程并注入Hook脚本
        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()

4. 执行效果

  • 执行Python脚本注入Hook代码:

  • 应用界面成功显示flag:

五、技术总结

  1. 核心流程:本案例通过Frida实现Java层Hook的完整流程为:定位目标类→修改关键方法逻辑→创建实例获取数据→搜索运行时对象→操作UI组件展示结果,全程围绕"创建类实例""方法重载处理""搜索运行时实例"三个核心技术点展开。

  2. 技术点应用

    • 创建类实例:通过Check.$new()生成Check对象,解决非静态方法get_flag的调用依赖;
    • 方法重载处理:用overload('java.lang.CharSequence')指定setText的目标版本,避免调用错误重载方法;
    • 搜索运行时实例:通过Java.choose找到动态创建的MainActivity实例,获取其成员变量t1(TextView)进行操作。
  3. 实践技巧

    • 针对非静态方法,需先创建实例或获取运行时实例才能Hook;
    • 处理重载方法时,参数类型需严格匹配(如CharSequence而非String,因String是其子类);
    • 搜索运行时实例需考虑对象初始化时机,可通过setTimeout延迟执行避免实例未创建的问题。
  4. 应用扩展:该技术组合可用于逆向分析中操作动态生成的对象、调用特定重载方法、获取非静态成员变量等场景,是Java层Hook的核心实用技巧。

相关推荐
xyy20253 小时前
Spring事务的传播方式
java·数据库·spring
@Kerry~3 小时前
phpstudy .htaccess 文件内容
java·开发语言·前端
roshy3 小时前
x86、arm、rsc-v指令集架构,指令集、OS、应用3者的关系
java·arm开发·架构
CRMEB系统商城3 小时前
CRMEB多商户系统(PHP)v3.3正式发布,同城配送上线[特殊字符]
java·开发语言·小程序·php
iナナ3 小时前
Java优选算法——位运算
java·数据结构·算法·leetcode
毕设源码-钟学长3 小时前
【开题答辩全过程】以 濒危动物保护管理系统为例,包含答辩的问题和答案
java·eclipse
Han.miracle4 小时前
数据结构二叉树——层序遍历&& 扩展二叉树的左视图
java·数据结构·算法·leetcode
Orange_sparkle4 小时前
若依使用基本步骤
java·vue
kevinfkq4 小时前
Java-idea编辑器中Jar方式打包启动
java·intellij-idea·jar