caocao逆向思路

抓包发现请求和返回数据都有加密,所以要逆向加解密

1.抓包分析接口
https://cap.caocaokeji.cn/demand/commonEstimatePrice/1.0p

打开jadx反编译发现采用的框架是okhttp3,由于post请求都会调用okhttp3中的FormBody,直接hook打印堆栈

test_js = """
//遍历已加载的类
Java.perform(
    function () {
        Java.enumerateLoadedClasses({
            onMatch: function (className) {
                if (className == 'okhttp3.FormBody') {
                    traceClass(className);
                }
            },
            onComplete: function () {
            }
        });
        send('done')
    });

//Hook指定类里的所有方法
function traceClass(classname) {
    try {
        //Hook住指定的类
        var target = Java.use(classname);

        //调用getDeclaredMethods方法获得该类的所有方法的声明
        var methods = target.class.getDeclaredMethods();

        //遍历所有方法的声明,拿到方法名和方法的重载名
        methods.forEach(function (method) {
            //得到方法名
            var methodName = method.getName();

            //得到方法的所有重载
            var overloads = target[methodName].overloads;

            //遍历该方法的所有重载,组装overload()里的参数
            overloads.forEach(function (overload) {
                var proto = "(";
                overload.argumentTypes.forEach(function (type) {
                    proto += type.className + ", ";
                });
                if (proto.length > 1) {
                    proto = proto.substr(0, proto.length - 2);
                }
                proto += ")";

                //组装overload完成, 开始Hook该方法
                overload.implementation = function () {
                    var args = [];
                    for (var j = 0; j < arguments.length; j++) {
                        args[j] = arguments[j] + ""
                    }
                    send("hooking: " + classname + "." + methodName + proto);
                    send(arguments)
                    printstack()

                    var retval = this[methodName].apply(this, arguments);
                    var arglist=proto.replace("(","").replace(")","").split(",")
                    if (arglist[0]=='java.util.HashMap'){
                         mapTostring(arguments[0])
                    }
                    send("return: "+retval)
                    return retval;
                }
            });
        });
    } catch (e) {
        send("'" + classname + "' hook fail: " + e)
    }
}


function printstack() {
    send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}

function mapTostring(map){
var keyset = map.keySet();
    var result =""
    var it = keyset.iterator();
    while(it.hasNext()){
        var keystr = it.next().toString();
        var valuestr = map.get(keystr).toString();
        result +=keystr +"="+valuestr+"&";
    }
    send(result);
}
"""

发现打印出来的堆栈中都有调用com.caocaokeji.rxretrofit.security.core.NativeEncryptCapInterceptor.a这个函数,根据名字猜测可能是加密之处

再打印出com.caocaokeji.rxretrofit.security.core.NativeEncryptCapInterceptor这个类的所有函数的输入输出

在结果中搜索接口commonEstimatePrice可以找到结果

**在这里插入图片描述
搜索commonEstimatePrice可以发现headers中的k值和加密返回的第二值是相同的
通过对比解密函数的第二个参数即非对称加密的key,发现key是加密post data函数的的返回值。
【后面有需要用到】

根据函数com.caocaokeji.rxretrofit.security.core.NativeEncryptCapInterceptor.a(okhttp3.Request, java.lang.String, okhttp3.Headers),需要知道要弄明白参数怎么加密的,需要得到调用函数的参数。

查找用例在这里插入图片描述

跟踪result的生成
在这里插入图片描述

根据Result = capInfo.getResult()知道要去跟踪setResult中的参数
在这里插入图片描述

根据a4 = a.a(a3, NativeCapComponent.a, NativeCapComponent.b);

