某搜索引擎,微信公众号文章,版本号7.9.6,pc端没有时间排序功能,只有app可以筛选时间。
通过抓包看到请求的request和response都是加密的,ok开始分析
搜索大法,直接搜url,就可以直接定位关键点。request加密和response解密的位置似乎都很清晰了,加解密都是通过SCoreTools.so中的函数来实现的。这里定位还是比较容易的,直接打开so文件开始分析。
ida打开so文件,查看导出表,四个静态注册的函数,先来看一下请求的encrypt。
通过hook观察,第一个参数是固定的一个url,第二个参数是url的params,里面包含了搜索词,排序方式等维度的字段,第三个参数是空。返回值就是我们抓包看到的post请求的formdata,六个参数用&分割。接下来进入encrypt函数做详细的分析。
先静态分析一下,传过来的str1和str2在进行类型变化后,都只在j_Sc_EncryptWallEncode进行了调用,然后通过对21304函数的hook查看res就是我们最终需要的加密。这一段的逻辑应该就是,先开辟一段内存空间,走j_sc函数,在里面进行一些列操作后,将加密结果放入res并返回1,然后走if,将res赋值给v15最后retrun,继续分析j_sc函数。
进入j_sc函数,scookie和forwarddata并没有做什么有意义的操作,重点需要关注的是EncryptHttpRequest3,hook查看参数信息,第六个参数的返回值是最终加密结果,做好标注继续往里走。
通过hook B100和base64两个函数,再配合下面的字符串拼接,可以确定我们最终需要的六个参数是由base64和B100这两个函数生成的。
这里稍微说一下加密逻辑,先是在wallkey中生成随机数random和random+16(这里需要注意的是random+16,并不是做值得运算,而是将random的内存地址+16,得到新的内存地址得值),把这个随机数直接base64(这里的base64经过测试,没有做任何源码的改动,可以直接用)就得到了 v 的值。随机数+16做rsa(rsa的公钥和偏移量都直接给出了)加密,把加密结果base64就得到了 k 的值。随机数+16经过genxor_s,结果base64得到 r 的值。
看到这里发现genxor_s的第二个参数,每一次都是动态变化的,往前追溯,发现其实就是一个随机数。genxor_s这个函数的操作非常简单,就是random+16和(*(a7 + 6) + 16)的值异或。
说到这里,三个参数的结果就已经拿到了。需要注意的就是传参的类型转换,py出身的我一开始对这个传参很是纠结,因为他是直接传的字节流,而不是我熟悉的base64encode(一个字符串)然后得到结果。这里用参数k的值举例,就可以完美解释这一过程。
上图中1为base64编码前的字节流(也就是rsa生成的结果,rsa的结果也是一个字节流,之前就因为太过于纠结字符串所以才看不懂),2为编码前的字节流转成字符串的样子(因为不符合utf-8的格式,所以转成字符串也看不出是什么)。不要去看他的字符串,因为他本来就是通过字节流来操作的。而按照py的逻辑复原,可以这样搞。
这里的字符串h,其实就是hook到的字节流拼在一起了。这里的操作其实和chr(int(“0c”, 16))是一样的。把字符串转成16进制的形式,然后再转chr就得到了bytes的类型。但是这里发现用chr并不科学,因为他对不符合utf-8的转换结果会有问题。所以最后选择了binascii.unhexlify()。这样的操作和我们熟悉的base64encode(str.encode())是一样的结果。图中可以看到此操作和hook到的结果是一样的。
继续向下看B100函数。
因为他这个so并没有混淆,所以这里看着就比较清晰,可以按照逻辑大胆尝试,将字符串压缩后经过aes加密最后base64编码。经过测试这里确实是这样的逻辑,算法也都是标准算法。只是需要注意的是,压缩是用的哪种压缩算法,AES的cbc模式key和iv要怎么去判断。
先来说一下压缩这个函数,看似输出的是v11,其实压缩后的结果是由第三个参数传出的。他的压缩算法一开始我是百思不得其解,我分别用了py的,zlib、gzip、bz2、lzma、brotli的compress进行压缩,发现结果与hook到的都不一样,一度怀疑是将标准算法做了改动,实则不然。最后无意间发现了其中的猫腻。
用zlib压缩得到的结果,去掉前四后八个字节,就和hook的结果一致了。而后面做AES加密的,也是这个。然后来说一下AES的key和iv的值要如何判断。
key比较好判断,因为没有混淆,见名知意都可以猜的到。而iv的判断,如果看过C++版本的cbc模式的AES源码,一下就能知道这就是iv的值,如果看不出来,建议熟悉一下算法的源码。这里的AES经过测试是一个标准的AES cbc模式。
到这里,加密就分析完了。最后来总结一下加密的逻辑。
随机生成两个随机数,一个做为aes的key,一个做为aes的iv,将key做rsa加密(rsa的公钥是写死的,所以服务器端的解密私钥也是固定的,这样传值服务器就能拿到我们的key),rsa的结果做base64编码得到了k。然后将key与一个等长的随机数进行异或操作,把结果做base64编码得到r。iv直接做base64编码得到v(到这里,服务器端就拿到了我们的随机key,iv,就可以解aes拿到我们的搜索词了),str1(也就是域名)过B100函数(先压缩,压缩结果走aes,然后base64编码)得到u,str2(请求参数,包含搜索词,排序等字段)过B100函数得到g,v52(是一个定值写死就行)过B100函数得到p。
response解密大概看了一下,也是没有混淆的aes,我用py写了一下是可以成功的,比较简单,这里就不多说明了。用py请求的时候,headers里面需要加一个’Accept-Encoding’: ‘’,不然会报解析错误,第一次见到headers会让requests.post()报错这种情况。
关于压缩和解压缩如果弄不明白,这里可以参考一位大佬的文章,关于各类压缩算法的Head特征探究,我也是看了这篇文章才解决了response解压缩的问题。
总的来说这个app并不是很难,都是标准算法也没有混淆。拿来熟悉一下几个常用的算法还是比较好的。
最后欢迎各位老司机进群交流:546452230