记录一次某APP的sign分析过程(篇章一)

[!tip]

前言:某东sign有三种算法,这次讲解的是最简单的一个分支。

某东算法已经被开源烂了,但是给新入门的朋友进行学习再合适不过了

打通任意一个分支都可以拿到正确的,可以请求的sign

目标样本版本12.2.2(最新版地址可能会发生偏移以及变化)

jadx打开样本 开始定位sign的生成逻辑

采用搜索CleanShot 2024-05-12 at 18.19.56

x-api-eid-token的方法来定位

CleanShot 2024-05-12 at 18.20.57

CleanShot 2024-05-12 at 18.24.53

发现在此处进行引用,从函数功能可以分析出,这是在组包,并返回组装完的字符串

按经验来说,组装成字符串之后就要加密了,我们按x 查找函数上一层引用

CleanShot 2024-05-12 at 18.26.01

发现上一层函数也在组包,返回的也是str类型

CleanShot 2024-05-12 at 18.26.11

继续网上寻找,发现同上

CleanShot 2024-05-12 at 18.26.43

继续向上寻找:

CleanShot 2024-05-12 at 18.27.10

最终找到加密位置

CleanShot 2024-05-12 at 18.27.39

    try {
            String signature = JDHttpTookit.getEngine().getSignatureHandlerImpl().signature(JDHttpTookit.getEngine().getApplicationContext(), queryParameter, str, deviceUUID, property, versionName);
            if (OKLog.D) {
                OKLog.d("Signature", "native signature sucess " + signature);
            }
            if (TextUtils.isEmpty(signature) || (urlParams = getUrlParams(signature)) == null || urlParams.isEmpty()) {
                return;
            }
            for (String str8 : urlParams.keySet()) {
                builder.addQueryParameter(str8, urlParams.get(str8));
            }
        } catch (Exception unused) {
        }

发现加密的是一个接口

CleanShot 2024-05-12 at 18.28.15

我们要找实现这个类型的方法

implements ISignatureHandler

搜索不到

所以我们直接搜索 ISignatureHandler

发现在这个类里有一定线索

CleanShot 2024-05-12 at 18.30.49

在初始化时传入了

CleanShot 2024-05-12 at 18.31.19

查找调用此函数的位置的方法

CleanShot 2024-05-12 at 18.31.41

在initapp中传入了,我们往上跟入

CleanShot 2024-05-12 at 18.32.07

进入方法查看

CleanShot 2024-05-12 at 18.32.27

CleanShot 2024-05-12 at 18.32.47

原来是使用new创建的 所以下次搜索可以使用

new ISignatureHandler

来搜索定位

CleanShot 2024-05-12 at 18.33.21

定位到关键函数,接下来进行hook 并主动调用:

function call(){
    Java.perform(function () {
        let BitmapkitUtils = Java.use("com.jingdong.common.utils.BitmapkitUtils");

        let context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext();
        let str = "wareBusiness";
        let str2 ='{"abTest800":true,"acceptPrivacy":true,"avoidLive":false,"bbtf":"","brand":"Redmi","businessType":"","bybt":"","cityCode":72,"cityId":0,"cpsNoTuan":null,"darkModelEnum":3,"districtId":0,"euaf":false,"eventId":"MyHistory_Product","fromType":0,"isDesCbc":true,"isFromOpenApp":true,"latitude":"0.0","lego":true,"longitude":"0.0","model":"Redmi Note 11T Pro","ocrFlag":false,"oneboxChannel":false,"oneboxKeyword":"","oneboxSource":"","openSimilarFlag":"","overseas":0,"pdVersion":"1","personas":null,"pluginVersion":101050,"plusClickCount":0,"plusLandedFatigue":0,"popBusinessType":"","poplayer":false,"productJdv":"-1|kong|t_1000210271_502774|zssc|d36d13b9-61c4-4fdf-b7f2-11dbc28d14dd-p_1999-pr_100746-at_502774-tg_ext_0-00-0-tgx-5050508-3935-20231110|1699610674","provinceId":"0","prstate":"0","refreshMe":null,"searchWareflag":"","selfDelivery":"0","skuId":"48905840961","source_type":"wojing_history","source_value":"","townId":0,"uAddrId":"0","utmMedium":null,"wareInnerSource":"extra.inner.source.init","yrqNew":"1"}'
        let str3 = "789e43b8e08521ee";
        let str4 = "android";
        let str5 = "12.2.2";

        let result = BitmapkitUtils.getSignFromJni(context, str, str2, str3, str4, str5);
        console.log("BitmapkitUtils.getSignFromJni result = " + result);
    });
}

CleanShot 2024-05-12 at 18.46.33

