首页
社区
课程
招聘
[原创]记录一次某app的sign分析过程(篇章一)新手推荐
发表于: 2024-7-29 14:19 4649

[原创]记录一次某app的sign分析过程(篇章一)新手推荐

2024-7-29 14:19
4649

[!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

1
2
3
4
5
6
7
8
9
10
11
12
13
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创建的 所以下次搜索可以使用

1
new ISignatureHandler

来搜索定位

CleanShot 2024-05-12 at 18.33.21

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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来分析入参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(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入参和出参进行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(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

我们可以看到明文数据了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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的使用,记录算法还原的过程


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 8
支持
分享
最新回复 (7)
雪    币: 297
活跃值: (3378)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
这样分析,你就不是我兄弟了
2024-7-29 16:00
1
雪    币: 3150
活跃值: (1712)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
3
gtict 这样分析,你就不是我兄弟了
哪里分析的不好,大佬来指正,我今晚就改
2024-7-29 19:23
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
大佬,能帮忙app去壳吗?https://f.ws28.cn/f/epnkmz8iilm
2024-7-29 19:24
0
雪    币: 27
活跃值: (1080)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
太强了大佬,我只分析到能分析到它是随机的算法,感觉有点复杂就没有往下分析了...我得再向大佬学习学习
2024-8-7 19:10
0
雪    币: 202
活跃值: (1255)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
大佬,能不能帮忙分析一下i国网这个app,怎么开启webview调试
2024-8-29 10:42
0
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
楠楠?
2024-11-4 11:31
0
雪    币: 1049
活跃值: (115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
有没有大佬告诉我,我连包都抓不到该怎么办?模拟器可以抓到api的接口,真机上只有/log/sdk/v2的接口
2024-12-12 13:29
0
游客
登录 | 注册 方可回帖
返回
//