【Frida Android】实战篇11:企业常用加密场景 Hook(1) - 指南
文章目录
⚠️本博文所涉安全渗透测试技术、方法及案例,仅用于网络安全技术研究与合规性交流,旨在提升读者的安全防护意识与技能能力。任何个人或组织在使用相关内容前,必须获得目标网络 / 系统所有者的明确且书面授权,严禁用于未经授权的网络探测、漏洞利用、数据获取等非法行为。
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实验,需完成以下准备工作:
启动Frida服务:在目标Android模拟器中部署并启动frida-server,步骤同前面几章节(如图所示)。

安装示例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 完整脚本与代码解析
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();
const result = this.digest(inputBytes);
console.log(`哈希算法: ${algorithm} | 明文: ${bytesToString(inputBytes)} | 密文: ${getHexBytes(result)}`);
console.log("----------------------------------------");
return result;
};
console.log("----------------------------------------");
// Hook PBEKeySpec获取输入参数
const PBEKeySpec = Java.use("javax.crypto.spec.PBEKeySpec");
PBEKeySpec.$init.overload("[C", "[B", "int", "int").implementation = function (pwdChar, salt, iter, keyLen) {
console.log(`明文:${charToString(pwdChar)} | 盐:${getHexBytes(salt)}`);
console.log(`迭代次数:${iter} | 密钥长度:${keyLen}位`);
return this.$init(pwdChar, salt, iter, keyLen);
};
// Hook SecretKeyFactory获取算法名
const SecretKeyFactory = Java.use("javax.crypto.SecretKeyFactory");
SecretKeyFactory.getInstance.overload("java.lang.String").implementation = function (algo) {
console.log(`算法:${algo}`);
return this.getInstance(algo);
};
// Hook Base64编码获取最终密文
// Hook java.util.Base64
const Base64Encoder = Java.use("java.util.Base64$Encoder");
Base64Encoder.encodeToString.overload('[B').implementation = function (byteArray) {
const result = this.encodeToString(byteArray);
console.log("密文(Base64):", result);
console.log("----------------------------------------");
return result;
};
// Hook android.util.Base64
const AndroidBase64 = Java.use("android.util.Base64");
AndroidBase64.encodeToString.overload('[B', 'int').implementation = function (input, flags) {
const result = this.encodeToString(input, flags);
console.log("密文(Base64):", result);
console.log("----------------------------------------");
return result;
};
} catch (error) {
console.error("Hook执行出错:", error.message);
}
});
同前面几个章节的步骤,通过命令npm run watch编译 hook 脚本。

代码关键部分解析:
辅助函数:
charToString:将PBEKeySpec中存储明文的char数组转换为字符串(口令通常以char数组形式传入,避免内存泄露);bytesToString:尝试将字节数组转为UTF-8字符串(适用于明文),失败则转为十六进制(适用于二进制数据如盐);getHexBytes:将字节数组转为十六进制字符串(便于密文、盐等二进制数据的可读性展示)。
纯哈希Hook(MessageDigest):
通过HookMessageDigest.digest(byte[])方法,在计算哈希前后分别获取算法类型(getAlgorithm())、输入明文(inputBytes)和输出密文(result),直接关联明文与密文的对应关系。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”的说明:
Android平台加载SO库时的底层校验行为(环境会对SO文件的ELF头进行MD5哈希校验,确保文档完整性),与应用业务逻辑无关,可直接忽略。就是启动时可能出现MD5相关打印,这
4. 章节总结
本章利用Frida实现了企业级哈希场景的Hook,核心方法包括:
- 利用
Java.use获取目标类(如MessageDigest、PBEKeySpec); - 重写关键方法(如
digest、构造方法、getInstance)的implementation,在保留原始功能的同时插入日志逻辑; - 通过辅助函数处理字节/字符数组与字符串的转换,提升输出可读性。
选择“纯哈希(SHA-256/SHA-512)”和“口令哈希(PBKDF2)”作为案例,是因为它们构成了企业加密场景的“基础+进阶”体系:
- 纯哈希是最基础的加密形式,考验算法选型的安全性(需摒弃弱哈希);
- PBKDF2是企业级口令存储的进阶方案,通过多参数协同提升安全性,是大厂安全基线的核心要求。
否符合安全标准,为企业数据安全加固提供依据。就是掌握这两类场景的Hook方法,可奏效检测加密构建
浙公网安备 33010602011771号