吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5872|回复: 53
收起左侧

[Android 原创] 某大厂生鲜超市加密协议分析

  [复制链接]
lixiaolevae 发表于 2021-11-18 20:22

接口抓包 分析

工具

Charles

关于charles的使用可翻阅我之前的charles专题文章

测试手机

Nexus 5x

郑重声明,本文只分享思路,不做它用,为保护案例商家安全隐私,敏感信息用xxx代替

接口:附近可服务的门店

curl
所有接口调用url均为https://colour.xxxxxxx.com/api, 通过postbody参数functionId控制获取具体的数据

curl -H 'Host: colour.xxxxxxx.com' -H 'x-mlaas-at: wl=0' -H 'user-agent: xxxxxxxapp_android' -H 'content-type: application/x-www-form-urlencoded; charset=utf-8' --data-binary "commonExtend=&loginType=4&sign=b6beeee33ad4142cc54f3e55a045fbb1c70ecdfdbffa985b559cc36797d20357&screen=1794*1080&d_brand=LGE&body=%7B%22commonExtend%22%3A%22%22%2C%22data%22%3A%7B%22lon%22%3A%22120.02877%22%2C%22lat%22%3A%2230.278442%22%7D%2C%22appName%22%3A%22xxxxxxx%22%2C%22screen%22%3A%221794*1080%22%2C%22lon%22%3A%22120.143936%22%2C%22platformId%22%3A%221%22%2C%22clientVersion%22%3A%223.6.4%22%2C%22storeId%22%3A%22232686%22%2C%22recommendSwitch%22%3A%22true%22%2C%22eu%22%3A%2275B6364667C69667%22%2C%22fv%22%3A%220333461727947597%22%2C%22osVersion%22%3A%228.1.0%22%2C%22partner%22%3A%22huawei%22%2C%22v%22%3A2%2C%22tenantId%22%3A%221%22%2C%22client%22%3A%22android%22%2C%22clientVersionBuild%22%3A%222110251117%22%2C%22model%22%3A%22Nexus5X%22%2C%22networkType%22%3A%22wifi%22%2C%22brand%22%3A%22LGE%22%2C%22lat%22%3A%2230.323437%22%7D&clientVersion=3.6.4&eu=75B6364667C69667&fv=0333461727947597&d_model=Nexus5X&functionId=xxxxxxx_platform_address_getPosition&t=1636957653670&partner=huawei&osVersion=8.1.0&build=2110251117&appid=****fresh_APP&client=xxxxxxx_android&lang=zh_CN&networkType=wifi" --compressed 'https://colour.xxxxxxx.com/api'

postBody URL解码后

commonExtend=&loginType=4&sign=b6beeee33ad4142cc54f3e55a045fbb1c70ecdfdbffa985b559cc36797d20357&screen=1794*1080&d_brand=LGE&body={"commonExtend":"","data":{"lon":"120.02877","lat":"30.278442"},"appName":"xxxxxxx","screen":"1794*1080","lon":"120.143936","platformId":"1","clientVersion":"3.6.4","storeId":"232686","recommendSwitch":"true","eu":"75B6364667C69667","fv":"0333461727947597","osVersion":"8.1.0","partner":"huawei","v":2,"tenantId":"1","client":"android","clientVersionBuild":"2110251117","model":"Nexus5X","networkType":"wifi","brand":"LGE","lat":"30.323437"}&clientVersion=3.6.4&eu=75B6364667C69667&fv=0333461727947597&d_model=Nexus5X&functionId=xxxxxxx_platform_address_getPosition&t=1636957653670&partner=huawei&osVersion=8.1.0&build=2110251117&appid=****fresh_APP&client=xxxxxxx_android&lang=zh_CN&networkType=wifi

接口正确返回 responseBody

