kuaidui作业unidbg逆向(2)

kuaidui作业unidbg逆向

nativeInitBaseUtil

在unidbg实现中,调用nativeGetSign之前,需要先调用nativeSetToken,这是因为需要先设置objSpamServer.random_number

image-20220126151954449
image-20220126152038418

nativeSetToken的入参又来自于nativeInitBaseUtil

kuaidui

先分析代码比较少的nativeInitBaseUtil

image-20220126152555193

看看getChallenge函数

image-20220126153147089

很简单,生成随机10位字符串

剩下的函数就比较容易明白它的作用。CRYMd5生成MD5,CRYStringCat字符串拼接,DES_Encryptdes加密,str2hex将输入转为16进制字符串。现在需要分析的其实只有后面两个函数。

unidbg实现

先用unidbg调用nativeInitBaseUtil,之后再结合ida来逆向

public class Kuaidui extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    public static String pkgName = "com.kuaiduizuoye.scan";
    public static String apkPath = "unidbg-android/src/test/java/com/kuaidui/kuaidui540.apk";
    public static String soPath = "";

    public Kuaidui() {
        emulator = AndroidEmulatorBuilder.for64Bit().setProcessName(pkgName).build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        vm.setJni(this);
        vm.setVerbose(true);
        new AndroidModule(emulator, vm).register(memory);
        DalvikModule dm = vm.loadLibrary("baseutil", true);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
    }

    @Override
    public int getStaticIntField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "android/content/pm/PackageManager->GET_SIGNATURES:I": {
                return 64;
            }
        }
        return super.getStaticIntField(vm, dvmClass, signature);
    }

    public void call_init() {
        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(vm.addLocalObject(new StringObject(vm, "F5D53AD5A66144B57783C7C67611F0F7|0")));
        Number ret = module.callFunction(emulator, 0x1010, list.toArray());
    }

    public static void main(String[] args) {
        Kuaidui test = new Kuaidui();
        test.call_init();
    }
image-20220126153904882

报了个错,跳转过去看看

image-20220126153935089

好像不支持,我们简单的把它加上去试试

image-20220126154052050
image-20220126170756492

出结果了,不过有一点不好的就是,每次运行生成的结果都不一样。这是因为getChallenge函数里面的rand函数导致的。将它hook住,改为固定值。

public void hook_libc() {
    IHookZz hookZz = HookZz.getInstance(emulator);
    hookZz.enable_arm_arm64_b_branch();
    hookZz.wrap(module.findSymbolByName("rand"), new WrapCallback<HookZzArm64RegisterContext>() {
        @Override
        public void preCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
        }

        @Override
        public void postCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
            ctx.setXLong(0, 1L);
        }
    });
}
public static void main(String[] args) {
    Kuaidui test = new Kuaidui();
    test.hook_libc();
    test.call_init();
}

由于随机数的结果一直为1,所以getChallenge的结果为BBBBBBBBBB

逆向

接下来开始逆向DES_Encryptstr2hex

image-20220126154835129

hook看看参数

emulator.attach().addBreakPoint(module.base+0x61f4);
image-20220126154937169
image-20220126155041419

很容易构造出。

blr在函数返回处下断点,c运行到函数返回处,mx0看看返回结果

image-20220126155539906
image-20220126155605178

有了输入,key和输出,在cyberchef验证下

image-20220126155837910

emm,和标准结果差的不是一点半点,有可能是DES-CBC,或者DES经过了魔改。

image-20220126160111449

这里是实现块加密的代码,可以看出就是DES-ECB,没有向量或其他东西。那说明DES很可能经过了魔改。

image-20220126160805050

这部分是密钥生成部分,涉及到的常量有PC_1, MOVE_TIMES, PC_2

image-20220126161310755

比较容易看到的修改就是PC_2有个值被改了。

还有就是块加密部分的常量

image-20220126170237660
image-20220126170203044

实际操作上,直接把这些常量全部拷贝下来就行了,根本不用对比,免得自己看漏了。

