跳转至

头条 app mas 参数说明

本文介绍今日头条 appv7.4.6 版本发包请求中的 mas 参数的生成方式,使用 unidbg 实现其结果。

前言

此文档描述不很详细,是因为使用 unidbg 实现此场景太简单了,可以转身去看看 unidbg 的文档。

1. 前提准备

本文主要是使用 unidbg 来生成 mas 参数,所需的工具不多。

今日头条 app mas 参数逆向前准备如下:

  • root 后的安卓手机一台,本文使用的为一加 7 pro
  • jeb 或者 ida 等反编译调试工具都可以
  • jadx 工具,版本无限制
  • frida v15.1.14,版本无限制
  • 今日头条 v7.4.6
  • HttpCanaryFiddler 等用于抓包

2. 操作步骤

2.1. 抓包

使用 HttpCanary 抓目标请求包,由下图可知请求需要 mas 参数

jrtt2

2.2. 查包信息

由下图可知此版本头条没有加壳,则可直接 jadx 走起。

jrtt1

2.3. jadx 定位参数生成位置

本文使用的是 gda 来反编译解析的,工具不限制。由下图可知,mas 参数由 encode 方法生成的结果再经过 toHexString 方法而得。encode 中的参数是由 getUserInfo 方法生成。

jrtt3

接下来进入 encode 方法,可知它为 libcms.so 中的方法。

jrtt4

也进入 getUserInfo 方法中看一看,可知它也为 libcms.so 中的方法。

jrtt5

2.4. frida hook 目标函数

接下来 hookencodegetUserInfo 两个目标函数,看看其参数格式,最好和抓包一起开启,比较好对照。

setImmediate(function () {

    // byte 转 string 方法
    function byteToString(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);
                }
                str += String.fromCharCode(parseInt(store, 2));
                i += bytesLength - 1;
            } else {
                str += String.fromCharCode(_arr[i]);
            }
        }
        return str;
    };

    // string 转 byte 方法
    function stringToByte(str) {
        var bytes = new Array();
        var len, c;
        len = str.length;
        for (var i = 0; i < len; i++) {
            c = str.charCodeAt(i);
            if (c >= 0x010000 && c <= 0x10FFFF) {
                bytes.push(((c >> 18) & 0x07) | 0xF0);
                bytes.push(((c >> 12) & 0x3F) | 0x80);
                bytes.push(((c >> 6) & 0x3F) | 0x80);
                bytes.push((c & 0x3F) | 0x80);
            } else if (c >= 0x000800 && c <= 0x00FFFF) {
                bytes.push(((c >> 12) & 0x0F) | 0xE0);
                bytes.push(((c >> 6) & 0x3F) | 0x80);
                bytes.push((c & 0x3F) | 0x80);
            } else if (c >= 0x000080 && c <= 0x0007FF) {
                bytes.push(((c >> 6) & 0x1F) | 0xC0);
                bytes.push((c & 0x3F) | 0x80);
            } else {
                bytes.push(c & 0xFF);
            }
        }
        return bytes;
    };

    // 转 hexString 方法
    function toHexString(byteArray) {
        return Array.from(byteArray, function (byte) {
            return ('0' + (byte & 0xFF).toString(16)).slice(-2);
        }).join('');
    }

    // bytes 转 string 方法
    function bytesToString(arr) {
        var str = '';
        arr = new Uint8Array(arr);
        for (var i in arr) {
            str += String.fromCharCode(arr[i]);
        }
        return str;
    }

    // hook encode 方法
    Java.perform(function () {
        var targetClass = 'com.ss.sys.ces.a';
        var methodName = 'encode';
        var gclass = Java.use(targetClass);
        gclass[methodName].overload(decodeURIComponent('[B')).implementation = function (arg0) {
            console.log('\n[Hook encode([B)]' + '\n\targ0_real = ' + arg0);
            console.log('\ttarg0_String = ' + byteToString(arg0));

            var i = this[methodName](arg0);
            console.log("\tencode_result_String = " + byteToString(i));
            console.log("\tencode_result_Hex = " + toHexString(i));
            console.log('\treturn ' + i);
            return i;
        };

    });

    // hook getUserInfo 方法
    Java.perform(function () {
        var targetClass = 'com.ss.android.common.applog.UserInfo';
        var methodName = 'getUserInfo';
        var gclass = Java.use(targetClass);
        gclass[methodName].overload('int', 'java.lang.String', '[Ljava.lang.String;', 'java.lang.String').implementation = function (arg0, arg1, arg2, arg3) {
            console.log('\n[Hook getUserInfo(int,java.lang.String,%5bLjava.lang.String;,java.lang.String)]' + '\n\targ0 = ' + arg0 + '\n\targ1 = ' + arg1 + '\n\targ2 = ' + arg2 + '\n\targ3 = ' + arg3);
            var i = this[methodName](arg0, arg1, arg2, arg3);
            console.log('\treturn ' + i);
            // console.log('ffffff_ = ' + stringToByte(i));
            return i;
        };
    });

});

以上的 hook 代码运行后的结果如下:

jrtt6

上面的参数及运行步骤就很明了啦,就不再啰嗦。

2.5. unidbg 运行目标函数

由于太简单了,就不再贴代码了。直接使用 unidbg 的官方文档中的示例改改就行了。

jrtt6

3. 总结

这个 app 使用 unidbg 比较容易就可以生成结果。这里补上 qin 大佬的解析参考文章.

评论

回到页面顶部