逆向分析某红色跑步App的API 前言
群友尝试破解自己学校的某红色校园跑App,来帮个忙
准备工具 分析数据包使用Reqable抓取上传跑步数据时的请求,内容如图:

图片1.png (412.3 KB, 下载次数: 2)
下载附件
图片1
2024-12-12 23:26 上传
一眼看到Header存在一个校验字段:_sign,内容是一个大写的的16字节HexString
分析dex这里已经怀疑可能是MD5,将Apk拖入jadx搜索_sign找到如图请求位置:

图片2.png (140.97 KB, 下载次数: 2)
下载附件
图片2
2024-12-12 23:26 上传
继续往下跟踪:

图片3.png (38.24 KB, 下载次数: 2)
下载附件
2024-12-12 23:26 上传

图片4.png (84.64 KB, 下载次数: 2)
下载附件
2024-12-12 23:26 上传
分析libsign.so至此找到调用生成_sign的函数getSignatureV2 这个函数是一个native实现,经过对目录lib/arm64-v8a的寻找,发现极为可疑的文件libsign.so
将libsign.so导入IDA分析,可见JNI函数:

图片5.png (77.52 KB, 下载次数: 2)
下载附件
图片5
2024-12-12 23:26 上传
直接点开Java_co_runner_app_jni_NativeToolImpl_getSignatureV2查看逻辑:

图片6.png (231.89 KB, 下载次数: 1)
下载附件
图片6
2024-12-12 23:27 上传
发现逻辑如下:
Java层传入的第1, 2个参数(IDA中为a3, a4)作为内部函数getSign的第1, 2个参数使用
内部函数getSecret()获取了一个Secret值,作为内部函数getSign的第3个参数使用
先分析Secret是怎么来的:

图片7.png (258.66 KB, 下载次数: 2)
下载附件
图片7
2024-12-12 23:27 上传
全部是一些memcpy,strcat之类的操作,说明Secret的生成路径是固定的,没有依靠时间等seed对其进行随机处理(getSign1和getSign3以及其调用的函数也没有使用随机,这里就先不放图了),所以我们基本可以认为使用frida对函数getSecret()进行钩取,可以获得其固定返回值。
(假装这里有一张截图)
由于我手上暂时没有 Android 13 及以下的环境,所以让群友代抓了一下getSecret()函数的返回值,多次发送请求进行抓取,发现Secret确实为固定值20eca08916b92********2786e315dea
拿到了三个参数,我们就可以研究函数getSign的逻辑了。双击getSign,跳转到函数中查看逻辑,如图:

图片8.png (174.25 KB, 下载次数: 2)
下载附件
图片8
2024-12-12 23:27 上传
分析起来不难,使用了三个std::__put_character_sequence<char,std::char_traits<char>>,基本可以看出来是把三个参数顺序连接了起来,而后面MD5函数的调用也印证了前面的猜想。
继续让群友代劳,抓了一些调用Java层调用getSignatureV2的参数和返回值,用来验证猜测是否正确。但是我在这一步卡了很久,因为我认为Secret在最后,最后多试了几种排列方法才发现Secret实际上在两个Java层传入参数的中间。
重新实现Java层参数接下来看一下Java层传入的两个参数都是什么东西。
第一个参数见上图分析dex时的图片可知,第一个参数是一个Map<String, String> paramValueMap经过sort得到的顺序,再以该顺序将其每个键与值从前到后拼接在一起得到的。就像这样:contentheartrate[]lasttime1733995696meter196nodetimenomo...
尝试在Python中还原该逻辑:
params = {} for segment in dataStr.split("&"): if "=" in segment: k, v = segment.split("=", 1) else: k, v = segment, "" k = urllib.parse.unquote_plus(k) v = urllib.parse.unquote_plus(v) params[k] = v sorted_keys = sorted(params.keys()) sb = [] for key in sorted_keys: sb.append(key) sb.append(params[key]) dataFinalStr = "".join(sb)库自带的parse会导致没有值的键消失,比如解析&content=&xxx=***的时候,content就离奇失踪了。
第二个参数我们可以看到uid > 0 ? (uid + sid).getBytes() : new byte[0] 其实就是把uid和sid直接拼一起而已。
王牌飞行员接下来的事就是把_sign应用到脚本里了,效果如图:

图片9.png (65.45 KB, 下载次数: 2)
下载附件
2024-12-12 23:27 上传
一些花絮一些大模型反编译平台(MLM01等)会出现搜索不到函数,还会出现AI瞎编代码逻辑的情况
同时使用 Android 14、15 和 KernelSU 的 Zygisk 模块时,会出现frida无法spawn程序的情况,报错为等待zygote程序(的PID)超时,尝试了一些办法(例如kill掉zygote进程)均无法解决,希望有大佬支招
这个App的34个dex让我的18G小内存在jadx搜索时不堪重负
Secret的位置是上Linux课的时候摸鱼试出来的
尾声谁写的getSecret,建议开除





查看全部评分