大润发优鲜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. 抓包分析。

ssl证书校验卡在启动页



抓包出问题



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

安装Xposed插件



证书校验绕过



  0x003.分析接口数据中的签名

    .a) 看的出来是360的壳,奇虎的,脱壳部这里不说明了,我已经脱完了.

原版的包jadx打开是这样的



    .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算法:

注意几个值



验证算法正确性



注意的是加密的json串不能有空格要替换为空



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)


友情提示:本文只为技术分享交流,请勿非法用途.产生一切法律问题与本人无关



在浏览的同时希望给予作者打赏,来支持作者的服务器维护费用.一分也是爱~