大润发优鲜APP签名分析
0x01、 目标需求:
.a) 大润发优鲜APP的接口参数以及相关的签名获取的方式
0x02、分析背景:
.a) 大润发优鲜APP
.b) APP版本1.4.0(目前最新)
.c) 软件使用360壳且混淆
.d) 软件通讯过程中部分接口采取了签名验证的方式,返回数据无加密
.e) APP校验了HTTPS证书,本文使用木木模拟器安装了绕过ssl校验的Xposed模块(https://github.com/Fuzion24/JustTrustMe)
0x03、分析流程:
.a) 通过数据包抓取或敏感函数hook方式获得接口功能
.b) 通过函数内部参数的组装继续分析参数的来源以及加密的流程
0x04、绕过SSL校验点,抓取数据包:
.a) 截图如下:
0x001. 抓包分析。


0x002. 换个方式,绕过这个校验。


0x003.分析接口数据中的签名
.a) 看的出来是360的壳,奇虎的,脱壳部这里不说明了,我已经脱完了.
.b) 打开之后搜索关键字符串paramMd5.(两个地方都是一个函数)

.c) 继续跟进去看一下.
//这几个是头部定义的字符串
//private static final String d = "isSimulator";
//private static final String e = "networkType";
//private static final String f = "viewSize";
//private static final String g = "time";
public static String b(String str) {
try {
JSONObject jSONObject = new JSONObject(str);
boolean z = false;
if (jSONObject.has(d)) {
z = jSONObject.getBoolean(d);
}
String str2 = "";
if (jSONObject.has(f)) {
str2 = jSONObject.getString(f);
}
String str3 = "";
if (jSONObject.has(e)) {
str3 = jSONObject.getString(e);
}
String str4 = "";
if (jSONObject.has(g)) {
str4 = jSONObject.getString(g);
}
//注意这里,这里是加密的核心函数b.a 跳过去看一下
return b.a(str + (z + str2 + str3 + str4));
} catch (JSONException e2) {
e2.printStackTrace();
return b.a(str);
}
}
.d) 继续跟进去看一下,算法核心代码,用的是散列算法HASH256(以为加个壳就安全了...都没有用ndk写算法),代码如下:
/* compiled from: FMRequest */
public static final class b {
private b() {
}
public static String a(String str) {
String str2 = "@456yx#*^&HrUU99";
if (e.e.equals(e.a().b()) || e.f.equals(e.a().b())) {
str2 = "@yx123*&^DKJ##CC";
}
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(str2.getBytes(), "HmacSHA256");
Mac instance = Mac.getInstance(secretKeySpec.getAlgorithm());
instance.init(secretKeySpec);
return Base64.encodeToString(instance.doFinal(str.getBytes()), 2);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
.e) 接下来还原python算法:


import hashlib
import json, base64, hmac
data = {"addrId": "", "apiVersion": "a1.32", "appVersion": "1.4.0", "body": {}, "channel": "WanDouJia",
"deviceId": "1265c71021c00905-c710-1265-0905-21c0", "httpsEnable": 1, "isSimulator": True,
"networkType": "WIFI", "osType": 1, "reRule": "2", "scopeType": 1, "source": "yx", "time": "20200814163056",
"token": "c45e0ae9c85e4a7f09f019df1767b3c6", "viewSize": "810x1440"}
dataStr = json.dumps(data, ensure_ascii=False)
isSimulator = data['isSimulator']
viewSize = data['viewSize']
networkType = data['networkType']
time = data['time']
#isSimulator 是否是模拟器 布尔值,Java中的值是小写,所以这里要转换为小写
dataStr = dataStr + (str(isSimulator).lower() + viewSize + networkType + time)
"""
String str2 = "@456yx#*^&HrUU99";
if (e.e.equals(e.a().b()) || e.f.equals(e.a().b())) {
str2 = "@yx123*&^DKJ##CC";
}
"""
data = dataStr.replace(" ","").encode('utf-8') # 加密数据
appsecret = "@456yx#*^&HrUU99".encode('utf-8') # 秘钥
signature = base64.b64encode(hmac.new(appsecret, data, digestmod=hashlib.sha256).digest())
print("秘钥为:" + signature.decode())
.f) Python2.7封装算法工具类(只需把抓包中的json数据替换掉值使用字符串传入即可,python3需要注意返回值数据url编码问题即可):# -*- coding:utf8 -*-
import hashlib
import json, base64, hmac , sys
from urllib import urlencode
reload(sys)
# 重新设置字符集(此时不会出现提示,别怀疑自己敲错了)
sys.setdefaultencoding("utf-8")
import requests
def sign(data='',key='@456yx#*^&HrUU99'):
"""
String str2 = "@456yx#*^&HrUU99";
if (e.e.equals(e.a().b()) || e.f.equals(e.a().b())) {
str2 = "@yx123*&^DKJ##CC";
}
"""
try:
dataStr = json.dumps(data, ensure_ascii=False)
dataStr = dataStr.replace(" ", "")
isSimulator = data['isSimulator']
viewSize = data['viewSize']
networkType = data['networkType']
time = data['time']
except:
dataStr = data.replace(" ", "")
data = json.loads(dataStr)
isSimulator = data['isSimulator']
viewSize = data['viewSize']
networkType = data['networkType']
time = data['time']
signStr = dataStr + (str(isSimulator).lower() + viewSize + networkType + time)
data = signStr.encode('utf-8')
appsecret = key.encode('utf-8')
signature = base64.b64encode(hmac.new(appsecret, data, digestmod=hashlib.sha256).digest()).decode()
body = {
"data": dataStr,
"paramsMD5": signature
}
return urlencode(body)
友情提示:本文只为技术分享交流,请勿非法用途.产生一切法律问题与本人无关
在浏览的同时希望给予作者打赏,来支持作者的服务器维护费用.一分也是爱~
1234
楼主,技术交流下吧,加q 173703938
用户 MacOS 1502 天前回复
wudilong
有免密登录接口吗?或者思路也行
用户 Windows10 1528 天前回复
1234
@wudilong验证登录、和免密搞定没?
用户 MacOS 1502 天前回复
admin
@wudilong不太理解哦。 没有密码不能登录的,需要登录才能获取到的数据,那就只能登录咯。
作者 MacOS 1523 天前回复