首页
论坛
课程
招聘
[原创]某东到家 app so signkeyV1 参数分析
2021-11-12 00:24 19833

[原创]某东到家 app so signkeyV1 参数分析

2021-11-12 00:24
19833

目录

前言

京东到家 app signKeyv1 参数分析,版本 8.14.0

charles 抓包

图片描述

 

就是 djencrypt 这个参数,是一个加密串,分析一下

java 层分析

图片描述

 

全局搜索定位到这个函数 base.net.volley.BaseStringRequest.getParams,跟进 DaojiaAesUtil.encrypt 看看

 

图片描述

 

在跟进 AesCbcCrypto.encrypt 这个函数

 

图片描述

 

里面写的还是比较清楚的,加密方式是 AES/CBC/PKCS5Padding 使用 cyberchef 解密试试

 

图片描述

 

解密成功,主要是分析这个 signKeyV1 参数,看长度是 64,猜测是 sha256 加密,分析一下

 

图片描述

 

全局搜索定位到这里,调用 k2 native 函数获取的,在 libjdpdj.so 文件里

 

图片描述

 

打开 so 进入 JNI_OnLoad 函数,这里是动态注册的,点击 off_117004

 

图片描述

 

这里看到,函数注册列表,点进 gk2 函数

 

图片描述

 

前面是一些数据处理,下面有个 j_hmac_sha256 函数,可以确实是用的 hmac sha256 算法

 

图片描述

 

这里的符号都没去掉,init update final 函数都能看到,这里应该是标准的算法,因为使用的是 openssl 库,先写个 frida hook 一下

frida hook

1
2
3
4
5
6
7
8
9
10
11
12
13
function hook() {
    var javaString = Java.use('java.lang.String');
    var zCls = Java.use('jd.net.z');
 
    zCls.k2.implementation = function (a) {
        console.log('zCls.k2.a: ', javaString.$new(a));
 
        var res = this.k2(a);
        console.log('zCls.k2.res: ', res);
 
        return res;
    }
}

这里 hook java k2 函数的输入输出

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
function hookSo1() {
    var hmac_sha256 = Module.findExportByName('libjdpdj.so', 'hmac_sha256')
    var HMAC_CTX_init = Module.findExportByName('libjdpdj.so', 'HMAC_CTX_init')
    var HMAC_Update = Module.findExportByName('libjdpdj.so', 'HMAC_Update')
    var HMAC_Init_ex = Module.findExportByName('libjdpdj.so', 'HMAC_Init_ex')
 
    Interceptor.attach(hmac_sha256, {
        onEnter: function (args) {
            console.log('hmac_sha256 参数 1: ', hexdump(args[0]));
            console.log('hmac_sha256 参数 2: ', hexdump(args[1]));
            console.log('hmac_sha256 参数 3: ', hexdump(args[2]));
            console.log('hmac_sha256 参数 4: ', hexdump(args[3]));
        },
        onLeave: function (retValue) {
        }
    })
 
    Interceptor.attach(HMAC_CTX_init, {
        onEnter: function (args) {
            console.log('HMAC_CTX_init 参数 1: ', hexdump(args[0]));
        },
        onLeave: function (retValue) {
        }
    })
 
    Interceptor.attach(HMAC_Update, {
        onEnter: function (args) {
            console.log('HMAC_Update 参数 1: ', hexdump(args[0]));
            console.log('HMAC_Update 参数 2: ', hexdump(args[1], {length: 1200}));
            console.log('HMAC_Update 参数 3: ', hexdump(args[2]));
        },
        onLeave: function (retValue) {
        }
    })
 
    Interceptor.attach(HMAC_Init_ex, {
        onEnter: function (args) {
            console.log('HMAC_Init_ex 参数 1: ', hexdump(args[0]));
            console.log('HMAC_Init_ex 参数 2: ', hexdump(args[1]));
            console.log('HMAC_Init_ex 参数 3: ', hexdump(args[2]));
            console.log('HMAC_Init_ex 参数 4: ', hexdump(args[3]));
            console.log('HMAC_Init_ex 参数 5: ', hexdump(args[4]));
        },
        onLeave: function (retValue) {
        }
    })
}

这里在 hook 一些 so 函数,hamc 会有个 key 一般在 init 的时候初始化

1
2
3
4
5
6
function main() {
    Java.perform(function () {
        hook();
        hookSo1();
    })
}

启动脚本 frida -UF -l hook.js | tee hook.log

 

图片描述

 

k2 函数的输入是请求参数

 

图片描述

 

HMAC_Init_ex so 函数的参数二,长度 32 猜测是 hamc key

 

图片描述

 

HMAC_Update 函数是请求参数

 

图片描述

 

最后的加密结果 ae07cde50402ef91660dea93dc196f7f82e7bc04322baf4022dc2879434f3fae 是这个,来验证一下

 

图片描述

 

使用 cyberchef 加密,结果一样,正是 hmac sha256

 

Tips: 下面在使用 unidbg 跑起来,毕竟多掌握一些工具总有用处

unidbg

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.xiayu.jingdongdaojia;
 
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.debugger.Debugger;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
 
import java.io.File;
import java.io.IOException;
 
public class SignKeyV1Test extends AbstractJni {
    private final AndroidEmulator emulator;
    private final Module module;
    private final VM vm;
 