发现疑似是hash函数

hook dlsym函数,得知函数加载的so

libjdbitmapkit.so

CleanShot 2024-05-12 at 18.48.24

定位到要分析的函数,由于位数疑似md5,所以使用龙哥的findhash插件,进行寻找

CleanShot 2024-05-12 at 18.49.03

CleanShot 2024-05-12 at 18.49.19

熟悉的朋友应该认出了,这个就是md5运算部分,我们寻找上层引用

CleanShot 2024-05-12 at 18.49.55

再次寻找上层引用

CleanShot 2024-05-12 at 18.53.59

发现调用位置就在我们目标分析的函数里(如果没跟到的小伙伴可以打印堆栈)

CleanShot 2024-05-12 at 18.55.43

我们进行hook来分析入参



(function () {

    // @ts-ignore
    function print_arg(addr) {
        try {
            var module = Process.findRangeByAddress(addr);
            if (module != null) return "\n"+hexdump(addr) + "\n";
            return ptr(addr) + "\n";
        } catch (e) {
            return addr + "\n";
        }
    }

    // @ts-ignore
    function hook_native_addr(funcPtr, paramsNum) {
        var module = Process.findModuleByAddress(funcPtr);
        try {
            Interceptor.attach(funcPtr, {
                onEnter: function (args) {
                    this.logs = "";
                    this.params = [];
                    // @ts-ignore
                    this.logs=this.logs.concat("So: " + module.name + "  Method: sub_25E0 offset: " + ptr(funcPtr).sub(module.base) + "\n");
                    for (let i = 0; i < paramsNum; i++) {
                        this.params.push(args[i]);
                        this.logs=this.logs.concat("this.args" + i + " onEnter: " + print_arg(args[i]));
                    }
                }, onLeave: function (retval) {
                    for (let i = 0; i < paramsNum; i++) {
                        this.logs=this.logs.concat("this.args" + i + " onLeave: " + print_arg(this.params[i]));
                    }
                    this.logs=this.logs.concat("retval onLeave: " + print_arg(retval) + "\n");
                    console.log(this.logs);
                }
            });
        } catch (e) {
            console.log(e);
        }
    }
    // @ts-ignore
    hook_native_addr(Module.findBaseAddress("libjdbitmapkit.so").add(0x25e0), 0x3);
})();


CleanShot 2024-05-12 at 18.56.26

发现入参是一段base64,解密后是乱码

在md5前,明文进行了额外处理:

CleanShot 2024-05-12 at 18.57.48

v39是我们要分析的密文,

继续往上跟踪v39生成的位置

CleanShot 2024-05-12 at 18.58.51

经过hook可知

密文由sub_18c9c计算而来

CleanShot 2024-05-12 at 18.59.33

其中两个参数是rand随机生成的

在这里决定了sign由哪个函数进行加密

CleanShot 2024-05-12 at 19.04.24

今天我们分析case2里面的加密函数,也是最简单的,后面我会分析剩下两个算法的计算方法

CleanShot 2024-05-12 at 19.05.01

CleanShot 2024-05-12 at 19.05.10

算法肉眼可见的可以复现,所以我们进行hook入参和出参进行分析

(function () {

    // @ts-ignore
    function print_arg(addr) {
        try {
            var module = Process.findRangeByAddress(addr);
            if (module != null) return "\n"+hexdump(addr) + "\n";
            return ptr(addr) + "\n";
        } catch (e) {
            return addr + "\n";
        }
    }

    // @ts-ignore
    function hook_native_addr(funcPtr, paramsNum) {
        var module = Process.findModuleByAddress(funcPtr);
        try {
            Interceptor.attach(funcPtr, {
                onEnter: function (args) {
                    this.logs = "";
                    this.params = [];
                    // @ts-ignore
                    this.logs=this.logs.concat("So: " + module.name + "  Method: sub_6858 offset: " + ptr(funcPtr).sub(module.base) + "\n");
                    for (let i = 0; i < paramsNum; i++) {
                        this.params.push(args[i]);
                        if (i==0){
                            // this.logs=this.logs.concat("this.args" + i + " onEnter: " +args[i].readCString());
                        }
                        else if(i==3){
                            this.logs=this.logs.concat("this.args" + i + " onEnter: " +hexdump(args[3]));
                        }
                        else {
                            this.logs=this.logs.concat("this.args" + i + " onEnter: " + print_arg(args[i]));

                        }
                    }
                }, onLeave: function (retval) {
                    for (let i = 0; i < paramsNum; i++) {
                        this.logs=this.logs.concat("this.args" + i + " onLeave: " + print_arg(this.params[i]));
                    }
                    this.logs=this.logs.concat("retval onLeave: " + print_arg(retval) + "\n");
                    console.log(this.logs);
                }
            });
        } catch (e) {
            console.log(e);
        }
    }
    // @ts-ignore
    hook_native_addr(Module.findBaseAddress("libjdbitmapkit.so").add(0x1882C), 0x4);
})();

