unidbg android app xgorgon 加密参数 leviathan

android 逆向 34 / 37

前言

libmcs leviathan 函数获取 xg 加密函数
app 版本: 11.5.0。unidbg 版本:0.9.7

Tips

本文内容参考了,龙哥星球的 td 案例

java 层就不分析了直接开始今天的主题,native 函数在 com.ss.sys.ces.a

frida hook

function hook() {
    var aCls = Java.use('com.ss.sys.ces.a');

    aCls.leviathan.implementation = function (a, b, c) {
        printLog('aCls.leviathan.a: ', a);
        printLog('aCls.leviathan.b: ', b);
        printLog('aCls.leviathan.c: ', c);
        printLog('aCls.leviathan.c: ', bytes2Hex(c));

        var res = this.leviathan(a, b, c);
        printLog('aCls.leviathan.res: ', res);
        printLog('aCls.leviathan.res: ', bytes2Hex(res));

        return res;
    }

    aCls.meta.implementation = function (a, b, c) {
        printLog('aCls.meta.a: ', a);
        printLog('aCls.meta.b: ', b);
        printLog('aCls.meta.c: ', c);

        var res = this.meta(a, b, c);
        printLog('aCls.meta.res: ', res);

        return res;
    }

    aCls.decode.implementation = function (a, b) {
        printLog('aCls.decode.a: ', a);
        printLog('aCls.decode.b: ', b);
        printLog('aCls.decode.b: ', bytes2Hex(b));

        var res = this.decode(a, b);
        printLog('aCls.decode.res: ', res);
        printLog('aCls.decode.res: ', bytes2Hex(res));

        return res;
    }

    aCls.encode.implementation = function (a) {
        printLog('aCls.encode.a: ', a);
        printLog('aCls.encode.a: ', bytes2Hex(a));

        var res = this.encode(a);
        printLog('aCls.encode.res: ', res);
        printLog('aCls.encode.res: ', bytes2Hex(res));

        return res;
    }
}

function main() {
    Java.perform(function () {
        hook();
    })
}

setImmediate(main);

清楚 dy app 全部数据,使用 frida spwan 模式启动

unidbg

run unidbg

package com.qinless.douyin.v1150;

import com.androidFrameWork.*;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.linux.android.AndroidARMEmulator;
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 com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.unix.UnixSyscallHandler;
import com.github.unidbg.virtualmodule.android.AndroidModule;

import java.io.File;
import java.io.IOException;

public class LibCmsTest extends AbstractJni implements IOResolver<AndroidFileIO> {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    public DvmClass AClass;

    public String apkPath = "/Volumes/T7/android/android-file/douyin-11.5.0.apk";

    public LibCmsTest() {
        AndroidEmulatorBuilder builder = new AndroidEmulatorBuilder(false) {
            @Override
            public AndroidEmulator build() {
                return new AndroidARMEmulator(processName, rootDir, backendFactories) {
                    @Override
                    protected UnixSyscallHandler<AndroidFileIO> createSyscallHandler(SvcMemory svcMemory) {
                        return new DyARM32SyscallHandler(svcMemory);
                    }
                };
            }
        };

        emulator = builder.setRootDir(new File("target/douyin-1150-test")).build();

        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        emulator.getSyscallHandler().addIOResolver(this);
        vm = emulator.createDalvikVM(new File(apkPath));
        vm.setVerbose(true);
        memory.addModuleListener(new hookImports(emulator, "libcms.so", "unidbg-android/src/test/resources/douyin/files/imports.txt"));
        new AndroidModule(emulator, vm).register(memory);
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);

        DalvikModule dm = vm.loadLibrary("cms", true);

        vm.setJni(this);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);

        AClass = vm.resolveClass("com/ss/sys/ces/a");
    }

    public static void main(String[] args) {
        LibCmsTest libCms = new LibCmsTest();
        libCms.destroy();
    }

    private void destroy() {
        try {
            emulator.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public FileResult<AndroidFileIO> resolve(Emulator<AndroidFileIO> emulator, String pathname, int oflags) {
        System.out.println("pathname: " + pathname);
        return null;
    }
}

老规矩架子先搭建起来

这里使用了自定义的 SyscallHandler 主要是处理了下 unidbg 未实现/实现不完善的函数(代码来自龙哥星球 td 案例)

还有 hook patch 逻辑(代码来自龙哥星球 td 案例)
跑起来

第一次运行发现程序卡住了,这时不用管,咱们先把需要的文件给补起来 /dev/__properties__, /proc/stat 这两个文件也是需要补的,直接复制手机里的即可

补完之后就可正常运行了

然后开始调用 meta native 函数,这是我 frida hook 的结果,需要调用

call meta

public void callMeta() {
    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 102),
            vm.resolveClass("android/content/Context").newObject(null),
            vm.resolveClass("java/lang/Object").newObject(1128)
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 101),
            null,
            vm.resolveClass("java/lang/Object").newObject(255)
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 1020),
            null,
            vm.resolveClass("java/lang/Object").newObject("")
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 105),
            null,
            vm.resolveClass("java/lang/Object").newObject(110501)
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 106),
            null,
            vm.resolveClass("java/lang/Object").newObject("com.ss.android.ugc.aweme")
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 107),
            null,
            vm.resolveClass("java/lang/Object").newObject("com.ss.android.ugc.aweme")
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 108),
            null,
            vm.resolveClass("java/lang/Object").newObject("/data/app/com.ss.android.ugc.aweme/base.apk")
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 109),
            null,
            vm.resolveClass("java/lang/Object").newObject("/storage/emulated/0")
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 110),
            null,
            vm.resolveClass("java/lang/Object").newObject("/data")
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 101),
            null,
            vm.resolveClass("java/lang/Object").newObject(0)
    );

    AClass.callStaticJniMethodObject(
            emulator, "meta(ILandroid/content/Context;Ljava/lang/Object;)Ljava/lang/Object;",
            DvmInteger.valueOf(vm, 100),
            vm.resolveClass("android/content/Context").newObject(null),
            vm.resolveClass("java/lang/Object").newObject(294)
    );
}

然后运行,在补两个环境即可

call leviathan

然后开始调用 leviathan native 函数

public void callLeviathan() {
    DvmObject<?> ret = AClass.callStaticJniMethodObject(
            emulator, "leviathan(II[B)[B",
            DvmInteger.valueOf(vm, -1),
            DvmInteger.valueOf(vm, 1647670270),
            new ByteArray(vm, QLUtils.hexToByteArray("3270d9629da3616f34db087c591628a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))
    );

    System.out.println(QLUtils.bytesToHexString((byte[]) ret.getValue()));
}

跑起来,然后就开始正常补环境即可

中间会遇到这个问题,不要慌,往上翻

可以看到里有读取文件夹,但是我们并没有处理这个所以报错(该问题也是请教了大佬)把这个补上即可。当然其他的读取文件也都一并补一下

这里一共补了四个

/data/data/com.ss.android.ugc.aweme/files/assets.czl 这个也不知道干啥的,不补就不行很奇怪,补上之后在运行就成功了,继续补 jni 环境

遇到这个错误时重载 java/io/File->getAbsolutePath()Ljava/lang/String; 函数返回正确的数据就行了

最后补完之后结果也是出来(博主没有测试过是否可用)

1 条评论
本文作者:
本文链接: https://www.qinless.com/?p=1631
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 qinless 的博客!
100

1 条评论

nick

什么是 QLContext? 你可以分享它的实现吗?

回复

发表评论

返回顶部