soul cs字段unidbg實現(xiàn)

soul cs字段unidbg實現(xiàn)

環(huán)境

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

可以看到,第二個參數(shù)是時間戳,第三個參數(shù)url,第四個參數(shù)是headers里面的參數(shù)拼接起來的。這個的簽名長度是36,是一個比較不常見的數(shù)字,對比了幾個簽名,發(fā)現(xiàn)都是028f**77ac的形式(實際上,在另一臺手機中,cs是02af**008c的形式),接下來研究一下cs的具體構(gòu)成。

通過frida,我們自己控制傳入的參數(shù),看看輸出是怎樣的。

// 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時,我們可以清晰地分辨出哪些位置是由時間戳構(gòu)成的,是由時間戳的哪一位構(gòu)成的??梢钥闯?code>6-7, 10-11, 14-15, 18-19位都是由時間戳成的。此外,當時間戳變化時,第24-31位也在變化,說明此處有時間戳參與計算。

在第二組中,時間戳和header都不變,當url變化時,第24-31位也在變化,說明此處有url參與計算,結(jié)合第一組得出,第24-31位有時間戳和url一起參與計算。

在第三組中,時間戳和url都不變,當header變化時,cs的4-5, 8-9, 12-13, 16-17都在變化,說明這些位置是由header計算得出。

其余位置無變化,可能與app版本或者手機環(huán)境本身相關(guān),也有可能直接就是固定的。

unidbg實現(xiàn)

接下來是用unidbg來調(diào)用生成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();
    }
}

然后就是報錯+補環(huán)境

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");
}

然后出現(xiàn)了讓我無法理解的情況。

image-20220120150848225

這個報錯讓我不知道怎么補環(huán)境了。。開啟全部日志之后,也不知道怎么找到問題。最終是一通亂拳,把"23"改成不存在的SDK版本(如"ff")才得以繼續(xù),這讓我很不理解,有誰知道什么原因可以分享一下。

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(實際上,隨便一個值也能返回結(jié)果)。

也可以查看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

結(jié)果出來了,但是和hook到的結(jié)果不完全一樣。幸運的是,不一樣的部分是之前觀察到的不變的部分,可能由于補的環(huán)境和正常環(huán)境不一樣,所以這些部分和正常的不一樣。不過和簽名相關(guān)的部分是一樣的,這樣的話,我們只需要把不一樣的部分改一下,應該就能用了?

完整實現(xiàn)

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();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容