    public String apkPath = "apk path";
    public String soPath = "so path";
 
    private static LibraryResolver createLibraryResolver() {
        return new AndroidResolver(23);
    }
 
    private static AndroidEmulator createARMEmulator() {
        return AndroidEmulatorBuilder.for32Bit().build();
    }
 
    public SignKeyV1Test() {
        emulator = createARMEmulator();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(createLibraryResolver());
        vm = emulator.createDalvikVM(new File(apkPath));
        vm.setVerbose(true);
 
        DalvikModule dm = vm.loadLibrary(new File(soPath), false);
        vm.setJni(this);
 
        dm.callJNI_OnLoad(emulator);
        module = dm.getModule();
    }
 
    public void callGetSignKeyV1() {
        DvmClass zClass = vm.resolveClass("jd/net/z");
 
        DvmObject<?> strRc = zClass.callStaticJniMethodObject(
                emulator,
                "k2([B)Ljava/lang/String;",
                new ByteArray(vm, "参数".getBytes())
        );
 
        System.out.println("callGetSignKeyV1: " + strRc.getValue());
    }
 
    public static void main(String[] args) throws IOException {
        SignKeyV1Test signKeyV1 = new SignKeyV1Test();
 
        signKeyV1.callGetSignKeyV1();
        signKeyV1.destroy();
    }
 
    private void destroy() throws IOException {
        emulator.close();
    }
}

代码写完跑起来

 

图片描述

 

这报错了,缺少函数 jd/utils/StatisticsReportUtil->getSign()Ljava/lang/String; 调用的是京东到家 apkjava 代码

 

图片描述

 

点进来看一下,打开逻辑是获取 apk 的签名之类的,这里的依赖比较多,不是很好补,一般签名啥的都是固定,直接 frida call 一下,获取返回值

1
2
3
4
5
6
function callGetSign() {
    var StatisticsReportUtil = Java.use('jd.utils.StatisticsReportUtil');
 
    var res = StatisticsReportUtil.getSign();
    console.log(res)
}

运行成功,获取返回值

 

图片描述

 

unidbg 补一下,直接写死字符串

 

图片描述

 

再次运行结果出来了,结果相同

 

Tips: 后面在打算学习学习 ida gdb 动态调试,暂时留空

IDA 动态调试

// TODO

GDB 动态调试

// TODO


原文链接:某东到家 app so signkeyV1 参数分析


【公告】看雪团队招聘安全工程师,将兴趣和工作融合在一起!看雪20年安全圈的口碑,助你快速成长!

最后于 2021-11-12 00:28 被爬山的小脑虎编辑 ,原因:
收藏
点赞4
打赏
分享
最新回复 (8)
雪    币: 264
活跃值: 活跃值 (708)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
diypc 活跃值 2021-11-17 22:52
2
0
Nice收藏了, 老哥的文章质量很高啊
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
xiangshui 活跃值 2021-11-19 11:22
3
0
请问是怎么躲过frida检测的呢?
雪    币: 2967
活跃值: 活跃值 (1611)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
爬山的小脑虎 活跃值 2 2021-11-19 13:14
4
0
xiangshui 请问是怎么躲过frida检测的呢?
葫芦娃 frida 可以过检测
雪    币: 571
活跃值: 活跃值 (1176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
灵风_spirit 活跃值 2021-12-22 17:07
5
0
你好,我学习后实践了一下,有个报错[main]I/JNI: error 加密结果为空。
可否帮忙看看,https://github.com/lixiaolevae/unidbg_lxl 测试类为JDPaiDaoJiaSoInvoke
雪    币: 2967
活跃值: 活跃值 (1611)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
爬山的小脑虎 活跃值 2 2021-12-22 23:31
6
0
代码看起来没啥问题,不过在调用 k2([B)Ljava/lang/String; 函数时,参数2,你传真正的参数试一下,我是 frida hook 出来的,写文章时没贴出来
雪    币: 2967
活跃值: 活跃值 (1611)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
爬山的小脑虎 活跃值 2 2021-12-22 23:32
7
0
灵风_spirit 你好,我学习后实践了一下,有个报错[main]I/JNI: error 加密结果为空。 可否帮忙看看,https://github.com/lixiaolevae/unidbg_lxl 测试类为JD ...
代码看起来没啥问题,不过在调用 k2([B)Ljava/lang/String; 函数时,参数2,你传真正的参数试一下,我是 frida hook 出来的,写文章时没贴出来
雪    币: 571
活跃值: 活跃值 (1176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
灵风_spirit 活跃值 2021-12-23 10:06
8
0
爬山的小脑虎 代码看起来没啥问题,不过在调用 k2([B)Ljava/lang/String; 函数时,参数2,你传真正的参数试一下,我是 frida hook 出来的,写文章时没贴出来
传真正的入参确实成功拿到了,谢谢指点解答。
雪    币: 571
活跃值: 活跃值 (1176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
灵风_spirit 活跃值 2021-12-23 10:08
9
0
灵风_spirit 传真正的入参确实成功拿到了,谢谢指点解答。
但我没想明白原因,so里面hmacsha256加密前也没有特殊的校验逻辑呀,比如对字符串长度校验等等
游客
登录 | 注册 方可回帖
返回