跟踪到这个函数,hook这个类的caocaokeji.sdk.blackbox.a所有函数,打印传入传出,搜索hooking: caocaokeji.sdk.blackbox.a.a(

[*] hooking: caocaokeji.sdk.blackbox.a.a(java.lang.String, java.lang.String, boolean, int, int)
[*] {'0': '{"deviceId":"android_device_id_is_null","clientType":"2","sign":"52857D03053D3D0B6F25482A7A9A0B61","appCode":"4PHG1YFJBF28","token":"CwOOwyOOOQDGX5z4","lt":"39.92421209056275","biz":"1","timestamp":"1626349362232","falg":"0","cityCode":"010","orderFeatures":"","useScene":"2","appVersion":"5.0.7","lg":"116.39786327434547","sceneOrigins":"[6]","version":"5.0.7","uid":"188158880","orderType":"1","bizServiceType":"[{\\"biz\\":1,\\"serviceTypes\\":[2]},{\\"biz\\":13,\\"serviceTypes\\":[2]}]","scene":"1"}', '1': 'V0JdlH0Fg5/XqLHBmuK/yxamks8/xDTH+Yv2g0Vg4jA=', '2': True, '3': 0, '4': 0}
[*] return: 9Urfg818OpEOUkzToJu983A0/o8axeSv3wdoubZzIk7UTa3SBlfjlxkKcDcpBbOgQNh8oxPJ7OE55k/ClKptOP5nEBsaLt8A2XRKsFhO16dknDnIUJuzrVOZCNHmlJnfK4ZX4ytD0j0hUCRamEhpuaskgpHz7jf0I0AnaSUyHXf6+eRPE3JJXmmM1l/RanV4NI7F88SgQ8v5056fgtHwQvbi/ujR+njL2EvRLy9X6pUInLMH4nGm/SODtN0vK9CrTtPw/MuCP7LNDmViJ2NbgwAjU+9UNsvi+bjctg82JZ81iJEEE/SnC3tCeNkV2KaczJv3VXYzMtciU4EqASmYNqucMF//COSBsTRt64Yidy4R3nX9rmpDve+IQNdN3da5ZJouVTPEcvL6oMUFEe0VkTaeFR2Hfz3UBlEFnflx9ImPhMhGpFsIc9vnmYIGJTyyjxABO7Bcm3zQSHf0xX7W1hh/GDdWtr1yl6DEOOuyVMk/65+o8UTFb4HDKO8Qq8LLiYugxIRtpRPaJOmOk8eXZJtKkhRii8uEzWYwhomEisq7rPx5mv57gjLlbkZ5ILhCnpXG+Qhvn5e+QnX0u8RXjfcK4LTsTX+cTwYYPt0sA3/xrpXzqT8rJCMhl3B4bicoEITsEmzkd7bcc4S4Sf8XvGNK/q5TlK/REP1hmUwsvD0=,d3eQnO0cS287hguj610/DxyIAhJgJAkxnQB3Hvg6K6A=,XFYPGg4PBwFEVkAEUhNOCQ==

验证是否是caocaokeji.sdk.blackbox.a.a(java.lang.String,

java.lang.String, boolean, int, int)
Java.perform(function () {
    var t = Java.use("caocaokeji.sdk.blackbox.a");
    t.a.overload('java.lang.String', 'java.lang.String', 'boolean', 'int', 'int').implementation = function(a, b, c, d, e){
        var resp = this.a(a, b, c, d, e);	
        send(a + "**********" + b + "**********" + c)		
        send(resp)
        return resp;
    }
})

function printstack() {
    send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}

根据输出结果知道确实是没错
在这里插入图片描述
跟进去发现关键在BlackboxJniBridge.capEncryptWithEncryptedKey(str, bytes, z, i2, i3);

在这里插入图片描述
跟进去发现是一个native函数
在这里插入图片描述

并且可以看到解密函数是caocaokeji.sdk.blackbox.a.c(java.lang.String, java.lang.String, int, int)
hook后打印

[*] 9JhSfESScSmvnMvFJpmf9qMPhY7C1uHKf2qzqqYV6HvIcc6wOHVj7Ow7mcFpo3bXsJ7D9q6IxIe7ZAcEqq894EROPmFbGehhqmcQayOQtUO3Qp90nFE30B+LX70FvYS/AvnNGiXRGYOknQPy/ICBdYCRzwYKScIoFSqYdUroFQ4lEqN93Rf9KPiTsReVZU077h1h245NOc4OsSHNYC+E3x62XjjtvE9Z6ZvNp5Y28glgkCqTlWDydMEUHVypc1+LcFh106YTcGSTO+hPnGxjIOS/PrIAx4/4JdBWo4xZ/yyhTXV0SWY85LMEMzA5tAZQugxoPQCWDZhCcSv3eAx3WgozABHfSqjgLJ7o6Vup99993SZHLVPivoZEHqAjJHX5THakgGdRaW7QZVyjqYifBCfEBD4cCJEXXe5q+wolNLJHci9N6u79jk9/6G2mMahIJjLd0ZwqBMn7zvAzvzC9Q6vQvjjX2ZAT5oXG1lK1nxhXsr3N7AmOZK9TraUJ82qXZHxzwWGCkHKmKbGdBw4eUNIIYGEsY1DFOsstoPqx+i3o6VZ4g0hhrqBWtfbes+Ii7AQFmOXuxDEva7m1JTKN8812He+HYHsQZgfNYO6dBjpu3C5tzW+afEKKeE46ZgCu8QEFaRRAR/SZUTZCAG/j2ewupwl8I9QAwL9k3brZSwgH8LGjNWza2dy7lMNOTYKaYRI8JrbiJZAGV95mEvEi8jSoHsG+1sQsAkMjA6e8f4QN2KTI8mJqgFZumElBOFo79xZ8GRGI2NnTskcQjmOswVYB74i++DP+7K4XYrCDXqaqbr11Z5T78W43kaB5+mySBahhcvD06gTDnXWpOzMHzHbU62GwaDSPiH4pTqBy1gM3P5DyFJH/qRNhVXtz+4pz+eIUwZLG52yDTo4Ed4K80oLiQiwKq2elS0I1zz8O/PnKk+Vfduzuqmp4b2pLv6bfKl34rye14nRe8TGHDVyWD4DXtrdj9Ey5osJ7MnrrtvaO9eP4TVRe5UwUsUnwhRY+SeMDcdfHHMYOXOCQHkubPXyQhR93+qLmxfw8C4OXiyvAi0a4AnR7DfHRrE7VmW9Glcd6Y1If4sqTBaIKqN35EJAzgDeZTNboqo9i4/TPAj9knBooFm6zWR0nC4aByprv0nDRjUd6srugU1lr1rQZGOtkZfDDgyjRSI59RvA1yVpT80RtjOz2jiNS7vtYanxJAA8d/YLueVCeyB6xuoskF0a2qbqy8pIkoHYcUSWyBeIgXZz4KA5SJi/CxkTxUvaldUTLvUpb+kGWBmH2YySOXbRxEf//oMFvNl/iw9Y+V4Pn6pw9cztyvO5cG6vQldLxe+4minUc+VyfUjG8aBR637UtCGXAlxHmtHxZ4MpxiqQR+VTy6wrM/zEpEhq4XUmb3AkQ2ZvPV3hLpYRxmTvjYBrFprCvVUrQXe55Wav1rhwRD0FwbAG906xryghzbNbvi5zVdVUhUdjgXyrHj/seatSmlFLaWv+dAx93lYEc7zJm4tF+/X3L9UwYljPUCd7xfcYviRrIGHKWzTkj/x2cpefpgCOFqQo5RyRVyqThIv/xjkBgsvfCFMzpJXu6CgQ//4ZWeWHWNELu9/O2WrKOBuk+QQ2HOcVRU6sJI0C/YOA2UvZODJGiQ4MauBrtRl5Bfpjztby+5U3TInMLHxm5DI49sgiVNFjGWUmgRwKCc5oGZcFAUyX3xR1o3WEAYEvaUcmhZBWxjQvPHRGmTAmxuJzUT3pGU/o1ATJWG5LgPPbvSIODauTpZTb0MlrTmmX8rZfti46zCFuwQ+BaFZz5W4uwRpuKIV5ukBFeMJhEoJjImGE9fFis0t/nWhCNzd2Ds7JnBZybYiz5q/lEAahuaeWhkc9Y33hNWDDsyCQIzkFV3MkOsTNPCAngztPiRNF0dsX2CYQgCiP7JTWSg99YLzvFo/+ldBVPQ83kJR00Xo7i9643NJsyVcvqEi/nEaozyT3TCWn56WUZSfBCfth24pwifKof3evRU1yw23kijJW9Tk4urFSxSWStWXzx1/YwhWXA4XIwf+WX9wcaNqwCMuwtY4nPD0lhTvOuVnPckVOzf4FtEtqcoRCXhbn/x186OY9DmkJFbNdwEsc615pYfZABgNv6vsSYcolx5QX8xzTv1F0I9a4ywwHye3EuTLuMxwS4LO5EOJAQg6QvebCoLuxIiF1gdNCQxkhNBnhAZJ1NpeJs1xeTXD70rSlbMrCEF95Vi2JYcmp82CqAoIqh1aX+qRaSN21Di1GwqaZnvhqdmLwD3DfXPYqijfvC6aqCeBGAkXPbRXQosP6vvw4ByVMkiYMmLwR+8rAIybxtWLZO1fj1fhW/hSQxYvEULtfy9qbvsqk+dhlD6RvKRhJZDrAqziyrLiqvea8/PkUbs4fJ1YLNTj6cJX4sUpkrzDunC4VRv/2/buiIU11olecQUoz2J8bqTflCH6OooA6ysqn3nlDHL6xgbA1Eg5r+GABXMqABzPIaCUj+1BU/SpWVT1ceLJlqib0hP8A8yb35wynQkP97gEkXT0p3W0Nf9RKFaRkAgnkJR470wWoYazarG7ssyuG8VE0IdENlWkCLHY2RngGJl/jLXXKWXdnBiqLoJ307XivUQFVlO4htEFyL7/KjgDyXzpL6pEUNmdgjicneZl0At2MXoxx3dzpj4teeUgM/sE2h0mbu0RV6URP9PgvVzHkX+kbL7+rd027l0rEXtbHW7AiEhyRPDYEEuFjAICQGg6TXmvKPpInbgQXbPvKZqrDCtn6/MJHb5gJucIqWa66M1AMYBKHM0VL4lVEg7A4Wp/c7VBQ+J3xD2of5liSpeLCfopJWaE76aYkp6vQF8SAfVct/1fIuZvAcjUTxH11O9GBuuYMSHBQ9vSmcdFwrGyRnbK6tePV098YlgCw3nJbnUuExE7YWqO2oQe/k0XXH4tlhSuVxiBa75RemdRr5qkwTirI+llVZ9qjaPqj28K+oTZ90eUAL4zRR7yU+iDKJxmCmtyaUY0FKZbKLXa4rBZLXzhhOmRUGPfMlAomo4X4HyUj4qZGv2DqASmNnQIJ+NLbGsU7CbOcFHTyANmg/xsyeToGowecOau/RcFZmDKTQ7qAqQS4EwUx2UnSUjPj1Go+z0Qfznosj3xLjpry3yyaIcMcM2oi/K89cEclDINu8F4q2d5mi0VkiAOD4l6G0zrJtyOxo+F2xOoiizJZk9yaafLTTjPZBN/V5Dtb9XompldoVMkymvA87nxZJ2SCn0yugtwl7CbRj8nYBWUtZsY10F2jXMAWCI1pfPwx/huXNp3RyA0XfTpHSGnR1h0empRzXyX3Quqr9gnmJdviGONtmi2ObMr0DErTlVU+unZK9KeemIRxdJCW9MhkqvcJZU/CKgAKreMJwDDvkfcBzTgtJXXG/IxREN3f10BEDjPdvlgkPjhnvxpYkyahA**********fqfaSvJVbKhCwnO0IoWbSBYb9eUNo/n2v/IaEmKS614=**********0
[*] {"code":0,"data":{"zhongYueExposeDTO":{"isShowZhongYue":0,"isDefaultSelectZhongYue":0,"showItem":"接受加盟网约车","isCallZhongYue":1},"detailDTOList":[{"serviceType":2,"orderType":1,"bizType":1,"discountEstimatePrice":3627,"orderChannelType":"internal","orderChannel":"zhuancheRealTimeV1","vehicleIconUrl":"https://ccimgs.oss.aliyuncs.com/config-web/20210106/5ff57de5c342dc12b81aa5db","supportedBusiness":["company_pay","for_others","multi_dest"],"startKm":3.0,"estimateId":"1372072510","discountDiff":151,"selected":1,"orderChannelName":"专车-新能源","extraTipsDTO":{},"sequence":0,"serviceMode":1,"configuredTips":["价格实惠接单快"],"success":true,"estimatePrice":3778,"serviceTypeName":"快选","startMinute":9},{"serviceType":3,"orderType":1,"bizType":1,"discountEstimatePrice":4881,"orderChannelType":"internal","orderChannel":"zhuancheRealTimeV1","vehicleIconUrl":"https://ccimgs.oss.aliyuncs.com/config-web/20210107/5ff69f0ca891e608079f394a","supportedBusiness":["company_pay","for_others","multi_dest"],"startKm":0.0,"estimateId":"1372072510","discountDiff":2017,"selected":0,"orderChannelName":"专车-舒适型","extraTipsDTO":{},"sequence":1,"serviceMode":1,"configuredTips":["好车型好服务"],"success":true,"estimatePrice":6898,"serviceTypeName":"舒适","startMinute":0},{"serviceType":4,"orderType":1,"bizType":1,"discountEstimatePrice":6533,"orderChannelType":"internal","orderChannel":"zhuancheRealTimeV1","vehicleIconUrl":"https://ccimgs.oss.aliyuncs.com/config-web/20210106/5ff57db2c342dc12b81aa554","supportedBusiness":["company_pay","for_others","multi_dest"],"startKm":0.0,"estimateId":"1372072510","discountDiff":1741,"selected":0,"orderChannelName":"专车-豪华型","extraTipsDTO":{},"sequence":2,"serviceMode":1,"configuredTips":["轻奢体验"],"success":true,"estimatePrice":8274,"serviceTypeName":"舒适+","startMinute":0},{"serviceType":5,"orderType":1,"bizType":1,"discountEstimatePrice":9534,"orderChannelType":"internal","orderChannel":"zhuancheRealTimeV1","vehicleIconUrl":"https://ccimgs.oss.aliyuncs.com/config-web/20210107/5ff69f12c342dc12b81c368c","supportedBusiness":["company_pay","for_others","multi_dest"],"startKm":0.0,"estimateId":"1372072510","discountDiff":500,"selected":0,"orderChannelName":"专车-商务型","extraTipsDTO":{},"sequence":3,"serviceMode":1,"configuredTips":["宽敞空间有格调"],"success":true,"estimatePrice":10034,"serviceTypeName":"商务","startMinute":0}],"userCarProtocols":[],"globalExtInfo":{"mergeFeeTips":"当前订单存在多种特殊计价"}},"success":true,"message":"成功"}

同时根据打印出ja这个类的所有函数的输入输出,能够发现解密后的encryptCode参数值在这里出现了
在这里插入图片描述
倒推回去发现生成的请求参数来源于这
在这里插入图片描述
所以数据的加密解密得到结果

function cc_encrypt(str){
    var clz = Java.use("caocaokeji.sdk.blackbox.a");
    return clz.a(str, 'V0JdlH0Fg5/XqLHBmuK/yxamks8/xDTH+Yv2g0Vg4jA=', true, 0, 0);
}

function cc_decrypt(args) {
    var str = args[0]
    //这个key是cc_encrypt返回值中的第二个值
    var key = args[1]
    var clz = Java.use("caocaokeji.sdk.blackbox.a");
    return clz.c(str, key, 0, 0);
}

python请求代码
`

def req(data):
    headers = {
        'Host': 'cap.caocaokeji.cn',
        'v': '1.0',
        'e': '1',
        'api': 'commonEstimatePrice',
        'pv': '2',
        'k': data[2],
        'ctag': '{"smdid":"-toAsJLjlHm4mSXKQ"}',
        'app': 'demand',
        'code': '4PHG1YFJBF28',
        'content-type': 'text/plain; charset=utf-8',
        'user-agent': 'okhttp/3.11.0',
    }
    response = requests.post('https://cap.caocaokeji.cn/demand/commonEstimatePrice/1.0', headers=headers, data=data[0])
    print([response.text, data[1], data[2]])
    return [response.text, data[1], data[2]]


def get_data(block, data):
    res = hook_prepare(block).getres(data)
    return res


def test(block=True):
    timestr = str(int(time.time() * 1000)-10)
    uid = ""
    bs = uid + "1" + "0" + timestr
    # sign = get_data(block, bs)
    # return
    info = {
    "deviceId":"android_device_id_is_null",
    "whoTel":"",
    "estimateTime":"25",
    "appCode":"4PHG1YFJBF28",
    "token":"",
    "origin":"1",
    "startCityCode":"010",
    "timestamp":"",
    "extInfo":"{}",
    "endLg":"116.351895",
    "appVersion":"5.0.7",
    "version":"5.0.7",
    "endLt":"39.942554",
    "encryptCode":"",
    "startLt":"39.92421106207774",
    "endCityCode":"010",
    "demandLabels":"0",
    "estimateKm":"6.05",
    "sign":"",
    "clientType":"2",
    "countPerson":"1",
    "startLg":"116.39786327434547",
    "terminalType":"special",
    "demandOrigin":"unityCallPage",
    "uid":"",
    "useTime":"1627006538539",
    "thankFee":"0",
    "orderType":"1",
    "mpType":"1"
}



    encrypt = get_data(block, json.dumps(info))
    res = req(encrypt)
    decrypt = get_data(block, res)

那么目前要解决的就是encryptCode和sign的生成
hook请求接口,查看请求的数据,将请求数据转成解密后的数据,对比后发现在请求之处传入的数据和调用原始解密传入的数据不一致,同时能看到encryptCode与https://cap.caocaokeji.cn/genius/caocaoPolylineRecommend/1.0的返回时一样的。

function body2String(body) {
     var t = Java.use('com.caocaokeji.rxretrofit.security.core.NativeEncryptCapInterceptor');
     t.a.overload('okhttp3.RequestBody').implementation = function(b){
        var resp = this.a(body);
        var resp2 = this.a(b)
        console.log("resp..." + resp)
        console.log("resp2..." + resp2)
        // printstack()
        return resp2
    }
}

function hookRequest() {
    var OkHttpClient = Java.use("okhttp3.OkHttpClient");
    OkHttpClient.newCall.implementation = function (request) {
        var result = this.newCall(request);
        console.log(request.toString());
        if(request.url().toString().indexOf('demand/commonEstimatePrice') !== -1){
            body2String(request.body())
        }
        return result;
    };
}

发现经过拦截器的参数请求多了这几个数,搜索其中的参数比如appCode,发现只有一处调用

appCode 4PHG1YFJBF28
token CwOOwyOOOQDGX5z4
appVersion 5.0.7
version 5.0.7
clientType 2
sign 5F53728C55E75C5CDEB448471FB8844B
uid 188158880

在这里插入图片描述
在这里插入图片描述
hook后传入传出的值,发现sign在此生成
并且也能发现,生成encryptCode参数的的需要的sign也是在此处生成,只是传入的值不同。

encryptCode--sign
appCode=4PHG1YFJBF28&appVersion=5.0.7&bizType=1&cityCode=010&clientType=2&deviceId=&polylineEndLg=116.593409&polylineEndLt=40.079175&polylineStartLg=116.39786327434547&polylineStartLt=39.92421106207774&polylines=[{"estimateDistance":27.97,"estimateTime":38,"polylineNo":0,"trafficNum":6},{"estimateDistance":32.11,"estimateTime":64,"polylineNo":1,"trafficNum":32}]&timestamp=&token=CwOOwyOOOQDGX5z4&uid=&version=5.0.7&key=607cdf076f301f4913602d6b
[*] 6A955C6BA37D77DC4B5EE4905635E0EB
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值