当前位置: 首页 > news >正文

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

文章目录

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

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

1 基本概念

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

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

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

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

2 Hook 语法

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

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

    var TargetClass = Java.use('com.example.TargetClass');
    
  2. Hook静态方法
    通过类名.方法名.implementation重写静态方法实现:

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

    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 核心源码分析

/* 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.Activityprotected 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 脚本

import Java from 'frida-java-bridge';Java.perform(function () {// 定位目标类(静态方法属于类,直接通过类名获取)var MainActivity = Java.use('com.ad2001.frida0x2.MainActivity');// Hook静态方法get_flagMainActivity.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"与你的本地代码路径一致,后续不再赘述。

import frida
import sys
import timedef 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 注入。

在这里插入图片描述

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场景。

http://www.dtcms.com/a/477496.html

相关文章:

  • 中国建筑网官网图片深圳网站排名优化团队
  • Vue3+Three.js实现3D模型加载与动画(实践:官方的一个样例)
  • mac maven 安装
  • 体验GPT-OSS-120B:在PH8平台上探索超大规模语言模型的强大能力
  • Spark和Flink差异
  • 怎么做网站广告赚钱专业网站设计团队
  • 网站建设这块是怎么挣钱的网站服务器买了后怎么做
  • 从注册到养号,Walmart鲲鹏系统打造完整运营链路
  • 云服务器安装JDK、Tomcat、MySQL
  • 科创企业品牌营销顾问:助力企业腾飞的关键角色
  • 怎样解读黑格尔的客观唯心主义
  • 5-2〔OSCP ◈ 研记〕❘ SQL注入攻击▸MySQL MSSQL基础
  • 电话交换机IPPBX的数据存储在AWS亚马逊云
  • 公共数据开放网站建设怎样做app
  • 嵌入式学习linux内核驱动8——IIC设备驱动和lm75-dht11
  • 经典机器学习深度学习领域数据集介绍
  • 个人网站怎么做才能值钱优设网页设计
  • 【Unity】MMORPG游戏开发(一)身份认证
  • 竞价网站与竞价网站之间做友情链接建邺区住房 建设 网站
  • Django视图与路由全解析:从URL到页面,一篇讲透
  • 推荐系统实战:python新能源汽车智能推荐(两种协同过滤+Django 全栈项目 源码)计算机专业✅
  • 数据结构二叉树——层序遍历 扩展二叉树的左视图
  • .NET Core + Nginx服务器零基础部署全流程(附资源)
  • 吴江区经济开发区规建设局网站手机app如何开发制作
  • excel中关联word邮件合并使用
  • const string getWord() ;和 string getWord() const ;是一样的效果吗
  • 语音控制 APP 开发:唤醒率 99% 的实现
  • Linux操作系统-进程(三)
  • electron中进程线程之间通信方式
  • wordpress 原图查看贵港seo