{
    "code": "0",
    "success": true,
    "msg": null,
    "data": {
        "success": true,
        "businessCode": 0,
        "msg": null,
        "type": 1,
        "locationInfo": {
            "addressExt": "浙江省杭州市余杭区",
            "addressSummary": "浙江省杭州市",
            "storeId": null,
            "lat": "30.278442",
            "lon": "120.02877",
            "testShop": false
        },
        "defaultAddress": null,
        "tenantShopInfoList": [{
            "storeId": 232xxx,
            "storeName": "华东****鲜云超",
            "storeAddress": "江东中路与江东门北街交汇处",
            "promiseInfo": "最快30分钟达 | 230.96KM",
            "tenantDesc": "",
            "businessInfo": "",
            "tenantInfo": {
                "tenantId": 1,
                "tenantName": "****鲜",
                "bigLogo": "http://img12.360buyimg.com/freshapp/jfs/t1/144864/26/9153/27011/5f6ae507E9dfc96a5/fc2f58d77bcbf2cd.png",
                "smallLogo": "http://img12.360buyimg.com/freshapp/jfs/t1/143548/8/13335/6626/5fa4affbE87f4ded3/f46b57081818d3ba.png",
                "circleLogo": "http://img12.360buyimg.com/freshapp/jfs/t1/140501/2/9085/6589/5f6ae50bE0508f49c/ae52b1fc1dc2aa59.png",
                "contactTel": "4006068768",
                "supportGiftCard": false,
                "supportEmployeeCard": false,
                "supportInvoiceCenter": false,
                "supportBalance": false,
                "clientInfo": null
            },
            "lon": "118.737681",
            "lat": "32.036757",
            "valid": true,
            "freeBuy": false,
            "delivery": false
        }, {
            "storeId": 196243,
            "storeName": "华中****鲜云超",
            "storeAddress": "光谷保利广场",
            "promiseInfo": "最快30分钟达 | 539.52KM",
            "tenantDesc": "",
            "businessInfo": "",
            "tenantInfo": {
                "tenantId": 1,
                "tenantName": "****鲜",
                "bigLogo": "http://img12.360buyimg.com/freshapp/jfs/t1/144864/26/9153/27011/5f6ae507E9dfc96a5/fc2f58d77bcbf2cd.png",
                "smallLogo": "http://img12.360buyimg.com/freshapp/jfs/t1/143548/8/13335/6626/5fa4affbE87f4ded3/f46b57081818d3ba.png",
                "circleLogo": "http://img12.360buyimg.com/freshapp/jfs/t1/140501/2/9085/6589/5f6ae50bE0508f49c/ae52b1fc1dc2aa59.png",
                "contactTel": "4006068768",
                "supportGiftCard": false,
                "supportEmployeeCard": false,
                "supportInvoiceCenter": false,
                "supportBalance": false,
                "clientInfo": null
            },
            "lon": "114.410486",
            "lat": "30.490744",
            "valid": true,
            "freeBuy": false,
            "delivery": false
        }],
        "nearStore": false,
        "fix": false,
        "fixLat": null,
        "fixLon": null
    },
    "extMap": {}
}

接口错误返回:接口有调用时效,会检测时间戳参数t,时效性为五分钟,五分钟后再次调用返回异常

{"code":"1","echo":"invalid signature"}

接口分析得知,有三个加密参数 sign eu fv

逆向分析

工具

jadx-反编译

frida+objection 动态调试

sign加密分析

sign从字符串特征和长度来看,看起来像sha256

jadx打开apk搜索关键字“HmacSha256”,看到加密HmacSha256搜选出多个

image-20211118200626723

frida+objection跟踪调用入参

需要objection个个追踪然后和抓包得到的sign比对,最终确定调用的为 com.xxx.common.http.GatewaySignatureHelper.HMACSHA256
image-20211118200712661

image-20211118200745887

objection追踪

android hooking watch class_method com.xxx.common.http.GatewaySignatureHelper.HMACSHA2
56  --dump-args --dump-return --dump-backtrace

--dump-backtrace追踪调用方法栈

