首页
社区
课程
招聘
[分享]sslpinning案例 ,byd验证服务端证书+白盒AES DFA
发表于: 2025-1-7 14:18 3678

[分享]sslpinning案例 ,byd验证服务端证书+白盒AES DFA

2025-1-7 14:18
3678

前言

之前发了一个 ssl pinning 的文章。里面没有什么案例。最近刚好看到BYD的这个app,在网络请求的过程中使用了 ssl pinning 。且是通过 X509Manager 方式实现的。所以这里就记录下分析过程。另外这个 app 的native 层也是 白盒AES 与五菱app一模一样的。

目标信息

版本:770

接口:进入app首页任意接口

是否有壳:bangbang壳

参数:request

参数定位

ssl pinning

打开抓包工具,这个提示就是 app 做了证书绑定。直接上 sslbypass

image-20241226153412492

根据之前的文章 Platform,checkServerTrusted 就是使用 x509 来验证服务器的证书,它调用的就是 app 继承实现了的 x509

所以就是 a.d&1

image-20250102112033503

1
2
3
4
5
6
var x509 = Java.use("com.byd.aeri.core.net.a.d$1");
                    console.log("x509 ",x509)
                    x509['checkServerTrusted'].implementation =function(arg0,arg1){
                        console.log("x509 check");
                        return ;
                    }  

数据就出来了

image-20250102112853207

charles 能看到数据了, 请求数据和返回的数据都被加密了。这次的目标就是 request。

image-20250102155335857

image-20250102160127084

request 参数分析

硬邦邦的壳,上次分析wu菱app也是用的邦邦。没啥说的,上老演员 xrt 把它脱了。

image-20241226152318018

演员表演完毕

image-20241226155352135

搜索关键词 request ,没有找到。再换一个方式搜索 “request” 有几个可疑的地方

image-20250107095838278

第一个像是检查长度的,先分析第二个

image-20250107100103140

数据来自 checkCodeUtil.checkcode("F" + a2, 1)

image-20250107100208515

checkcode 就是到native 层了

image-20250107100324617

在 CheckCodeUtils 最下方也找到了对应的 so

image-20250107100404625

hook 来获取传入的参数

目标接口

cf5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1K9h3I4A6L8X3E0S2M7s2m8K6k6i4u0$3k6i4u0Q4x3X3c8U0L8W2)9J5k6h3u0&6k6q4)9J5k6h3q4#2N6r3!0Q4x3V1k6U0L8s2g2T1i4K6u0r3i4K6y4r3M7#2)9K6c8p5k6G2M7V1W2F1N6r3g2J5k6X3q4U0k6f1q4H3M7q4)9J5k6h3k6G2M7Y4N6S2M7X3c8u0L8X3k6G2M7X3#2S2N6r3W2G2L8V1k6D9L8%4N6Q4x3U0k6S2L8i4m8Q4x3@1u0K6k6i4u0$3K9h3y4W2c8r3W2J5i4K6y4p5b7i4m8H3i4K6u0W2b7$3!0E0L8i4g2F1K9i4c8&6i4K6u0W2g2r3!0H3K9h3y4x3K9i4y4@1

对应的 request 的参数为

image-20250107101203646

对比之后 frida hook 结果中有我们的目标数据

image-20250107101245462

CheckCodeUtil.checkcode is called: str=F{"encryData":"D4761015BF05FD9400E1440CADB654F398C9DD70EE8838DEE4A6C4093D3894A7B66C09A0927F8FEDA8C15B0F56A6FC6DF1787ACC97228B5894C8DFFD5272752B","orderType":1,"identifier":"club100","reqTimestamp":"1736215765773","imeiMD5":"B17C3F7EEE64C071B136EE850685A81C","sign":"2Ce9D2d38B28AC4f71da48a8BC75A96a8Da3Ddf","numPerPage":5,"identifierType":2,"pageNum":1,"appChannel":"1","status":"3","objective":""}, i=1, str2=1736215765774