CleanShot 2024-05-12 at 19.08.43

不是所有的call都能触发这个分支,得多call几次

CleanShot 2024-05-12 at 19.09.14

我们可以看到明文数据了

(function () {

    // @ts-ignore
    function print_arg(addr) {
        try {
            var module = Process.findRangeByAddress(addr);
            if (module != null) return "\n"+hexdump(addr) + "\n";
            return ptr(addr) + "\n";
        } catch (e) {
            return addr + "\n";
        }
    }

    // @ts-ignore
    function hook_native_addr(funcPtr, paramsNum) {
        var module = Process.findModuleByAddress(funcPtr);
        try {
            Interceptor.attach(funcPtr, {
                onEnter: function (args) {
                    this.logs = "";
                    this.params = [];
                    // @ts-ignore
                    this.logs=this.logs.concat("So: " + module.name + "  Method: sub_6858 offset: " + ptr(funcPtr).sub(module.base) + "\n");
                    for (let i = 0; i < paramsNum; i++) {
                        this.params.push(args[i]);
                        if (i==1){
                            this.logs=this.logs.concat("this.args" + i + " onEnter: " +args[i].readCString());
                        }
                        else if(i==3){
                            this.logs=this.logs.concat("this.args" + i + " onEnter: " +hexdump(args[3]));
                        }
                        else {
                            this.logs=this.logs.concat("this.args" + i + " onEnter: " + print_arg(args[i]));

                        }
                    }
                }, onLeave: function (retval) {
                    for (let i = 0; i < paramsNum; i++) {
                        this.logs=this.logs.concat("this.args" + i + " onLeave: " + print_arg(this.params[i]));
                    }
                    this.logs=this.logs.concat("retval onLeave: " + print_arg(retval) + "\n");
                    console.log(this.logs);
                }
            });
        } catch (e) {
            console.log(e);
        }
    }
    // @ts-ignore
    hook_native_addr(Module.findBaseAddress("libjdbitmapkit.so").add(0x1882C), 0x4);
})();

修改脚本 拿到arg1的keyCleanShot 2024-05-12 at 19.11.47

CleanShot 2024-05-12 at 19.12.51

CleanShot 2024-05-12 at 19.13.00

afcb2afb1f349bed06555aef7fd47cecbec115c6083eafa51f608df10bdd350bab2a21cb1ffb6cb7df2b9dea43cf07ccaaf12ffd1b378cf126678edb57840d1ebbcb2184c52ca2ee265595e144cf35c4aff728cb08ef5ee11f658f9a088435f66bf03ef931275eb9df4382dc7bd335f66bf031cb0c2991f2284586e873840dcc6b82eefa1c2da0a1f7134ba430c5401ea2d12bfc08ed76b6ef1d4bdb47de5013adb0e688f7ed9ff728bfb4cc43cb41cc63fc31c437ef5eeb1e63b0c57dce7c368efc31c5c5c56f93df55b6eb42d4400dbddf20baddf358a122668ede309c790b95c12184c520a2e42b6596dc309c3517a2de15cb1f2aaef814419be772df7a1e…省略

使用c语言进行复现,发现结果一致,接下来把结果from hex 再base64

把base64进行md5加密 即可得到京东的sign

void TenSeattosEncrypt(char* input, int input_len)

{

    int v4, v5;

    char v6;

    const char* TenSeattos_key = "80306f4370b39fd5630ad0529f77adb6";

    unsigned char table[0x10] = { 0x37, 0x92, 0x44, 0x68, 0xA5, 0x3D, 0xCC, 0x7F, 0xBB, 0xF, 0xD9, 0x88, 0xEE, 0x9A, 0xE9, 0x5A };

    for (int i = 0; i != input_len; ++i) {

        v4 = i & 7;

        v5 = table[i & 0xF];

        v6 = (v5 + (*(unsigned char*)(input + i) ^ *(unsigned char*)(TenSeattos_key + v4) ^ table[i & 0xF])) ^ table[i & 0xF];

        *(unsigned char*)(input + i) = v6;

        *(unsigned char*)(input + i) = *(unsigned char*)(TenSeattos_key + v4) ^ v6;

    }

}

至此,我们已经完成了最容易的一个分支的京东sign的计算

下一篇文章将分析另外的两个加密过程,涉及到unidbg/unicorn的使用,记录算法还原的过程

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值