(agent) [172759] Called com.xxx.common.http.GatewaySignatureHelper.HMACSHA256([B, [B)
(agent) [172759] Backtrace:
    com.xxx.common.http.GatewaySignatureHelper.HMACSHA256(Native Method)
    com.xxx.common.http.GatewaySignatureHelper.signature(TbsSdkJava:60)
    com.xxx.common.http.HttpRequest.paramHandler(TbsSdkJava:108)
    com.xxx.common.http.HttpRequest.add(TbsSdkJava:9)
    com.xstore.****fresh.modules.search.SearchRequest.getWareInfosIcon(TbsSdkJava:11)
    com.xstore.****fresh.modules.productdetail.utils.GetWareInfoIconUtils.getWareInfoMsg(TbsSdkJava:7)
    com.xstore.****fresh.modules.category.menulist.NewProductCategoryFragment.setListView(TbsSdkJava:34)
    com.xstore.****fresh.modules.category.menulist.NewProductCategoryFragment.initView(TbsSdkJava:32)
    com.xstore.****fresh.modules.category.menulist.NewProductCategoryFragment.onCreateView(TbsSdkJava:3)
    androidx.fragment.app.Fragment.performCreateView(TbsSdkJava:4)
    androidx.fragment.app.FragmentStateManager.createView(TbsSdkJava:15)
    androidx.fragment.app.FragmentStateManager.moveToExpectedState(TbsSdkJava:23)
    androidx.fragment.app.FragmentManager.executeOpsTogether(TbsSdkJava:34)
    androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(TbsSdkJava:10)
    androidx.fragment.app.FragmentManager.execPendingActions(TbsSdkJava:4)
    androidx.fragment.app.FragmentManager$5.run(TbsSdkJava:1)
    android.os.Handler.handleCallback(Handler.java:790)
    android.os.Handler.dispatchMessage(Handler.java:99)
    android.os.Looper.loop(Looper.java:164)
    android.app.ActivityThread.main(ActivityThread.java:6494)
    java.lang.reflect.Method.invoke(Native Method)
    com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

(agent) [172759] Arguments com.xxx.common.http.GatewaySignatureHelper.HMACSHA256([object Object], [object Object])
(agent) [172759] Return Value: b196066d6b926a9e032ea9ae1b0a52a048b03ac063ad55bfbc3fca0fe88959c6

上游重要的方法为这三个:
com.xxx.common.http.GatewaySignatureHelper.HMACSHA256(Native Method)
com.xxx.common.http.GatewaySignatureHelper.signature(TbsSdkJava:60)
com.xxx.common.http.HttpRequest.paramHandler(TbsSdkJava:108)

追signature

android hooking watch class_method com.xxx.common.http.GatewaySignatureHelper.signature  --dump-args --dump-return

多次触发抓包发现调用的是以下方法

public static String signature(Map<String, String> map, String str) {
        if (map == null || map.isEmpty() || TextUtils.isEmpty(str)) {
            return null;
        }
        TreeSet treeSet = new TreeSet();
        for (String str2 : map.keySet()) {
            treeSet.add(str2);
        }
        StringBuffer stringBuffer = new StringBuffer();
        Iterator it = treeSet.iterator();
        while (it.hasNext()) {
            String obj = it.next().toString();
            String str3 = map.get(obj);
            if (DEBUG) {
                String str4 = TAG;
                Log.d(str4, "sorted key : " + obj + ", value : " + str3);
            }
            if (!TextUtils.isEmpty(str3)) {
                stringBuffer.append(str3);
                stringBuffer.append("&");
            }
        }
        String stringBuffer2 = stringBuffer.toString();
        if (stringBuffer2.endsWith("&") && stringBuffer2.length() > 1) {
            stringBuffer2 = stringBuffer2.substring(0, stringBuffer2.length() - 1);
        }
        if (DEBUG) {
            String str5 = TAG;
            Log.d(str5, "raw signature param str : " + stringBuffer2);
        }
        return HMACSHA256(strToByteArray(stringBuffer2), strToByteArray(str));
    }

多次触发调用后,可确定第二个参数str是加密盐值,且为恒定值:fa5010c35exxxxxxx40060d65d3f3801
第一个参数是map,objection只显示为[object Object],无法显示其具体kv内容
写个frida hook脚本将map kv打印出来

function main() {
    console.log("Enter hook js");
    Java.perform(function x() {
        console.log("Inside Java Perform");
        var cls = Java.use("com.xxx.common.http.GatewaySignatureHelper");
        cls.signature
        .overload('java.util.Map', 'java.lang.String')
        .implementation 
        = function(map, salt) {
            console.log("args1: " + map.toString());
            console.log("args2: " + salt);
            var result = this.signature(map, salt);
            return result;
        }

    })
}
setImmediate(main)
frida -U -f com.xstore.****fresh -l hook_signature.js --no-pause

但很遗憾,有反注入检测,执行后app直接重启
也没关系,用笨办法试一下,将postbodyStr按&切开后组装成map,调用signature看看得到的sign是否一致,或者相差多少

package com.*******.hmdd.spider.cmptr.xxxxxxxxxx;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;

public class xxxxxxxxxxTestSign {

    public static void main(String[] args) {
        String postBody = "commonExtend=&loginType=4&sign=a992b4c32cb9e81500ea37adb4dfbc8a63a661bb3a290c4fdb39abf4d752d4ac&screen=1794*1080&d_brand=LGE&body={\"commonExtend\":\"\",\"data\":{\"lon\":\"120.02877\",\"lat\":\"30.278442\"},\"appName\":\"xxxxxxx\",\"screen\":\"1794*1080\",\"lon\":\"120.02877\",\"platformId\":\"1\",\"clientVersion\":\"3.6.4\",\"storeId\":\"232686\",\"recommendSwitch\":\"true\",\"eu\":\"8646C6D4A785B424\",\"fv\":\"937385A766E41727\",\"osVersion\":\"8.1.0\",\"partner\":\"huawei\",\"v\":2,\"tenantId\":\"1\",\"client\":\"android\",\"clientVersionBuild\":\"2110251117\",\"model\":\"Nexus5X\",\"networkType\":\"wifi\",\"brand\":\"LGE\",\"lat\":\"30.278442\"}&clientVersion=3.6.4&eu=8646C6D4A785B424&fv=937385A766E41727&d_model=Nexus5X&functionId=xxxxxxx_platform_address_getPosition&t=1636963490297&partner=huawei&osVersion=8.1.0&build=2110251117&appid=****fresh_APP&client=xxxxxxx_android&lang=zh_CN&networkType=wifi";
        String[] kvStr = postBody.split("&");
        Map<String, String> map = new HashMap<>();
        for (String kv : kvStr) {
            String[] cell = kv.split("=");
            if (cell.length == 2) {
                map.put(cell[0], cell[1]);
            }
        }
        String sign = signature(map, SALT);
        System.out.println("sign = " + sign);
    }

    public static final String SALT = "fa5010c35exxxxxxx40060d65d3f3801";

    public static String signature(Map<String, String> map, String str) {
        if (map == null || map.isEmpty() || str == null || str.length() == 0) {
            return null;
        }
        TreeSet treeSet = new TreeSet();
        for (String str2 : map.keySet()) {
            treeSet.add(str2);
        }
        StringBuffer stringBuffer = new StringBuffer();
        Iterator it = treeSet.iterator();
        while (it.hasNext()) {
            String key = it.next().toString();
            if ("sign".equals(key)) {
                continue;
            }
            String value = map.get(key);
            if (value == null || value.length() == 0) {
                continue;
            }
            stringBuffer.append(value);
            stringBuffer.append("&");

        }
        String stringBuffer2 = stringBuffer.toString();
        if (stringBuffer2.endsWith("&") && stringBuffer2.length() > 1) {
            stringBuffer2 = stringBuffer2.substring(0, stringBuffer2.length() - 1);
        }
        return HMACSHA256(strToByteArray(stringBuffer2), strToByteArray(str));
    }

    private static String HMACSHA256(byte[] bArr, byte[] bArr2) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(bArr2, "HmacSHA256");
            Mac instance = Mac.getInstance("HmacSHA256");
            instance.init(secretKeySpec);
            return byte2hex(instance.doFinal(bArr));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e2) {
            e2.printStackTrace();
            return null;
        }
    }

    public static String byte2hex(byte[] bArr) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        while (bArr != null && i < bArr.length) {
            String hexString = Integer.toHexString(bArr[i] & 255);
            if (hexString.length() == 1) {
                sb.append('0');
            }
            sb.append(hexString);
            i++;
        }
        return sb.toString().toLowerCase();
    }

    public static byte[] strToByteArray(String str) {
        if (str == null) {
            return null;
        }
        return str.getBytes();
    }
}
sign = a992b4c32cb9e81500ea37adb4dfbc8a63a661bb3a290c4fdb39abf4d752d4ac

