【Frida Android】实战篇11:企业常用加密场景 Hook(1)

文章目录

    • [1. 前言](#1. 前言)
    • [2. 准备工作](#2. 准备工作)
    • [3. Hook 实现与分析](#3. Hook 实现与分析)
      • [3.1 Hook思路](#3.1 Hook思路)
      • [3.2 完整脚本与代码解析](#3.2 完整脚本与代码解析)
      • [3.3 Hook结果说明](#3.3 Hook结果说明)
        • [关于"MD5 | 明文:ELF"的说明:](#关于“MD5 | 明文:ELF”的说明:)
    • [4. 章节总结](#4. 章节总结)

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

1. 前言

在企业级应用的安全架构中,哈希算法是保障数据机密性的核心技术之一,其80%的应用场景集中在三大核心领域:密钥存储、用户口令加密和敏感数据脱敏

然而,哈希算法的安全性并非一劳永逸------随着算力的飞速提升,MD5、SHA-1等早期算法已被证实存在碰撞漏洞,可被轻易破解;即便是采用强哈希算法,若直接对原始数据进行哈希计算(无加盐、低迭代次数),也极易遭受暴力破解或彩虹表攻击(现代GPU每秒可完成数十亿次哈希计算,简单哈希几乎无防御能力),同时无法抵御"撞库攻击"(攻击者利用其他平台泄露的明文-密文对匹配当前系统)。

基于企业实际安全需求,本文聚焦两类最具代表性的哈希场景:

  • 纯哈希场景:以SHA-256、SHA-512为核心(这两种算法是当前大厂普遍采用的强哈希标准,抗碰撞性和安全性经过长期验证),通过Hook哈希计算的核心类,可直接捕获明文与密文的对应关系,适用于检测基础哈希逻辑的安全性;
  • 口令哈希场景:以PBKDF2为核心(业界公认的安全密钥派生函数,通过"哈希+随机盐+高迭代次数"显著提升破解难度),需完整捕获明文、盐、迭代次数、算法名等关键参数,才能还原加密链路,这是企业级口令存储的主流方案(符合NIST等安全标准)。

选择这两个场景的原因在于:它们覆盖了企业从基础哈希应用到高级安全加固的全链路需求,且均为实际业务中最易出现安全漏洞的环节------纯哈希场景考验基础算法选型,口令哈希场景则考验加密参数配置的严谨性。

2. 准备工作

本章节使用的示例 APK、相关源码如下::

链接: https://pan.baidu.com/s/1Kj4PaVI2t587-k4khYpMZA?pwd=irce

提取码: irce

为顺利开展Hook实验,需完成以下准备工作:

  1. 启动Frida服务:在目标Android模拟器中部署并启动frida-server,步骤同前面几章节(如图所示)。

  2. 安装示例APK:将待测试的混淆APK(包含纯哈希和PBKDF2加密逻辑)拖进模拟器中安装。

3. Hook 实现与分析

3.1 Hook思路

企业哈希场景的Hook核心是"跟踪加密链路的关键节点":

  • 纯哈希场景 :Java中MessageDigest是所有哈希算法(如SHA-256、SHA-512)的统一入口,其digest方法负责接收明文并输出密文,因此Hook该方法即可捕获算法类型、明文和密文;
  • 口令哈希(PBKDF2)场景 :PBKDF2的加密过程涉及多个核心组件------PBEKeySpec存储明文、盐、迭代次数等参数,SecretKeyFactory指定加密算法(如PBKDF2WithHmacSHA256),Base64Encoder负责最终密文的编码,因此需依次Hook这三个类的关键方法,才能完整还原加密链路。

3.2 完整脚本与代码解析

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

const PACKAGE_NAME = "com.example.fridaapk";

// char数组转明文
function charToString(pwdCharArr) {
  if (!pwdCharArr) return "空";
  let pwd = "";
  for (let i = 0; i < pwdCharArr.length; i++) pwd += pwdCharArr[i];
  return pwd;
}

// 字节数组转明文
function bytesToString(byteArr) {
  if (!byteArr || byteArr.length === 0) return "空";
  try {
    // 尝试将字节数组转换为字符串(适用于可打印字符)
    const str = Java.use("java.lang.String").$new(byteArr, "UTF-8");
    return str.toString();
  } catch (e) {
    // 转换失败则返回十六进制(适用于二进制数据如盐)
    return getHexBytes(byteArr);
  }
}

// 字节数组转16进制
function getHexBytes(byteArr) {
  if (!byteArr || byteArr.length === 0) return "空";
  let hex = "";
  for (let i = 0; i < byteArr.length; i++) {
    const b = byteArr[i] & 0xFF;
    hex += (b < 16 ? "0" : "") + b.toString(16);
  }
  return hex;
}

Java.perform(() => {
  try {
    // Hook MessageDigest检测纯哈希算法
    const MessageDigest = Java.use("java.security.MessageDigest");
    MessageDigest.digest.overload('[B').implementation = function (inputBytes) {
      const algorithm = this.getAlgorithm(); // 获取当前哈希算法(如SHA-256)
      const result = this.digest(inputBytes); // 执行原始哈希计算
      // 打印算法类型、输入明文、输出密文
      console.log(`哈希算法: ${algorithm} | 明文: ${bytesToString(inputBytes)} | 密文: ${getHexBytes(result)}`);
      console.log("----------------------------------------");
      return result;
    };
    console.log("----------------------------------------");


    // Hook PBEKeySpec获取输入参数(PBKDF2核心参数)
    const PBEKeySpec = Java.use("javax.crypto.spec.PBEKeySpec");
    PBEKeySpec.$init.overload("[C", "[B", "int", "int").implementation = function (pwdChar, salt, iter, keyLen) {
      // 打印明文(char数组转字符串)、盐(字节数组转十六进制)
      console.log(`明文:${charToString(pwdChar)} | 盐:${getHexBytes(salt)}`);
      // 打印迭代次数和密钥长度(PBKDF2安全强度的关键指标)
      console.log(`迭代次数:${iter} | 密钥长度:${keyLen}位`);
      return this.$init(pwdChar, salt, iter, keyLen); // 执行原始初始化
    };

    // Hook SecretKeyFactory获取算法名(确认PBKDF2的具体实现)
    const SecretKeyFactory = Java.use("javax.crypto.SecretKeyFactory");
    SecretKeyFactory.getInstance.overload("java.lang.String").implementation = function (algo) {
      console.log(`算法:${algo}`); // 如PBKDF2WithHmacSHA256
      return this.getInstance(algo); // 执行原始方法
    };

    // Hook Base64编码获取最终密文(PBKDF2结果的常见存储形式)
    const Base64Encoder = Java.use("java.util.Base64$Encoder");
    Base64Encoder.encodeToString.overload('[B').implementation = function (byteArray) {
      const result = this.encodeToString(byteArray); // 执行原始Base64编码
      console.log("密文(Base64):", result); // 打印最终存储的密文
      console.log("----------------------------------------");
      return result;
    };
  } catch (error) {
    console.error("Hook执行出错:", error.message);
  }
});

同前面几个章节的步骤,通过命令npm run watch编译 hook 脚本。

代码关键部分解析:
  1. 辅助函数

    • charToString:将PBEKeySpec中存储明文的char数组转换为字符串(口令通常以char数组形式传入,避免内存泄露);
    • bytesToString:尝试将字节数组转为UTF-8字符串(适用于明文),失败则转为十六进制(适用于二进制数据如盐);
    • getHexBytes:将字节数组转为十六进制字符串(便于密文、盐等二进制数据的可读性展示)。
  2. 纯哈希Hook(MessageDigest)

    通过HookMessageDigest.digest(byte[])方法,在计算哈希前后分别获取算法类型(getAlgorithm())、输入明文(inputBytes)和输出密文(result),直接关联明文与密文的对应关系。

  3. PBKDF2链路Hook

    • PBEKeySpec:Hook构造方法获取口令明文(pwdChar)、盐(salt)、迭代次数(iter)、密钥长度(keyLen),这些是PBKDF2的核心安全参数;
    • SecretKeyFactory:HookgetInstance方法获取具体算法(如PBKDF2WithHmacSHA256),确认哈希函数的实现;
    • Base64Encoder:HookencodeToString方法获取最终密文(企业通常将PBKDF2结果Base64编码后存储)。

3.3 Hook结果说明

运行脚本后,操作应用中与哈希相关的功能(在示例应用中点击"纯SHA256""SHA512""PBKDF2加密"3个按钮),控制台会输出如下信息(如图所示):

  • 纯哈希场景:点击按钮后,会直接打印"哈希算法: SHA-256 | 明文: xxx | 密文: xxx",清晰展示输入输出关系;
  • PBKDF2场景:会依次打印明文、盐、迭代次数、算法名、最终Base64密文,完整还原加密全链路。
关于"MD5 | 明文:ELF"的说明:

启动时可能出现MD5相关打印,这是Android系统加载SO库时的底层校验行为(系统会对SO文件的ELF头进行MD5哈希校验,确保文件完整性),与应用业务逻辑无关,可直接忽略。

4. 章节总结

本章通过Frida实现了企业级哈希场景的Hook,核心方法包括:

  • 利用Java.use获取目标类(如MessageDigestPBEKeySpec);
  • 重写关键方法(如digest、构造方法、getInstance)的implementation,在保留原始功能的同时插入日志逻辑;
  • 通过辅助函数处理字节/字符数组与字符串的转换,提升输出可读性。

选择"纯哈希(SHA-256/SHA-512)"和"口令哈希(PBKDF2)"作为案例,是因为它们构成了企业加密场景的"基础+进阶"体系:

  • 纯哈希是最基础的加密形式,考验算法选型的安全性(需摒弃弱哈希);
  • PBKDF2是企业级口令存储的进阶方案,通过多参数协同提升安全性,是大厂安全基线的核心要求。

掌握这两类场景的Hook方法,可有效检测加密实现是否符合安全标准,为企业数据安全加固提供依据。

相关推荐
城东米粉儿9 分钟前
android StrictMode 笔记
android
qq 137401861112 分钟前
解读ASTM D7386标准:守护物流环节astmd7386的包装安全底线
功能测试·可用性测试·安全性测试
Zender Han12 分钟前
Flutter Android 启动页 & App 图标替换(不使用任何插件的完整实践)
android·flutter·ios
童无极24 分钟前
Android 弹幕君APP开发实战01
android
赛恩斯40 分钟前
kotlin 为什么可以在没有kotlin 环境的安卓系统上运行的
android·开发语言·kotlin
于山巅相见41 分钟前
【3588】Android动态隐藏导航栏
android·导航栏·状态栏·android11
乡野码圣42 分钟前
【RK3588 Android12】开发效率提升技巧
android·嵌入式硬件
eybk1 小时前
Beeware生成安卓apk取得系统tts语音朗读例子
android
qq 13740186112 小时前
GB/T 35774:守护运输安全的包装测试GBT35774“指南针”
功能测试·可用性测试·安全性测试