介绍

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。[1]

有些人把MD5列为加密算法,这其实是不正确的,因为MD5计算本身就是不可逆的。

Java实现MD5散列计算

在Java中,我们用对数据进行MD5计算,代码大概是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Utils {
public static void main(String[] args) {
String md5 = md5("luoyesiqiu".getBytes());
System.out.println(md5);
System.out.println(md5.substring(8,24));

}

public static String md5(byte[] input){
StringBuilder md5 = new StringBuilder();
try {
MessageDigest messageDigest = MessageDigest.getInstance("md5");
byte[] buf = messageDigest.digest(input);
for (byte b : buf){
int val = b;
if(val < 0){
val += 256;
}
String hex = String.format("%02x",val);
md5.append(hex);
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return md5.toString();
}
}

以上代码会输出32位的MD5值和16位的MD5值,16位MD5值是从32位中截取的:

1
2
10ff0971d5ce668c3a9c20a8c96ba43e
d5ce668c3a9c20a8

分析和实现

众所周知,MD5计算是不可逆的。如果,我们想要得到MD5计算前的数据该怎么办?想得到MD5计算前的数据,我们可以Hook呀!Hook MessageDigest类的digest方法,这个方法输入要计算的数据,返回一个计算的结果,只要Hook这个方法就能得到MD5计算前的数据和计算后的数据了,完美!Hook工具这里用的frida

在frida中,想要Hook,要用JavaScript写代码逻辑,写法可以参考我以前的博文:frida的用法–Hook Java代码篇

Hook代码

MessageDigest类的digest方法有两个重载方式,我们把它们都给Hook了。

这里创建一个名为hookMD5.js的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
var algorithm = 'MD5';

if(Java.available)
{
Java.perform(function(){
var MessageDigest= Java.use('java.security.MessageDigest');
var digest1 = MessageDigest.digest.overload("[B","int","int");
digest1.implementation=function(buf,offset,len){
var ret = digest2.call(this,buf);
parseIn(this,buf);
parseOut(this,ret);
return ret;
}

var digest2 = MessageDigest.digest.overload("[B");
digest2.implementation=function(buf){
var ret = digest2.call(this,buf);
parseIn(this,buf);
parseOut(this,ret);
return ret;
}
});

}

function parseIn(digest,input){
var Integer= Java.use('java.lang.Integer');
var String= Java.use('java.lang.String');
if(digest.getAlgorithm() != algorithm){
return;
}
try{
console.log("original:"+String.$new(input));
}
catch(e){
console.log(parseHex(input));
}
}

function parseOut(digest,ret){
var Integer= Java.use('java.lang.Integer');
var String= Java.use('java.lang.String');
var result = "";
for(var i = 0;i<ret.length;i++){
var val = ret[i];
if(val < 0){
val += 256;
}
var str = Integer.toHexString(val);
if(String.$new(str).length()==1){
str = "0" + str;
}
result += str;
}

if(digest.getAlgorithm()==algorithm){
console.log(digest.getAlgorithm() + "(32):" + result);
console.log(digest.getAlgorithm() + "(16):" + result.substring(8,24));
console.log("");
}
}

function parseHex(input){
var Integer= Java.use('java.lang.Integer');
var byte_array = "";
for(var j = 0;j<input.length;j++){
var hex = Integer.toHexString(input[j]);
if(hex.length == 1){
hex = "0" + hex;
}
byte_array += hex;
}

console.log("original(hex):");
var pair = "";
var hex_table = "";
for(var k = 0;k<byte_array.length;k++){
pair += byte_array.charAt(k);
if((k+1)%2 == 0){
pair += " "
hex_table += pair;
pair = ""
}

if((k+1)%32 == 0){
hex_table += "\n"
}
}
return hex_table;
}

写好后注入到目标进程:

frida -U -l hookMD5.js com.xxx.xxx

注:-U代表对USB设备注入;com.xxxx.xxxx是要Hook的App包名

Hook某App运行结果如下:

result

上面的frida脚本,不仅可以Hook MD5算法流程,还可以Hook SHA家族的散列算法流程,修改Javascript脚本开头的algorithm变量即可达到目的,读者可以自行尝试。

参考