好家伙,运行后发现sign和接口抓包的一毛一样
说明postBody中所有的参数都参与了sign的运算,换句话说sign参数是postBody参数构造的最后一步。

fv&eu 分析

这两个一致在变化,应该也是加密
搜下代码

image-20211118201231262
有多个,xxxcrashreport像是崩溃报告类,排除。其他的一个一个watch追吧

定位到

image-20211118201321979

image-20211118201401263
image-20211118201438690

由于eu和fv一致变化,猜测androidId是随机的,即调用了getRandomString方法,此可以通过hook证明确实调用了getRandomString
image-20211118201513898
eu和fv的加工
image-20211118201546631
对象EncryptResult只是个简单的封装,含有eu和fv两个参数
那么java很好还原,做做变体即可。
用到的HexUtils如下,还原时照抄就好了
image-20211118201621038

心得

  1. 逆向需要耐心也需要大胆的猜想去不断尝试,同时需要寻求巧妙的验证方式。本例的分析向上和向下的追溯均有,灵活应对
  2. 逆向工作会用到的很多好用的工具,平时注意多收集一些好用的工具或博文以事半功倍,本文所用到的工具和相关扩展知识点均贴出了链接,方便读者收藏~
  3. 本文旨在分享一些逆向技巧和思路,本文所举case相关敏感已打码略去,读者不可利用本文所述内容进行非法商业获取利益,若执意带来的法律责任由读者自行承担。

