搞安卓逆向我们都会遇到算法加密,通常我们都是hook写好脚本,frida注入看结果,然后在hook再去跟结果,平常逆向的产品多了再怎么都会遇到底层加密算法想同的产品,重复的去写hook太麻烦了,于是我整理了一份市场上通用的加密算法通杀脚本,涵盖MD5,SHA,MAC,DES,3DES,AES,RSA以及数字签名的算法通杀hook,使用这脚本只要系统函数底层调用了此加密的api就一定能hook到值,再去打印出堆栈就更好的定位到关键代码,并且秉着技术交流的想法对每种算法hook做进一步解释说明,此系列一个加密一篇文章
封装工具函数堆栈函数的打印一定要封装进去,如果说我们hook到加密值得话那么就顺带调用他的堆栈直接跟栈做代码定位就方便多了
function stack_print() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(
Java.use("java.lang.Throwable").$new()
)
);
}
为了便于我们对数据得查看编码函数也一定要安排上,因为大多时候我们hook底层的API函数得到的都是数组值形式
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
function tobase64(tag, data) {
console.log(tag + " base64: ", ByteString.of(data).base64());
}
function toHex(tag, data) {
console.log(tag + " Hex: ", ByteString.of(data).hex());
}
function toUtf8(tag, data) {
console.log(tag + " Utf8: ", ByteString.of(data).utf8());
}
MAC算法的通杀hook
首先还是贴底层调用MAC的算法代码,通过代码我们可以得到几个需要hook的点,第一要得到update明文数据,第二得到doFinal后的数据和结果,第三我们也可以去hook-init或者SecretKeySpec得到密钥
public class MAC {
public static String getMAC(String plainText) throws Exception {
// 首先必须有个密钥
SecretKeySpec hmacMD5Key = new SecretKeySpec("a123456789".getBytes(), 1, 8, "HmacSHA1");
// 得到一个方法
Mac hmacMD5 = Mac.getInstance("HmacSHA1");
// 初始化密钥
hmacMD5.init(hmacMD5Key);
//传入数据
hmacMD5.update(plainText.getBytes());
return ByteString.of(hmacMD5.doFinal("saltstr".getBytes())).hex();
}
}
Java在线开发文档中去寻找到MAC的类路径以及下面的一些必用函数
水水水水水水水,写不动了脑子糊了,后续再来补坑,直接贴代码了,这些玩意其实说不上难,主要是对底层代码的解释有点让人难搞
var mac = Java.use("javax.crypto.Mac");
//获取到密钥,init方法三个重载
mac.init.overload('java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (key, AlgorithmParameterSpec) {
console.log("Mac.init('java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!");
return this.init(key, AlgorithmParameterSpec);
}
mac.init.overload('java.security.Key').implementation = function (key) {
console.log("Mac.init('java.security.Key') is called!");
// getAlgorithm为静态方法使用this调用
var algorithm = this.getAlgorithm();
// 获取到方法名
var tag = algorithm + " init Key";
var keyBytes = key.getEncoded();
toUtf8(tag, keyBytes);
toHex(tag, keyBytes);
tobase64(tag, keyBytes);
console.log("=======================================================");
return this.init(key);
}
// 获取传入的数据,四个重载方法都写上
mac.update.overload('byte').implementation = function (data) {
console.log("Mac.update('byte') is called!");
return this.update(data);
}
mac.update.overload('java.nio.ByteBuffer').implementation = function (data) {
console.log("Mac.update('java.nio.ByteBuffer') is called!");
return this.update(data);
}
mac.update.overload('[B').implementation = function (data) {
console.log("Mac.update('[B') is called!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " 调用update得到的数据:";
toUtf8(tag, data);
toHex(tag, data);
tobase64(tag, data);
console.log("=======================================================");
return this.update(data);
}
mac.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {
console.log("Mac.update('[B', 'int', 'int') is called!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " 调用update得到的数据:";
toUtf8(tag, data);
toHex(tag, data);
tobase64(tag, data);
console.log("=======================================================", start, length);
return this.update(data, start, length);
}
// 获取加密后的值。这里本来有几个重载的为啥只写这一个呢就是牵扯到java底层代码的解释,我自己也绕蒙了,反正写这一个够了
mac.doFinal.overload().implementation = function () {
console.log("Mac.doFinal() is called!");
var result = this.doFinal();
var algorithm = this.getAlgorithm();
var tag = algorithm + " 调用doFinal返回输出的数据:";
toHex(tag, result);
tobase64(tag, result);
console.log("=======================================================");
return result;
}



