新氧APP8.24.2接口签名sign和xy_sign分析
0x01、 目标需求:
.a) 新氧APP的接口参数以及相关的签名获取的方式
.b) 需要获取所有城市下的分类店铺等数据
0x02、分析背景:
.a) 版本8.24.2(目前最新版)
.b) 软件无壳
.c) 软件通讯过程中采取了签名验证的方式,
0x03、分析流程:
.a) 通过数据包抓取或敏感函数hook方式获得接口功能
.b) 通过函数内部参数的组装继续分析参数的来源以及加密的流程(校验 _sign 和 xy_sign)0x04、分析开始:
.a) jadx-gui 分析签名
0x001. 打开jadx-gui 直接搜索关键词_sign:
0x002. 打开jadx-gui 直接搜索关键词_sign 我对第一个结果比较感兴趣点进去看看(两个sing都在这里,传入的map就是url中的参数):
if (z) {
treeMap.put("xy_sign", paramsNewSign(paramsMd5(INetWorkCommonParasm.getParamsString(treeMap))));
treeMap.put("_sign", paramsSign(INetWorkCommonParasm.getParamsString(treeMap)));
}
0x003. xy_sign是有MD5的处理,_sign是没有的跟到INetWorkCommonParasm.getParamsString这个函数看一下是如何处理map的(这里是直接把map转成了url的key=value&key=value的格式,其中如果key=adinfo的话value进行url解码):
@NonNull
public static String getParamsString(Map<String, String> map) {
if (map == null) {
return "";
}
StringBuilder sb = new StringBuilder();
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> next = it.next();
String key = next.getKey();
String value = next.getValue();
if (!TextUtils.isEmpty(value)) {
if ("adinfo".equalsIgnoreCase(key)) {
value = URLDecoder.decode(value);
}
sb.append("&");
sb.append(key);
sb.append(ContainerUtils.KEY_VALUE_DELIMITER);
sb.append(value);
} else {
it.remove();
map.remove(key);
}
}
return sb.substring(1);
}
0x004. 这里还需要看一下paramsNewSign的函数是如何处理的(主要的转码函数如下,然后再进行Base64编码):
public byte[] basesec_EncDataToBinary(String str, int i) throws Exception {
if (this.ctx == null) {
throw new NullPointerException("not init yet");
} else if (str == null || "".equals(str)) {
throw new NullPointerException("input data is null or zero length");
} else {
try {
byte[] bytes = str.getBytes("UTF-8");
byte[] bArr = new byte[(bytes.length + 7)];
int length = bytes.length + 12;
byte[] bArr2 = new byte[length];
int[] iArr = {bytes.length + 12};
setVersion(bArr, 1);
setCallNum(bArr, 1);
setOpt(bArr, (byte) (i & 255));
setData(bArr, bytes);
int o0o = o0o(this.ctx, bArr, bArr2, iArr); //如果参数2,3,4没有被修改,这行代码将毫无意义
if (o0o != 0 || length < iArr[0]) {
throw new OException(Integer.toBinaryString(-o0o));
}
byte[] bArr3 = new byte[iArr[0]];
System.arraycopy(bArr2, 0, bArr3, 0, iArr[0]);
return bArr3;
} catch (Exception unused) {
throw new OException("in data can not getbytes(UTF-8)");
}
}
}
.b) 编写测试代码测试(如果不着急可以还原这个加密算法,如果着急可以通过hook的方式去调用它的函数来获取返回值).
0x001. 这里hook到了加密的参数,继续往下追生成参数的地方:
0x002. 这里hook到了加密的参数,继续往下追生成参数的地方(xy_sign调用的是paramsNewSign , _sign调用的是paramsSign)xy_sign的加密先使用md5 然后传入给paramsNewSign函数:
0x003. 调试测试数据的算法(v1: b733a2707835e008bfc8511511999feb , res: KSEeNJPfEA6GdaapPAvWjw%3D%3D)我们只要把v1的值带入还原函数返回res的值就证明我们成功了:
0x0001. hook Navite函数 传入了2个byte数组1个int数组,在so内做了运算,改变倒数第二个的byte数组内容 只有前16位是有东西的, 然后在java内有一个System.copyArray函数 取的就是最后一个int数组的最终结果也就是16,最终结果是中间的后面全是0的数组的前16位然后base64编码在url编码
0x0002. 由于时间 问题,大家可以自己还原一下算法,本文中直接采用unidbg 或者 提取so文件直接调用该函数,未完待续,后面会补充算法的实现过程.经测试,数据一致,如下图(v1: b733a2707835e008bfc8511511999feb , res: KSEeNJPfEA6GdaapPAvWjw%3D%3D):
0x0004. 其主要汇编代码如下:
.text:0000EACC PUSH {R4-R7,LR}
.text:0000EACE ADD R7, SP, #0xC
.text:0000EAD0 PUSH.W {R8,R9,R11}
.text:0000EAD4 SUB SP, SP, #0x90
.text:0000EAD6 MOV R4, R0
.text:0000EAD8 LDR R0, =(__stack_chk_guard_ptr - 0xEADE)
.text:0000EADA ADD R0, PC ; __stack_chk_guard_ptr
.text:0000EADC LDR R6, [R0] ; __stack_chk_guard
.text:0000EADE LDR R0, [R6]
.text:0000EAE0 STR R0, [SP,#0xA8+var_1C]
.text:0000EAE2 CBZ R4, loc_EB18
.text:0000EAE4 MOV R5, R1
.text:0000EAE6 CBZ R1, loc_EB18
.text:0000EAE8 CBZ R2, loc_EB18
.text:0000EAEA CBZ R3, loc_EB18
.text:0000EAEC LDRB R0, [R5]
.text:0000EAEE CMP R0, #1
.text:0000EAF0 BNE loc_EB28
.text:0000EAF2 LDR.W R0, [R5,#3]
.text:0000EAF6 REV.W R9, R0
.text:0000EAFA LDRB R0, [R5,#1]
.text:0000EAFC CMP R0, #1
.text:0000EAFE BEQ loc_EB38
.text:0000EB00 CBNZ R0, loc_EB58
.text:0000EB02 CMP.W R9, #0x81
.text:0000EB06 BLT loc_EB74
.text:0000EB08 LDR R0, [R6]
.text:0000EB0A LDR R1, [SP,#0xA8+var_1C]
.text:0000EB0C SUBS R0, R0, R1
.text:0000EB0E BNE loc_EBB2
.text:0000EB10 MOVS R0, #0
.text:0000EB12 MOVS R1, #4
.text:0000EB14 MOVS R2, #3
.text:0000EB16 B loc_EB66
友情提示:本文只为技术分享交流,请勿非法用途.产生一切法律问题与本人无关. 本文中所有的调试代码均在gitee仓库中(包含很多app的关键点的hook代码) Git公开仓库地址 欢迎star or fork
在浏览的同时希望给予作者打赏,来支持作者的服务器维护费用.一分也是爱~
cc
你好,unidbg什么时候实现一下学习学习
用户 Windows10 1150 天前回复
admin
@ccunidbg实现也挺容易的,实现了几个APP还没发出来。
作者 MacOS 1137 天前回复
白云
666,学习了
用户 Windows10 1274 天前回复