image-20220126161829920
image-20220126162404458

另外一个就是byte转bit的时候高位低位是相反的,比如0x8d在标准实现中是10001101,这里则是11010001

在标准DES实现的基础上做出修改后,再调用试试

image-20220126162654331

和unidbg hook到的结果一致。

接下来就是str2hex函数。

image-20220126163012195
image-20220126163041739

str2hex的输入就是DES_Encrypt的结果,输出就是最后的结果。

image-20220126163255158

其实对照着代码也挺容易实现的,就是把一个byte转成bit之后再逆序,然后再转成2个byte,最后变成hex形式。

def byte2hex(data):
    """
        8d
        |
    10001101
        |
    10110001
    /    \
    1011 0001
    |    |
    0b   01
    """
    result = []
    for v9 in data:
        d = f'{v9:08b}'[::-1]
        result.append(int(d[:4], 2))
        result.append(int(d[4:], 2))
    return bytes(result).hex()

把前面的拼接起来验证下

def calc_request_hex(challenge, devid, pkg_sign='2fb53de6d38eff7109f19d68e047123b'):
    """
    Args:
        challenge: length-of-10 random string, charset: a-zA-Z0-9
        devid: cuid
        pkg_sign: md5 of pkg signature, default: sign of v5.4.0
    """
    data = '##'.join(('8&%d*', challenge, pkg_sign, devid))
    data2 = encrypt_hex(data.encode(), b'@fG2SuLA')
    return data2

def encrypt_hex(data, key):
    cryptor = DES(key, padmode=PAD_PKCS5)
    data2 = cryptor.encrypt(data)
    data3 = byte2hex(data2)
    return data3

def test_request_hex():
    challenge = 'B' * 10
    devid = 'F5D53AD5A66144B57783C7C67611F0F7|0'
    req_str = calc_request_hex(challenge, devid)
    print(req_str)
image-20220126164022514

和unidbg结果一致。

nativeSetToken

nativeSetTokennativeInitBaseUtil相比是负责解密数据。

image-20220126164334084

其实主要也就这几个地方。

image-20220126164414998

hex2Str就是str2hex的反向操作,重写一下即可。

def hex2byte(data):
    """
    0b   01
    |    | 
    1011 0001
    \    /
    10110001
        |
    10001101
        |
        8d
    """
    data2 = bytes.fromhex(data)
    result = []
    for idx in range(0, len(data2), 2):
        d = f'{data2[idx]:04b}{data2[idx+1]:04b}'
        result.append(int(d[::-1], 2))
    return bytes(result)

接下来用hook到的真实数据试试。

先将请求数据解密看看

def decrypt_hex(data, key):
    data2 = hex2byte(data)
    cryptor = DES(key, padmode=PAD_PKCS5)
    data3 = cryptor.decrypt(data2)
    return data3

def test_decode2():
    key = b'@fG2SuLA'
    data = '03090b0106000807080e00080201090708060f00040700020d0503070c0606000c01000a080e07050808020d00060b0d070f0f080306060b00000f0d04070d0d02070b0f020a01010c0d030a09080e090b040f02080c0b040c0e0e060c0d0201020f0e02030d0107020d000e0e02090401070505030a0c0605080303040e0803020c020e0d020408010b030e0b090f060302000e0f0902010706040c00080d0e060d000f0805040b0e07000d020b0f07'
    req_str = decrypt_hex(data, key)
    print(req_str)
image-20220126165057321

可以看到和nativeInitBaseUtil中的形式是一样的。

然后是响应数据解密,它的key是原始请求数据的7-12位加上#G4,也就是wobBX#G4

def test_decode():
    key = b'wobBX#G4'
    data = '0a040609080d020b0a03090e010306050d0d050c0c060207070105010b0b01090801000a020d0c0b03030209030e0d0a'
    resp_str = decrypt_hex(data, key)
    print(resp_str)
image-20220126165540276

objSpamServer.random_number映入眼帘。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容