免费评分

参与人数 20威望 +2 吾爱币 +116 热心值 +17 收起 理由
frica01 + 1 + 1 我很赞同!
qtfreet00 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
df_2015 + 1 + 1 用心讨论,共获提升!
Jinjibewater + 1 + 1 666
春天的萌动 + 1 + 1 我很赞同!
xcydbty + 1 我很赞同!
Dope97cc + 1 + 1 我很赞同!
wjy929520 + 1 谢谢@Thanks!
hanlaoshi + 1 + 1 我很赞同!
MXGT + 1 热心回复!
xuancang + 1 用心讨论,共获提升!
lu952450497 + 1 + 1 学习一下
alderaan + 1 + 1 热心回复!
lothario + 1 我很赞同!
Pinsir + 1 热心回复!
yi025 + 1 + 1 用心讨论,共获提升!
chinawolf2000 + 1 + 1 热心回复!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Apollyon + 1 + 1 牛疯了..
剑来…… + 1 + 1 大佬tql

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
kanmanli 发表于 2021-11-20 00:39
提示: 作者被禁止或删除 内容自动屏蔽
咔c君 发表于 2021-11-18 21:00
ll小布 发表于 2021-11-18 21:11
pigger 发表于 2021-11-18 21:12
支持支持支持
tttyyyuuu 发表于 2021-11-18 21:14
写得很详细,学习了
baihe1 发表于 2021-11-18 21:20
哇,这个厉害,虽然看了半天JAVA也没看懂
sandy1991 发表于 2021-11-18 21:38
写得不错!值得学习一小时
zhi048 发表于 2021-11-18 22:26
有点意思
lyfjyq 发表于 2021-11-18 23:04
这下可以不花钱吃肉蛋奶了
93244 发表于 2021-11-19 00:06
好厉害呀,值得我学习!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 11:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表