CheckCodeUtil.checkcode result=FwQZ914rbytVRu2pdCt5ACvoOYn1jwSDfdo3UCMN3efYq1W6xNKqIzHCnpCk4DC82ADR+PD5orUlcbwkVe92Yr5NSKc5zoPcQGKvgwxMJztBnVRIp4610zdP9ZXtK3WKBaB03qVfxr2OTO0DkR+++0sM91EPQDPl9JNSjhZKqbTutbj6BrFTWtTC8nliGnoLj4AcJlWxdr+WSfFd0t09oHy1Bz77ndYXpYB7xYnqGaNTR7g08WxV4NC73s1jZmR0xCl4c2MN6+oplcOLEtT38KWZkkucwJah2FJ0dloXGmNmLwZV5I7LINep2x1k1PECO0Ib1Hs/ibMk1zqiMNSuHT0mS3vYJf4EiDxjo7GEohhHSJgk2ipQN/SprfeV0dCvWTJI+cnrbIgs5O6gnzGihNGfh0v51/92gngul3gz+ZCBqdPhaKOtxWR9wagkaT90UCvYpXWTvqpLX2cV3R1p9y/f3AOUv5nI0zPxZDnZlRCORLqOn99uQCmkf3x63x0Pv/f3GQ7+q45QT80PzKzo0nQlxunJ1stmjNUi6O7VAttQBRp8tQ7vdIhnoE6amXOT+2sRpgd4CfH6lsd6FHP78WtSadsYbFGPsyLHJlHk2uToaWusi+MKTsclM3Fqz1YuBccSIijrV5qxlfQ3p+naUYzTO6zdBuk/TVhH527vds0UZIMWwLege5McHTtVmTfNbVYxBlyQcX5pV77F0nrbwm0P81XicGIA+I9wRLCydo+9uRX+kGKnQ3vHmB+D1SXsV

参数搞定,接下来基操进入 native 层。

native 分析

IDA 打开 encrypt ,搜索 java 能够找到对应方法,说明是静态注册的。

image-20250103153736295

进入 checkcode 查看发现是没有汇编指令的,猜测应该是 so 加固了。这个时候需要先dump内存中的so。因为内存中的 so 在 app 运行起来之后一定是解密状态的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function dump_so(so_name) {
    Java.perform(function () {
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        var dir = currentApplication.getApplicationContext().getFilesDir().getPath();
        var libso = Process.getModuleByName(so_name);
        console.log("[name]:", libso.name);
        console.log("[base]:", libso.base);
        console.log("[size]:", ptr(libso.size));
        console.log("[path]:", libso.path);
        var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";
        var file_handle = new File(file_path, "wb");
        if (file_handle && file_handle != null) {
            Memory.protect(ptr(libso.base), libso.size, 'rwx');
            var libso_buffer = ptr(libso.base).readByteArray(libso.size);
            file_handle.write(libso_buffer);
            file_handle.flush();
            file_handle.close();
            console.log("[dump]:", file_path);
 
        }
    });
}

dump出来的so信息是不完整的,需要修复 so。使用开源工具 c6dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6r3z5p5I4q4c8W2c8Q4x3V1k6e0L8@1k6A6P5r3g2J5

1
.\soFixer-Windows-64.exe -s D:\pyworkSpace\frida-code\app\byd\libencrypt.so_0x7026013000_0x1d9000.so -o D:\pyworkSpace\frida-code\app\byd\libencrypt.so_0x7026013000_0x1d9000_fix.so  -m 0x7026013000  -d

使用修复后的 so 导入 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
public class fbyd extends AbstractJni {
    private AndroidEmulator emulator;
    private VM vm;
    private final Module module;
 
 
    public fbyd() {
        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.byd.aeri.caranywhere")
                .build();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
 
        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/java/com/byd/aeri/caranywhere/byd770.apk"));
        vm.setJni(this);
        vm.setVerbose(true);
        // libandroid.so 用一个虚拟的 so 来代替
        new AndroidModule(emulator, vm).register(memory);
 
        //so 模块
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/byd/aeri/caranywhere/libencrypt.so_0x7026013000_0x1d9000_fix.so"), true);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
 
    }
 
    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature) {
            case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;":
                return vm.resolveClass("android/app/ActivityThread").newObject(null);
            case "android/os/SystemProperties->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;":
                String arg = varArg.getObjectArg(0).getValue().toString();
                System.out.println("SystemProperties get arg===>" + arg);
                if (arg.equals("ro.serialno")) {
                    return new StringObject(vm, "9B131FFBA001Y5");
                }
 
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }
 
    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature){
            case "android/app/ActivityThread->getSystemContext()Landroid/app/ContextImpl;":
                return vm.resolveClass("android/app/ContextImpl").newObject(null);
            case "android/app/ContextImpl->getPackageManager()Landroid/content/pm/PackageManager;":
                return vm.resolveClass("android/content/pm/PackageManager").newObject(null);
            case "android/app/ContextImpl->getSystemService(Ljava/lang/String;)Ljava/lang/Object;":
                String arg = varArg.getObjectArg(0).getValue().toString();
                System.out.println("getSystemService arg ===> "+arg);
                return vm.resolveClass("android.net.wifi").newObject(signature);
            case "android/net/wifi->getConnectionInfo()Landroid/net/wifi/WifiInfo;":
                return vm.resolveClass("android/net/wifi/WifiInfo").newObject(null);
            case "android/net/wifi/WifiInfo->getMacAddress()Ljava/lang/String;":
                return new StringObject(vm, "02:00:00:00:00:00");
 
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }
 
    public static void main(String[] args) {
        fbyd b = new fbyd();
    }
}

