android app X-SS-QUERIES 参数分析

android 逆向 34 / 37

前言

帮朋友看的一个样本,版本比较老,逻辑也不复杂,这里就简单分享下 so 算法的还原思路
样本下载: https://www.wandoujia.com/apps/35609/history_v7600

charles 抓包

就是这个参数

java 层分析

全局搜索 X-SS-QUERIES 关键词,定位到这里

加密结果是调用的 TTEncryptUtils.a 这个函数获取的

这里调用了 ttEncrypt native 函数,在 ttEncrypt.so 文件里。嗯 java 逻辑就是这么简单

frida hook

function getMapData(mapSet) {
    try {
        var result = {};
        var key_set = mapSet.keySet();
        var it = key_set.iterator();
        while (it.hasNext()) {
            var key_str = it.next().toString();
            result[key_str] = mapSet.get(key_str).toString();
        }
        return result
    } catch (error) {
        return mapSet
    }
}

function bytes2Hex(arrBytes) {
    var str = "";
    for (var i = 0; i < arrBytes.length; i++) {
        var tmp;
        var num = arrBytes[i];
        if (num < 0) {
            tmp = (255 + num + 1).toString(16);
        } else {
            tmp = num.toString(16);
        }
        if (tmp.length === 1) {
            tmp = "0" + tmp;
        }
        if (i > 0) {
            str += " " + tmp;
        } else {
            str += tmp;
        }
    }
    return str.replace(' ', '');
}

function bytes2String(arr) {
    if (typeof arr === 'string') {
        return arr;
    }
    var str = '',
        _arr = arr;
    for (var i = 0; i < _arr.length; i++) {
        var one = _arr[i].toString(2),
            v = one.match(/^1+?(?=0)/);
        if (v && one.length === 8) {
            var bytesLength = v[0].length;
            var store = _arr[i].toString(2).slice(7 - bytesLength);
            for (var st = 1; st < bytesLength; st++) {
                store += _arr[st + i].toString(2).slice(2);
            }
            try {
                str += String.fromCharCode(parseInt(store, 2));
            } catch (error) {
                str += parseInt(store, 2).toString();
                console.log(error);
            }

            i += bytesLength - 1;
        } else {
            try {
                str += String.fromCharCode(_arr[i]);
            } catch (error) {
                str += parseInt(store, 2).toString();
                console.log(error);
            }

        }
    }
    return str;
}

function getProcessId() {
    var androidProcess = Java.use('android.os.Process');
    return 'processId: ' + androidProcess.myTid() + ' - ';
}

function printLog() {
    var result = '';

    for (var i = 0; i < arguments.length; i++) {
        result += arguments[i] + ' ';
    }

    console.log(getProcessId(), result)
}

function hook() {
    var TTEncryptUtils = Java.use('com.bytedance.frameworks.core.encrypt.TTEncryptUtils');

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

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

        return res;
    }
}

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

setImmediate(main);

输入输出都 hook 出来了

unidbg

package com.xiayu.jinritoutiao;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.context.Arm32RegisterContext;
import com.github.unidbg.debugger.Debugger;
import com.github.unidbg.debugger.DebuggerType;
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.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.utils.Inspector;
import com.xiayu.XiayuUtils;
import jdk.nashorn.internal.runtime.Debug;
import unicorn.ArmConst;

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

public class TTEncrypt extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final DvmClass TTEncryptUtils;

    public String apkPath = "/Volumes/T7/android/android-file/jinritoutiao-7.0.6.apk";

    TTEncrypt() {
        emulator = AndroidEmulatorBuilder.for32Bit().build();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        DalvikModule dm = vm.loadLibrary("ttEncrypt", true);
        module = dm.getModule();
        vm.setVerbose(true);
        vm.setJni(this);
        dm.callJNI_OnLoad(emulator);

        TTEncryptUtils = vm.resolveClass("com/bytedance/frameworks/core/encrypt/TTEncryptUtils");
    }

    public void callFunction() {
        String input = "";

        ByteArray array = TTEncryptUtils.callStaticJniMethodObject(
                emulator, "ttEncrypt([BI)[B",
                new ByteArray(vm, input.getBytes()),
                input.length()
        );

        System.out.println(XiayuUtils.bytesToHexString(input.getBytes()));
        System.out.println(XiayuUtils.bytesToHexString(array.getValue()));
    }

    public static void main(String[] args) {
        TTEncrypt ttEncrypt = new TTEncrypt();
        ttEncrypt.callFunction();
        ttEncrypt.destroy();
    }

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

unidbg 也是没啥需要补的环境,直接就能跑出结果出来

so 层分析

这里是静态注册的函数,主要关注这两个函数 initKey, ss_encrypt

initKey 函数,静态初始化一些数据

ss_encrypt 函数,逻辑也比较简单,但是有两个 S

了解 AES 算法的小伙伴一眼就能看出来,这是 S 盒跟逆 S 盒

  • 但是看代码逻辑,很明显不是 aes 就很奇怪也看不出来是啥,简化版的 aes S-AESS 盒也不对,有了解的巨佬还望指点一下。这里因为代码逻辑也不是很复杂就直接参照着还原了

so 还原思路

  • 我这个只用了一个 S 盒,先定义 S, Key

  • 这里的 v22 = a3 = keyS0 进行置换

  • 这里是获取一些水,用于下面的计算

  • 这里是明文跟 S0 进行置换

  • 这里就是最后的计算逻辑了

最后

逻辑不是很复杂,最后也是成功还原了

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

2 条评论

我没有名字

你咋这么厉害呢

回复

会爬山的小脑虎

@我没有名字 巨佬怎么说

回复

发表评论

返回顶部