Nic*app的native层算法破解
Nic*app的native层算法破解
抓包分析
这里抓包分析的过程不详细说了 可以看到主要有两个参数 sign和signV1
反编译
1、 sign破解
搜索关键词定位以及跳转到声明之后,最后就是一个md5,用objection看看就完事了
这是java层的sign破解,完事。
2、*-sign-v1破解
这个算法主要是在native层,这里借助了frida进行破解,最后换成cpp代码。这里定位到这个,刚开始我也不知道这里,就瞎找,分析到这里。
最后确定到这两个方法,用objection确定下,最后native走的那个方法
可以看到最后走的是getSignRequest这个方法,并且根据上图,就可以看到so是libsalt.so。
2.1 分析so
- 导入jni头文件,这里没有jni_onload,就是静态注册的。在
Java_com_*_main_helpers_utils_*SignUtils_getSignRequest
找到一个加密的函数,追下去看看。
- 看到这么多函数,根据名字就猜测一下了,md5操作,看看是不是导出函数。
果然是导出函数,直接frida就去Hook一下导出函数。
Interceptor.attach(Module.findExportByName("libsalt.so", "*_md5"), {
onEnter: function (args) {
console.log("*_md5 onEnter", Memory.readCString(args[0]))
},
onLeave: function (retvalue) {
console.log("*_md5 onLeave", Memory.readCString(retvalue))
}
})
Hook之后得到这么多
其实a4698cf0eea7a9b92a0194618079aba9是did2a0194618079aba9a4698cf0eea7a9b9
中间进行切割,然后交换位置。5nhrec75lf3drenb09a349366a5b7eda4ee99d7a104fb38b8a5f746c1c9c99c0b458e1ed510845e5 是随机字符串+md5(a4698cf0eea7a9b92a0194618079aba9)+8a5f746c1c9c99c0b458e1ed510845e5的结果8a5f746c1c9c99c0b458e1ed510845e5
是固定值,随机字符串是java层算法。python这一部分的实现
did = '2a0194618079aba9a4698cf0eea7a9b9'
rstr = 'yqkdfs6y4d2ci8wm' # 随机字did[:符串
new_did = did[len(did) // 2:] + len(did) // 2]
did_md5 = get_md5(new_did)
sss = rstr + did_md5 + '8a5f746c1c9c99c0b458e1ed510845e5'
dest = get_md5(sss)
cpp实现:
string did = "2a0194618079aba9a4698cf0eea7a9b9";
string ranstr = "28npqz4cpmw6ifpc";
string new_did = getStringSubs(did);
string did_md5 = getMd5(new_did);
string sss = ranstr + did_md5 + "8a5f746c1c9c99c0b458e1ed510845e5";
string dest = getStringSubs(getMd5(sss));
上边的dest是新字符串拼接需要的
这里有一个json的操作,其实刚开始我也不知道干了什么,最后我用frida-trac 就轻松解决了。
frida-trace -FU -i "strlen"
array[i] = buffer[2 * i] & 0xF0 | buffer[2 * i + 1] & 0xF;
这个是对字符串进行了高低位的操作,也就是这个app的signV3唯一个非标准算法。拿到高低位操作之后的字符串之后,拼接了之前的dest(我自己写的那个),然后进行sha1操作。sha1之后取了第8位到最后的字符串,然后又中间进行切割,然后交换位置,最后生成的就是signV3的结果。cpp最后实现的算法:
string getMd5(const string &message) {
return MD5(message).toStr();
}
string getSha1(const string &message) {
SHA1 checksum;
checksum.update(message);
const string hash = checksum.final();
return hash;
}
string getStringSubs(const string &message) {
string string1 = message.substr(0, message.length() / 2);
string string2 = message.substr(message.length() / 2);
return string2 + string1;
}
void SignV3() {
string did = "2a0194618079aba9a4698cf0eea7a9b9";
string ranstr = "28npqz4cpmw6ifpc";
string new_did = getStringSubs(did);
string did_md5 = getMd5(new_did);
string sss = ranstr + did_md5 + "8a5f746c1c9c99c0b458e1ed510845e5";
string dest = getStringSubs(getMd5(sss));
string arr = "aid=fbef33334c2388ccee5757dc2dbb0b45&comments_sort=asc&isnewsession=false&mark_read_sid=361595316249559936&mcc=&mnc=&nextkey=×tamp=1592463682&ua=Mozilla/5.0 (Linux; Android 8.1.0; AOSP on msm8996 Build/OPM4.171019.021.D1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36";
const char *buffer = "aid=fbef33334c2388ccee5757dc2dbb0b45&comments_sort=asc&isnewsession=false&mark_read_sid=361595316249559936&mcc=&mnc=&nextkey=×tamp=1592463682&ua=Mozilla/5.0 (Linux; Android 8.1.0; AOSP on msm8996 Build/OPM4.171019.021.D1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36";
int i = 0;
//这里的算法可以看看ida的图 自己实现呐
string new_sss = arr.substr(0, i) + dest;
string sha1Res = getSha1(new_sss).substr(8);
string sign_v3 = getStringSubs(sha1Res);
cout << "_sign_v3=" << sign_v3;
}
int main() {
SignV3();
return 0;
}
算法检验
至于为什么最后不放python算法,实在是因为,还原成python之后,高低位操作的时候,部分数据不对了。就只有cpp了
更多推荐
- 多线程编程学习三(线程间通信).
- 关于create database语句在10g,11g中的不同(r5笔记第88天)
- Web开发模式【Mode I 和Mode II的介绍、应用案例】
- 多线程编程学习四(Lock 的使用)
- Android编程规范
- 干货 | 深入分析Object.wait/notify实现机制
- 关于ORA-01555的问题分析(r5笔记第87天)
- 项目工具类
- AJAX常见面试题
- 干货 | Tomcat类加载机制触发的Too many open files问题分析
- 并行查询缓慢的问题分析(r5笔记第86天)
- Swagger文档转Word 文档
- AJAX应用【股票案例、验证码校验】
- IT中的闰秒问题(r5笔记第85天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 打卡群2刷题总结1005——有效的括号
- 腾讯云服务器操作系统TencentOS安装与体验
- 打卡群2刷题总结1004——无重复字符的最长子串
- 如何恢复故障KVM虚拟机qcow2磁盘镜像文件LVM分区中的数据
- 8000字 | 32 张图 | 一文搞懂事务+隔离级别+阻塞+死锁
- 闪回flashback
- 海贼王 One Piece,一起康康Vue版本号中的彩蛋
- MySQL 三万字精华总结 + 面试100 问,吊打面试官绰绰有余(收藏系列)
- Oracle数据结构
- 打卡群2刷题总结1003——搜索旋转排序数组
- Rman备份恢复和管理
- 重做日志和日志挖掘
- 『技术随手学』解决windows与ubuntu平台 CondaHTTPError: HTTP 000 CONNECTION FAILED 问题
- DevOps编程操练:用Jenkins流水线建立代码质量预警机制
- 『技术随手学』pip conda 替换清华源 Windows与Ubuntu通用