文章目录[隐藏]
android 逆向 25 / 38
- app OAuth api_sign 分析
- app sig 参数加密分析 unidbg 模拟黑盒调用
- app sign so 加密参数破解|unidbg
- sgmain x-sign 分析 - unidbg
- androidAsync fridaManager sgmain 70102 rpc 远程调用
- app edata 参数 so aes 加密分析破解|unidbg
- frida 加载 sekiro dex 文件 实现与服务端交互
- frida sekiro 实现 sgmain 70102 远程 rpc 调用
- xposed sekiro hook 获取 wx 万能 key
- unidbg console debugger 使用
- unidbg hook inline hook 使用
- app 公众号文章列表 so 加解密算法分析还原 | 简单分析
- app 公众号文章列表 so 加解密算法分析还原 | 加密 rsa base64 分析
- app 公众号文章列表 so 加解密算法分析还原 | 加密 zip aes 分析
- app 公众号文章列表 so 加解密算法分析还原 | response 内容解密分析
- app sign so 加密算法分析还原|简单分析
- app sign so 加密算法分析还原|so 算法分析
- app sign so 加密算法分析还原|so sub_126AC 函数算法还原
- app so signkeyV1 参数分析
- ida 动态调试 android so 文件|基础入门环境搭建
- app so newSign 参数分析破解
- app tzRgz52a 参数分析破解
- app sign-v2 签名算法 aes 加解密分析
- app so 加密参数分析|protocbuf 分析
- mxtakatak android app 加解密分析
- android app so 加密算法分析破解|mtgsig unidbg
- android app so 加密算法分析破解|siua unidbg
- android app nsign so 加密算法分析
- android app sig 参数 so 加密逻辑逆向分析
- android app so sig 加密参数 unidbg
- 狗狗音乐登陆协议加密参数逆向分析
- android sign so 加密参数分析|unidbg
- android app X-SS-QUERIES 参数分析
- unidbg android app xgorgon 加密参数 leviathan
- sgmain 6.4.x xsign 加密算法分析研究
- sgmain 6.4.x xminiwua 加密算法分析研究
- 某 app mas 算法分析还原 cms so
- 某东登陆协议 tlv 逻辑分析
本文只作学习研究,禁止用于非法用途,否则后果自负,如有侵权,请告知删除,谢谢!
前言
帮朋友看的一个 app,印度佬搞的 takatak 版本 1.18.10
链接 https://play.google.com/store/apps/details?id=com.next.innovation.takatak&hl=zh&gl=US
charles 抓包
主要分析 x-guard-key
的加密, response
的解密
java 分析
反编译全局搜索,但是没有结果,怎么办呢
根据请求头的 ua
可以猜测,用的 okhttp
发包的,熟悉 okhttp
的小伙伴可以使用 frida hook okhttp header
相关的函数,但是巧了,俺对 okhttp
不熟悉,无奈之后寻找其他关键词,搜索 url
评论接口,全局搜索有结果,那就好办了,打开 jeb
搜索该类
查看 o
的交叉引用
看不出来啥,点进 y.g
看看
这里的逻辑也比较简单,先看 y.m
了解 okhttp
的小伙伴大概都猜到了,这里就是构建 request
对象的逻辑,再来分析 y.i
b.a.a.c.i0.s
这个函数有点可疑,点进去看看
果然有猫腻,这里就是加密的逻辑,jeb
直接显示了字符串名称,不知道为啥 jadx
不行,离谱
-
x-guard-key
的值是rsa
加密 -
x-guard-value
的值是hmacsha1
加密
下面在分析分析 response
解密
回到这里,b.a.a.c.i0.s
是加密逻辑,我们直接看最后这个调用的函数
点进 y.d
函数,意外的看到下面函数有些 log
日志,抱着好奇心点进函数看一下
结果看到了 aes
解密,不管是不是这个逻辑,咱们先来 frida hook
一下相关函数看看
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;
}
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 i0Cls = Java.use('b.a.a.c.i0');
var hCls = Java.use('b.a.a.b.h');
var mac = Java.use('javax.crypto.Mac');
// ---------- request ----------
i0Cls.s.implementation = function (a, b, c, d, e) {
printLog('i0Cls.s.a: ', a);
printLog('i0Cls.s.b: ', b);
printLog('i0Cls.s.c: ', c);
printLog('i0Cls.s.c: ', JSON.stringify(getMapData(c)));
printLog('i0Cls.s.d: ', d);
printLog('i0Cls.s.e: ', e);
return this.s(a, b, c, d, e);
}
hCls.e0.implementation = function (a) {
printLog('hCls.e0.a: ', bytes2Hex(a));
var res = this.e0(a);
printLog('hCls.e0.res: ', res);
return res;
}
mac.doFinal.overload('[B').implementation = function (a) {
printLog('mac.doFinal.a: ', bytes2String(a));
printLog('mac.doFinal.a: ', bytes2Hex(a));
var res = this.doFinal(a);
printLog('mac.doFinal.res: ', bytes2Hex(res));
return res;
}
// ---------- response ----------
var MessageDigest = Java.use('java.security.MessageDigest');
var IvParameterSpec = Java.use("javax.crypto.spec.IvParameterSpec");
var Cipher = Java.use("javax.crypto.Cipher");
MessageDigest.digest.overload('[B').implementation = function (a) {
printLog('MessageDigest.digest.a: ', bytes2Hex(a));
var res = this.digest(a);
printLog('MessageDigest.digest.res: ', bytes2Hex(res));
return res;
}
IvParameterSpec.$init.overload('[B').implementation = function (a) {
printLog('IvParameterSpec.$init.a: ', bytes2Hex(a));
var res = this.$init(a);
printLog('IvParameterSpec.$init.res: ', bytes2Hex(res));
return res;
}
Cipher.doFinal.overload('[B', 'int', 'int').implementation = function (a, b, c) {
printLog('Cipher.doFinal.a: ', bytes2Hex(a));
printLog('Cipher.doFinal.b: ', b);
printLog('Cipher.doFinal.c: ', c);
var res = this.doFinal(a, b, c);
printLog('Cipher.doFinal.res: ', bytes2Hex(res));
return res;
}
}
function main() {
Java.perform(function () {
hook();
})
}
setImmediate(main);
全部代码,跑起来 hook
一下,记得同时抓个包,好验证一些数据
因为 rsa
有随机数,不好验证码,就直接去看 response
的解密逻辑,上图是抓包内容
这里是 frida hook
结果
-
1、aes key iv
-
2、response 响应数据
-
3、response 解密结果,根据特征可以判断出来是 gzip
使用 CyberChef
解密成功,aes
正是解密逻辑,至于 rsa
加密,还有 aes key iv
的生成逻辑,大家可以自行分析一下
最后
这个样本还是比较简单,没有 so 没有混淆,定位到之后可以很方便的还原出来