美文网首页
soul cs字段unidbg实现

soul cs字段unidbg实现

作者: ever_hu | 来源:发表于2022-01-20 20:08 被阅读0次

    soul cs字段unidbg实现

    环境

    app 3.83.0

    Java

    image-20220120150049788

    jadx搜索

    image-20220117164321104

    查找用例

    image-20220117164420865

    cn.soulapp.android.net.q.j.b

    image-20220117164450987

    cn.soulapp.android.net.SoulNetworkSDK.g

    image-20220117164538640

    cn.soulapp.android.soulpower.SoulPowerful.i

    image-20220117164704641

    cn.soulapp.android.soulpower.SoulPowerful.h

    image-20220117165143223

    hook 看看

    android hooking watch class_method cn.soulapp .android.soulpower.SoulPowerful.h --dump-args --dump-return
    
    image-20220120141036784

    可以看到,第二个参数是时间戳,第三个参数url,第四个参数是headers里面的参数拼接起来的。这个的签名长度是36,是一个比较不常见的数字,对比了几个签名,发现都是028f**77ac的形式(实际上,在另一台手机中,cs是02af**008c的形式),接下来研究一下cs的具体构成。

    通过frida,我们自己控制传入的参数,看看输出是怎样的。

    // hook_soul.js
    function callh(ts, url, header) {
        var soul = Java.use("cn.soulapp.android.soulpower.SoulPowerful");
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        var context = currentApplication.getApplicationContext();
        var ret = soul.h(context, ts, url, header);
        // console.log("h-ret:", ret);
        return ret;
    }
    
    rpc.exports = {
        callh: callh
    }
    
    # hook_soul.py
    import frida
    
    def read_js():
        with open(r"soul\hook_soul.js", 'r') as fp:
            return fp.read()
    
    def on_message(message, data):
        pass
    
    if __name__ == '__main__':
        device = frida.get_usb_device()
        pid = device.get_frontmost_application().pid
        session = device.attach(pid)
        js = read_js()
        script = session.create_script(js)
        script.on('message', on_message)
        script.load()
    
        url = 'hello'
        header = 'everhu'
        for ts in [0x000000ff, 0x0000ff00, 0x00ff0000, 0x01234567, 0x10325476, 0x11111111]:
            cs = script.exports.callh(int(ts), url, header)
            print(f'0x{ts:08x}', url, header, cs)
        
        ts = 0x11111111
        header = 'everhu'
        for c in 'abcdef':
            url = c * 5
            cs = script.exports.callh(ts, url, header)
            print(f'0x{ts:08x}', url, header, cs)
    
        ts = 0x11111111
        url = 'hello'
        for c in 'abcdef':
            header = c * 6
            cs = script.exports.callh(ts, url, header)
            print(f'0x{ts:08x}', url, header, cs)
    
    ts url header cs
    0x000000ff hello everhu 028f 0b 00 5a f0 33 00 27 f0 77 6U 601d8960 77ac
    0x0000ff00 hello everhu 028f 0b 00 5a 0f 33 f0 27 00 77 6U dee8bd70 77ac
    0x00ff0000 hello everhu 028f 0b f0 5a 00 33 00 27 0f 77 6U 7cbbaebc 77ac
    0x01234567 hello everhu 028f 0b 31 5a 65 33 40 27 72 77 6U 12a566c1 77ac
    0x10325476 hello everhu 028f 0b 20 5a 74 33 51 27 63 77 6U 8446767b 77ac
    0x11111111 hello everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 0b64c40a 77ac
    0x11111111 aaaaa everhu 028f 0b 11 5a 11 33 11 27 11 77 6U a2a9352f 77ac
    0x11111111 bbbbb everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 776491c4 77ac
    0x11111111 ccccc everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 817b3dbe 77ac
    0x11111111 ddddd everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 247ea924 77ac
    0x11111111 eeeee everhu 028f 0b 11 5a 11 33 11 27 11 77 6U d4f29b3c 77ac
    0x11111111 fffff everhu 028f 0b 11 5a 11 33 11 27 11 77 6U cc70a6c4 77ac
    0x11111111 hello aaaaaa 028f 3f 11 be 11 86 11 60 11 77 6U 0b64c40a 77ac
    0x11111111 hello bbbbbb 028f d8 11 d7 11 80 11 24 11 77 6U 0b64c40a 77ac
    0x11111111 hello cccccc 028f 1c 11 a9 11 6b 11 b3 11 77 6U 0b64c40a 77ac
    0x11111111 hello dddddd 028f 6a 11 f5 11 99 11 73 11 77 6U 0b64c40a 77ac
    0x11111111 hello eeeeee 028f 0b 11 71 11 f2 11 cd 11 77 6U 0b64c40a 77ac
    0x11111111 hello ffffff 028f ef 11 fc 11 58 11 63 11 77 6U 0b64c40a 77ac

    记录如上表所示。

    image-20220120145028155

    在第一组中,当时间戳为0x111111110x01234567时,我们可以清晰地分辨出哪些位置是由时间戳构成的,是由时间戳的哪一位构成的。可以看出6-7, 10-11, 14-15, 18-19位都是由时间戳成的。此外,当时间戳变化时,第24-31位也在变化,说明此处有时间戳参与计算。

    在第二组中,时间戳和header都不变,当url变化时,第24-31位也在变化,说明此处有url参与计算,结合第一组得出,第24-31位有时间戳和url一起参与计算。

    在第三组中,时间戳和url都不变,当header变化时,cs的4-5, 8-9, 12-13, 16-17都在变化,说明这些位置是由header计算得出。

    其余位置无变化,可能与app版本或者手机环境本身相关,也有可能直接就是固定的。

    unidbg实现

    接下来是用unidbg来调用生成cs,先搭个框架。

    public class Soul extends AbstractJni {
        private final AndroidEmulator emulator;
        private final VM vm;
        private final Module module;
    
        public static String pkgName = "cn.soulapp.android";
        public static String apkPath = "unidbg-android/src/test/java/com/soul/soul3830.apk";
        public static String soPath = "";
    
        public Soul() {
            emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
            Memory memory = emulator.getMemory();
            memory.setLibraryResolver(new AndroidResolver(23));
            vm = emulator.createDalvikVM(new File(apkPath));
            vm.setJni(this);
            vm.setVerbose(true);
            DalvikModule dm = vm.loadLibrary("soulpower", true);
            module = dm.getModule();
            dm.callJNI_OnLoad(emulator);
        }
    
        public void call_h() {
            List<Object> list = new ArrayList<>(10);
            list.add(vm.getJNIEnv());
            list.add(0);
            list.add(vm.addLocalObject(vm.resolveClass("android/content/Context").newObject(null)));
            list.add(0x01234567);
            String url = "hello";
            list.add(vm.addLocalObject(new StringObject(vm, url)));
            String header = "everhu";
            list.add(vm.addLocalObject(new StringObject(vm, header)));
    
            Number ret = module.callFunction(emulator, 0xaa73c, list.toArray());
            System.out.println("ret h: " + vm.getObject(ret.intValue()).getValue().toString());
        }
    
        public static void main(String[] args) {
            Soul test = new Soul();
            System.out.println("=== Start call h");
            test.call_h();
        }
    }
    

    然后就是报错+补环境

    image-20220120150519066
    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "android/os/Build$VERSION->RELEASE:Ljava/lang/String;": {
                return new StringObject(vm, "7.1.2");
            }
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }
    
    image-20220120150651113
    // wrong case
    case "android/os/Build$VERSION->SDK:Ljava/lang/String;": {
        return new StringObject(vm, "23");
    }
    

    然后出现了让我无法理解的情况。

    image-20220120150848225

    这个报错让我不知道怎么补环境了。。开启全部日志之后,也不知道怎么找到问题。最终是一通乱拳,把"23"改成不存在的SDK版本(如"ff")才得以继续,这让我很不理解,有谁知道什么原因可以分享一下。

    case "android/os/Build$VERSION->SDK:Ljava/lang/String;": {
        return new StringObject(vm, "ff");
    }
    
    image-20220120151517512

    这个需要返回一个byte,那具体要返回多少呢?可以使用objection和hluwa/Wallbreaker来获取

    clone下来后,启动objection,然后加载插件

    plugin load E:\Wallbreaker
    

    然后dump整个类的实例

    plugin wallbreaker classdump cn.soulapp.andro id.soulpower.InfoGather
    
    image-20220120161513916

    所以应该返回119(实际上,随便一个值也能返回结果)。

    也可以查看android.os.Build $VERSION

    plugin wallbreaker classdump android.os.Build$VERSION
    
    image-20220120161717699
    @Override
    public byte getStaticByteField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "cn/soulapp/android/soulpower/InfoGather->aa:B": {
                return (byte) 119;
            }
        }
        return super.getStaticByteField(vm, dvmClass, signature);
    }
    
    image-20220120155318862

    注意0x77 = 119

    结果出来了,但是和hook到的结果不完全一样。幸运的是,不一样的部分是之前观察到的不变的部分,可能由于补的环境和正常环境不一样,所以这些部分和正常的不一样。不过和签名相关的部分是一样的,这样的话,我们只需要把不一样的部分改一下,应该就能用了?

    完整实现

    package com.soul;
    
    import com.github.unidbg.AndroidEmulator;
    import com.github.unidbg.Module;
    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.memory.Memory;
    
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    public class Soul extends AbstractJni {
        private final AndroidEmulator emulator;
        private final VM vm;
        private final Module module;
    
        public static String pkgName = "cn.soulapp.android";
        public static String apkPath = "unidbg-android/src/test/java/com/soul/soul3830.apk";
        public static String soPath = "";
    
        public Soul() {
            emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
            Memory memory = emulator.getMemory();
            memory.setLibraryResolver(new AndroidResolver(23));
            vm = emulator.createDalvikVM(new File(apkPath));
            vm.setJni(this);
            vm.setVerbose(true);
            DalvikModule dm = vm.loadLibrary("soulpower", true);
            module = dm.getModule();
            dm.callJNI_OnLoad(emulator);
        }
    
        @Override
        public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
            switch (signature) {
                case "android/os/Build$VERSION->RELEASE:Ljava/lang/String;": {
                    return new StringObject(vm, "7.1.2");
                }
                case "android/os/Build$VERSION->SDK:Ljava/lang/String;": {
                    return new StringObject(vm, "ff");
                }
            }
            return super.getStaticObjectField(vm, dvmClass, signature);
        }
    
        @Override
        public byte getStaticByteField(BaseVM vm, DvmClass dvmClass, String signature) {
            switch (signature) {
                case "cn/soulapp/android/soulpower/InfoGather->aa:B": {
                    return (byte) 119;
                }
            }
            return super.getStaticByteField(vm, dvmClass, signature);
        }
    
        public void call_h() {
            List<Object> list = new ArrayList<>(10);
            list.add(vm.getJNIEnv());
            list.add(0);
            list.add(vm.addLocalObject(vm.resolveClass("android/content/Context").newObject(null)));
            list.add(0x01234567);
            String url = "hello";
            list.add(vm.addLocalObject(new StringObject(vm, url)));
            String header = "everhu";
            list.add(vm.addLocalObject(new StringObject(vm, header)));
    
            Number ret = module.callFunction(emulator, 0xaa73c, list.toArray());
            System.out.println("ret h: " + vm.getObject(ret.intValue()).getValue().toString());
        }
    
        public static void main(String[] args) {
            Soul test = new Soul();
            System.out.println("=== Start call h");
            test.call_h();
        }
    }
    

    相关文章

      网友评论

          本文标题:soul cs字段unidbg实现

          本文链接:https://www.haomeiwen.com/subject/lhqshrtx.html