来个主动调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    private void call_native(){
//        253AC Java_com_bangcle_comapiprotect_CheckCodeUtil_checkcode
        List<Object> args = new ArrayList<>();
        args.add(vm.getJNIEnv());
        args.add(0);
        String arg1 = "F{\"encryData\":\"5D8B849FC54256C755A5B15BB96F7E9F0BE482FA77C91EF02960172AC72B4FFD\",\"identifier\":\"club100\",\"reqTimestamp\":\"1736215765786\",\"imeiMD5\":\"B17C3F7EEE64C071B136EE850685A81C\",\"sign\":\"E4f996d9FC2eAEb460aeFCf4DC8eB732CC95E539\",\"numPerPage\":10,\"identifierType\":2,\"pageNum\":0,\"appChannel\":\"1\",\"objective\":\"\"}";
        int arg2 = 1;
        String tm = "1736215765788";
 
        args.add(vm.addLocalObject(new StringObject(vm,arg1)));
        args.add(arg2);
        args.add(vm.addLocalObject(new StringObject(vm,tm)));
        Number retNum = module.callFunction(emulator,0x253AC,args.toArray());
        StringObject ret = vm.getObject(retNum.intValue());
        System.out.println("md5result:" + ret);    
    }

得到的结果

md5result:FwQZ914rbytVRu2pdCt5ACvoOYn1jwSDfdo3UCMN3efYq1W6xNKqIzHCnpCk4DC82ADR+PD5orUlcbwkVe92Yr5NSKc5zoPcQGKvgwxMJztBnVRIp4610zdP9ZXtK3WKBaB03qVfxr2OTO0DkR+++0sM91EPQDPl9JNSjhZKqbTutbj6BrFTWtTC8nliGnoLj4AcJlWxdr+WSfFd0t09oHy1Bz77ndYXpYB7xYnqGaNTR7g08WxV4NC73s1jZmR0xCl4c2MN6+oplcOLEtT38KWZkkucwJah2FJ0dloXGmNmLwZV5I7LINep2x1k1PECO0Ib1Hs/ibMk1zqiMNSuHT0mS3vYJf4EiDxjo7GEohhHSJgk2ipQN/SprfeV0dCvWTJI+cnrbIgs5O6gnzGihNGfh0v51/92gngul3gz+ZCBqdPhaKOtxWR9wagkaT90UCvYpXWTvqpLX2cV3R1p9y/f3AOUv5nI0zPxZDnZlRCORLqOn99uQCmkf3x63x0Pv/f3GQ7+q45QT80PzKzo0nQlxunJ1stmjNUi6O7VAttTt3ppZQgP3muw4h7H4zynlWGbC3EAFOA0mVto3K+8zH1TOIO+d83jEXqBf1OWImvHPcj6RTJB5e5CvIMRKUfO0Xd0BHoHTI88wl8kLr/sf1oIbjWjdOEbwZTwLidUa9m1Aw21JlKg+JnFo+MsYP1QccR5nA13FeyKYIukW39P1ehAvGMP406PLkLaEb9rzVuZWGH2S5aeOp408aIoIgxgpeJuybaB0h++FFlmzuvglkA==

和抓包的一致。然后接着分析 so 算法

so 算法起始和 五菱那篇的 AES DFA 一模一样的。可以转去看那篇文章哈。

okhttp 证书绑定流程 ssl pinning分析

五菱AES DFA。如今弯道今犹在,不见当年过弯人

总结

算上水了一篇文章吧,主要是学习怎么过 ssl pinning 。多谢各位大佬的时间啦!!


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

最后于 2025-1-7 14:19 被绿豆粥编辑 ,原因: 改标题
收藏
免费 2
支持
分享
最新回复 (8)
雪    币: 761
活跃值: (700)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习了。
2025-1-7 18:08
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-1-7 23:04
0
雪    币: 928
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
学习新知识
2025-1-8 01:41
0
雪    币: 142
活跃值: (3448)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
xrt 这个是啥工具,,
2025-1-8 09:50
0
雪    币: 200
活跃值: (201)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
gtict xrt 这个是啥工具,,
同问
2025-1-8 10:46
0
雪    币: 18
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
gtict xrt 这个是啥工具,,
fart吧 可能它自己改了
2025-1-8 14:15
0
雪    币: 1639
活跃值: (946)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
8
xiayutian_ fart吧 可能它自己改了
是的,按照fart改的,就少了fart特征 
2025-1-8 21:09
0
雪    币: 18
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
绿豆粥 是的,按照fart改的,就少了fart特征 [em_014]
太强啦
2025-1-9 14:37
0
游客
登录 | 注册 方可回帖
返回