记一次反编译过程

此文不涉及任何道义和伦理的讨论

尝试旧版本APP,查看对应的旧版本接口是否加密

安卓的历史版本可以通过hiapk下载,资源的链接一般类似这种形式: http://apk.hiapk.com/appinfo/com.maihaoche.bentley/1050805,最后的数字用于标识不同的版本号。
IOS的历史版本可以通过itunes下载,只需修改请求中携带的版本号即可下载不同版本,具体参见:iTunes下载 App Store 任意历史版本应用


在mac上通过Genymotion可以安装安卓apk,在虚拟中配置代理地址10.0.3.2,端口8888,配合charles即可将虚拟机中请求拦截到charles中。

分析了若干历史版本之后,发现该的App2.X版本未进行加密,但是对应的域名和接口也已经停用,所以这条路走不通。

尝试反编译APP

apk的反编译可以使用:apk2gold
反编译之后,可以看到解析出的java代码。
得到代码之后如何定位到加密部分的逻辑因场景而定,在这个app中,定位到一个关键类BaseWeb,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected static TreeMap<String, Object> jiaMi(TreeMap<String, Object> treeMap, boolean z) {
...
try {
String sha1 = sha1(treeMap, z);
if (sha1 == null) {
return null;
}
treeMap2.put("key", sha1);
return treeMap2;
} catch (Exception e) {
u.a(e);
treeMap2.put("key", "");
return treeMap2;
}
}

可以看到key字段是通过sha1函数生成,具体代码如下:

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
public static String sha1(TreeMap<String, Object> treeMap, boolean z) {
if (treeMap == null) {
treeMap = new TreeMap();
}
if (!z) {
treeMap.put("publicKey", Info.a().getMoreStrInfo(AppContext.getInstance(), 345));
} else if (!mApplication.isLogin()) {
return null;
} else {
treeMap.put("publicKey", mApplication.getUser().getPrivateKey());
}
if (treeMap == null || treeMap.size() <= 0) {
return "";
}
String str = "";
int i = 0;
for (String str2 : treeMap.keySet()) {
Object obj = treeMap.get(str2);
if (obj != null) {
if (i > 0) {
str = str + "^_~";
} else {
i++;
}
...
str = str + str2 + "=" + formaterFloat(Double.parseDouble(obj.toString()));
...
}
}
// 标准的sha1加密
return i.e(str);
}

这个方法根据z的取值决定publicKey是通过Info.a().getMoreStrInfo(AppContext.getInstance(), 345)这个方法来生成,还是直接取mApplication.getUser().getPrivateKey()
因为我们感兴趣的接口的z取值都是false,所以要深入探究一下Info.a().getMoreStrInfo(AppContext.getInstance(), 345)这个方法。
反编译发现这个方法是包裹在so中,也是常见的保护敏感信息的方式。
Hopper反编译 结果:

祭出神器IDA Pro,代码可读性提高很多:


汇编无力,直接F5:
找到传入参数为345时候的对应逻辑:

其实就是简单取得了一个字符串:4cbce54e——17c70193

成功

因为参数是完全明文的,将参数和反编译获取的publicKey放入TreeMap,拼接成字符串之后执行标准sha1,即可获取一个合法的key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
TreeMap<String,Object> treeMap = new TreeMap();
treeMap.put("publicKey", "4cbce54---------70193");
treeMap.put("参数key", "参数value");

String str = "";
int i = 0;
for (String str2 : treeMap.keySet()) {
Object obj = treeMap.get(str2);
if (obj != null) {
if (i > 0) {
str = str + "^_~";
} else {
i++;
}
if (obj instanceof String) {
str = str + str2 + "=" + obj;
}
}
}
System.out.println(e(str));

对比结果

真实请求:

模拟 key :