某app的sign分析
sign分析
charles抓取到sign值是一个128位的值,猜测可能是md5或sha128。
寻找sign计算的关键点,可以通过jnitrace
对NewStringUTF
等函数进行hook,也可以直接写frida脚本hook strlen/NewStringUTF
等字符串处理函数,以字符串长度为32进行过滤。最后定位到在libbbili.so!0x16bdc
位置会调用NewStringUTF
生成sign值
来到libbbili.so!0x16bdc
查看大致加密过程,其根据参数a3
和a4
的值得到四个4字节大小salt值,然后调用关键加密函数18ff0
查看1605C
函数,其根据a3
和a4
的值从三张表中选一张,和上面的分析对比,调用加密函数前会从选择的表中0, 19, 38, 57
索引处得到四个四字节的salt
盐值。
对加密函数18ff0
和外部函数162A8
进行hook,并打印参数。发现a3
的值可能是0/1
,a4的值一直都是0
,对应的盐值为2653583c 8873dea2 68ab9386 918b1d65
或者560c52cc d288fed0 45859ed1 8bffd973
。被加密的数据是接口的请求参数(包括时间戳),最后调用完加密函数18ff0
后可以得到sign
值。
分析18ff0
函数看到明显的MD5
初始化常量
根据md5
算法的特征定位到MD5Init
和MD5Update
函数,首先会将请求参数进行md5
hash
然后循环调用四次MD5Update
对四个盐值进行hash,最后的sign值就是MD5(请求参数+ 盐值)
外部函数162A8
对应的应该是一个jni函数,往上回溯到0x9050
和0x9058
位置后到顶,并且在JNI_OnLoad
中会调用RegisterNative
引用,所以这两个函数应该是动态注册的
因为so增加了ollvm
并且有字符串加密,unicorn
跑一下所有的.init.array
中的函数或者直接从内存中dump so并patch so的data段,此时字符串应该已经解密恢复了,再次查看0x9050
和0x9058
引用处可以看到动态注册对应的java
类和方法
也可以直接通过frida hook RegisterNative
进行打印
对应的java类处往上回溯可以找到协议登陆等关键位置
unidbg模拟执行
对上述0x9058
函数中的加密相关过程包括MD5Update
进行hook并打印每一次调用的参数。当arg3 = 1
,arg4 = 0
,请求参数是appkey=783bbb7264451d82&build=7530400&buvid=XYA09D4AFCD9B0A480A29C74A6A9D9F1726AA&c_locale=zh-Hans_CN&channel=bili&disable_rcmd=0&local_id=XYA09D4AFCD9B0A480A29C74A6A9D9F1726AA&mobi_app=android&platform=android&s_locale=zh-Hans_CN&statistics={"appId":1,"platform":3,"version":"7.53.0","abtest":""}&ts=1701828749
时,对应的sign
为88df41ed8a657f3070347fbdec4ea72a
。
unidbg
初始化参数列表并调用0x9058
其中SignedQuery
类是直接从反编译的apk中拿的,报错的地方进行一些修补就可以用了,0x9058
函数返回的就是SignedQuery
对象,其sign
成员就是生成的sign值。
剩下的就是对运行中的报错信息进行补环境,对于com.bilibili.nativelibrary.SignedQuery
类直接用从反编译代码中抠出来的实现即可。
最后模拟执行生成的sign
值与预期的一样