头条 app mas 参数说明
本文介绍今日头条 app
的 v7.4.6
版本发包请求中的 mas
参数的生成方式,使用 unidbg
实现其结果。
前言
此文档描述不很详细,是因为使用 unidbg
实现此场景太简单了,可以转身去看看 unidbg
的文档。
1. 前提准备
本文主要是使用 unidbg
来生成 mas
参数,所需的工具不多。
今日头条 app mas
参数逆向前准备如下:
root
后的安卓手机一台,本文使用的为一加 7 pro
jeb
或者 ida
等反编译调试工具都可以 jadx
工具,版本无限制 frida v15.1.14
,版本无限制 - 今日头条
v7.4.6
HttpCanary
或 Fiddler
等用于抓包
2. 操作步骤
2.1. 抓包
使用 HttpCanary
抓目标请求包,由下图可知请求需要 mas
参数
2.2. 查包信息
由下图可知此版本头条没有加壳,则可直接 jadx
走起。
2.3. jadx 定位参数生成位置
本文使用的是 gda
来反编译解析的,工具不限制。由下图可知,mas
参数由 encode
方法生成的结果再经过 toHexString
方法而得。encode
中的参数是由 getUserInfo
方法生成。
接下来进入 encode
方法,可知它为 libcms.so
中的方法。
也进入 getUserInfo
方法中看一看,可知它也为 libcms.so
中的方法。
2.4. frida hook 目标函数
接下来 hook
这 encode
和 getUserInfo
两个目标函数,看看其参数格式,最好和抓包一起开启,比较好对照。
| 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
代码运行后的结果如下:
上面的参数及运行步骤就很明了啦,就不再啰嗦。
2.5. unidbg 运行目标函数
由于太简单了,就不再贴代码了。直接使用 unidbg
的官方文档中的示例改改就行了。
3. 总结
这个 app
使用 unidbg
比较容易就可以生成结果。这里补上 qin
大佬的解析参考文章.
- 参考文章:
qin
佬算法实现参考文章 - 至于如何运行
unidbg
脚本,其它文章已说过。