首先解压,在assets文件夹下在找到了要汇编的文件:appmgr.jar,这个文件在安装后在/data/data/<package name>/files/文件夹下会生成一个appmgr.apk,
「其实只是一个存放代码的资源文件,安装的时候重命名了,以apk结尾罢了。」
-------------2014.10.9修正---------------
上面打中文引号的这句话有待考究,如果直接解压得到classes.dex,那个dex只有6kB,而安装后提取的classes.dex有278.23KB,这篇文章用到的是后者。
解压这个apk文件,得到classes.dex,拖到IDA Pro 6.1以上版本里面去,可以识别成Android的dex文件。
鉴于要找signature_md5的算法,用alt+t查找「signature_md5」字符串,找到:
CODE:0001ACCE const-string/jumbo v1, aSignature_md5 # "signature_md5"//定义一个aSignature_md5字符串,放到v1寄存器
//Dalvik指令集看这里
接下来对v1做了这些事情:
从JSON里拿到一个键值对放到v2,v1里;
move-result-object,把数据放回v1里;
check-cast,转换成String格式;
定义逗号,放到v8里;//为什么会有逗号出现?
用v8里的逗号,调用String的split方法 split v1,注意split返回的是分割后的数组;
把split之后的数组放到v11里;
下面就不太重要了因为定义了Edition_brief这个变量,下面追踪v11:
array-length开始,把v11的数组长度放到v9里去;
下面if-ge, greater or equal,v2>=v9就跳转到loc_1AD72,注意v2上面定义了是0;就是如果长度等于零则跳转,注意这个v9是JSON里拿到的字符串分割成数组的长度;
if-nez,not equal zero,v8!=0就跳转到loc_1AD72,注意上面刚定义v8等于0,这里有些迷糊;
下一句从packageInfo_signatures得到签名放到v13,v10;
注意Arrays.toString(ref);和xx.toString();是不同的。
xx.toString();输出的东西是[@XXXXX一串,而且好像每次都不一样,大神猜测是地址,网上有人说是HASH。
数值数组是不能够整体输出的,
你可以使用循环输出
for(i=0;i<10;i++)
System.out.println(a[i]+" ");
而借助Arrays.toString(a)就节省了上面的循环步骤,而一步输出(当然,调用函数的内部进行了处理)。
然后注意v13中存储了v13,v13又给了v23,v23经过一系列转换格式,最后用一个ad.a(ref)得到了signature_md5;
然后倒数第三行,v11和v2给了v24;
v23和v24比较,亦即PackageInfo_signatures经过ad.a(ref)后,与JSON中的MD5比较。
到此可以看到要找的函数就是ad.a(byte [] x );
此时用dex2jar把dex反编译成jar,放到jd-gui里,找到ad.a():
TIP:昨天才发现函数名冒号后面的代表返回类型。a这个函数里是这样的:
可是循着w.f()找过去发现没有getMD5()。
大神提议用Notepad++的搜索文件中的字符串功能搜索360 apk整个文件反编译出的smali,不知道他怎么想到的。
找到了。下面就是追踪混淆代码了,需要极大的耐心。
evt.c(),然后找到evg.b(),
找到eua.d();本来在eua里找这个a(paramArrayOfByte)怎么也找不到,转念一想才想起来应该在b所在的函数evg里找!
最后找到了。
这个真需要很大耐心。我的话恐怕某一步早就放弃了,因为难以接受弄了半天弄不出的结果。大神牛B。
2014年8月7日。
--------------------------2014年10月30日---------------------------
昨天花时间跟了一下另一个混淆了的应用的某个算法,最后跟到了,记录一下。
首先是用jd-gui从混淆的类中跟到的相关的一系列程序片段(只是思路,并没有遵循语法):
//chasing the signmd5 algorithm u localu = (u)localIterator.next(); localJSONObject2.put("signmd5", localu.a(this.a));//start from here,find localu.a(this.a)! this.c = paramJSONObject.getString("PackageName"); private long d = -1L; //localu.a(this.a) public long a(Context paramContext) { if (this.d == -1L) { PackageInfo localPackageInfo = j.a(paramContext, this.c); if (localPackageInfo != null) this.d = j.a(j.a(localPackageInfo.signatures[0].toCharsString().getBytes())); } return this.d; } //the first j.a(byte[] paramArrayOfByte) returns us a String //the second j.a(String paramArrayOfByte) returns us a long int,which is presented to this.d //j.a(byte[] paramArrayOfByte) public static String a(byte[] paramArrayOfByte) { try { MessageDigest localMessageDigest = MessageDigest.getInstance("MD5"); localMessageDigest.update(paramArrayOfByte); String str = b(localMessageDigest.digest()); return str; } catch (NoSuchAlgorithmException localNoSuchAlgorithmException) { return ""; } catch (Exception localException) { } return ""; } //j.a(String paramArrayOfByte) public static long a(String paramString) { long l1 = 0L; int i = 8; if ((TextUtils.isEmpty(paramString)) || (paramString.length() < 32)) return -1L; String str = paramString.substring(i, 24); int j = 0; long l2 = l1; while (j < i) { l2 = l2 * 16L + Integer.parseInt(str.substring(j, j + 1), 16); j++; } while (i < str.length()) { l1 = l1 * 16L + Integer.parseInt(str.substring(i, i + 1), 16); i++; } return 0xFFFFFFFF & l1 + l2; } //j.a(Context paramContext, String paramString) public static PackageInfo a(Context paramContext, String paramString) { try { PackageInfo localPackageInfo = paramContext.getPackageManager().getPackageInfo(paramString, 64); return localPackageInfo; } catch (Exception localException) { } return null; } //b(localMessageDigest.digest()); public static String b(byte[] paramArrayOfByte) { if (paramArrayOfByte == null) return ""; StringBuilder localStringBuilder = new StringBuilder(2 * paramArrayOfByte.length); for (int i = 0; i < paramArrayOfByte.length; i++) { localStringBuilder.append(b[((0xF0 & paramArrayOfByte[i]) >>> 4)]); localStringBuilder.append(b[(0xF & paramArrayOfByte[i])]); } return localStringBuilder.toString(); }
可以看出只是要找到“localu.a(this.a)”,localu是u这个类的对象,所以到u里面找a的各种方法。
好了最后找到之后写成Android代码:
package com.larry.baidusignmd5; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import android.app.Activity; import android.content.Context; import android.content.pm.PackageInfo; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; public class MainActivity extends Activity { // Context context = getApplicationContext();// is it right? Context context = MainActivity.this; private long signmd5 = -1L; String two = null; private static final char[] b = { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102 }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String pkgName = "com.phantomz.lightball"; PackageInfo localPackageInfo = one(context, pkgName); byte[] sig = localPackageInfo.signatures[0].toCharsString().getBytes(); two = two(sig); signmd5 = three(two); String stringmd5 = Long.toString(signmd5); Log.d("signmd5",stringmd5); } // j.a(Context paramContext, String paramString) public static PackageInfo one(Context paramContext, String paramString) { try { PackageInfo localPackageInfo = paramContext.getPackageManager() .getPackageInfo(paramString, 64); return localPackageInfo; } catch (Exception localException) { } return null; } // j.a(byte[] paramArrayOfByte) public static String two(byte[] paramArrayOfByte) { try { MessageDigest localMessageDigest = MessageDigest.getInstance("MD5"); localMessageDigest.update(paramArrayOfByte); String str = b(localMessageDigest.digest()); return str; } catch (NoSuchAlgorithmException localNoSuchAlgorithmException) { return ""; } catch (Exception localException) { } return ""; } // b(localMessageDigest.digest()); public static String b(byte[] paramArrayOfByte) { if (paramArrayOfByte == null) return ""; StringBuilder localStringBuilder = new StringBuilder( 2 * paramArrayOfByte.length); for (int i = 0; i < paramArrayOfByte.length; i++) { localStringBuilder.append(b[((0xF0 & paramArrayOfByte[i]) >>> 4)]); localStringBuilder.append(b[(0xF & paramArrayOfByte[i])]); } return localStringBuilder.toString(); } // j.a(String paramArrayOfByte) public static long three(String paramString) { long l1 = 0L; int i = 8; if ((TextUtils.isEmpty(paramString)) || (paramString.length() < 32)) return -1L; String str = paramString.substring(i, 24); int j = 0; long l2 = l1; while (j < i) { l2 = l2 * 16L + Integer.parseInt(str.substring(j, j + 1), 16); j++; } while (i < str.length()) { l1 = l1 * 16L + Integer.parseInt(str.substring(i, i + 1), 16); i++; } return 0xFFFFFFFF & l1 + l2; } }
好了。可以看出同样是读signature,但是也是要读本地安装的signature;而原来的应用中似乎是可以读下载应用的signature的。