介绍
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运行结果如下:

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