目录
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!
前言
抓包
定位加密函数
native层分析
编辑 post请求python还原算法
总结
上一篇文章是一个半月前了,还是web的,因为博主一直在学android的内容,所以断更了很久,本期带来的是唯品会so层api_sign
设备: pixel4 android10
下载地址: aHR0cHM6Ly93d3cud2FuZG91amlhLmNvbS9hcHBzLzMxNTgzL2hpc3Rvcnlfdjc0NTA2
版本: 7.45.6
抓包工具: charles配合socksdroid
本来是准备搞最新版的,奈何我的frida启动新版的会闪退,不知道是app检测了还是我的frida版本的问题,这里先埋个坑,后面能解决了再回头看
1. 搜索一个商品后往下滑翻个页,一共两个包,复制curl出来 文本对比
2. 发现是通过传商品id进行翻页的,商品id由另一个包里返回,就是说只有头部的api_sign是变化的,把他注释掉返回的是API signature must not be empty,看来这是必须要分析的,观察它的长度是40位,猜测可能是sha1或者hmacsha1,不确定是在java层还是so层,这里我用算法助手hook了一下摘要算法和hamc算法发现搜不到加密的参数,有可能是魔改的java层或者在so层
3. 把app拖到jadx里反编译后搜索关键字 api_sign
4. 这里第二个很像,双击点进去瞅瞅,如下图
5. 从b.b点进去瞅瞅
6. 传进来第一个参数是一个上下文对象,第二个treemap对象,三四是两个str,再点进a这个函数里看看
7. 正常情况下返回apisign,从apiSign点进去,这里我就贴几张图快进一下
8. 这里是关键了,如下图
9. 找到com.vip.vcsp.KeyInfo这个类里的gs,发现它调用了gsNav这个native方法 ,这个native方法加载自LibName,点进去是keyinfo,所以是libkeyinfo.so
10. 未发现java层算法猜测是so层,掏出ida分析libkeyinfo.so,在exports导出表里搜索gsnav发现是静态注册
11. 点进去后f5转成伪c代码
12. 从j_Functions_gs进去后拉到最下面看返回值
13. j_getByteHash有点可疑,点进去看看
14. 发现调用了getByteHash,再从getByteHash点进去
15. 发现sha1字眼,十有八九是sha1算法了,有没有魔改还得验证,所以可以hook getByteHash这个函数Module.findExportByName("libkeyinfo.so", "getByteHash"),函数名以汇编中出现的为准
16. 开着hook和抓包,在输出里搜索api_sign的值
18. 对比后发现是标准sha1,接着分析传进去的参数, 通过搜索可以发现传进去的参数前32位是盐值,后40位看起来也像是sha1,接着往上看,发现传进去的明文是盐值加表单参数拼接的字符串
19. 复制出来验证一下发现也是标准sha1,到这里流程就清晰了 sha1(a84c5883206309ad076deea939e850dc + 请求参数拼接后的字符串) , 得到第一次哈希值 sha1(a84c5883206309ad076deea939e850dc + 第一次哈希值), 得到第二次哈希值,即头部的api_sign
import hashlib
data = {
"api_key": "23e7f28019e8407b98b84cd05b5aef2c",
"app_name": "shop_android",
"app_version": "7.45.6",
"channel_flag": "0_1",
"client": "android",
"client_type": "android",
"context": "",
"darkmode": "0",
"deeplink_cps": "",
"did": "0.0.8b7c1485b5c14a39511af05c7ab3fe77.1c7d15",
"extParams": "{\"showSellPoint\":\"1\",\"mclabel\":\"1\",\"cmpStyle\":\"1\",\"ic2label\":\"1\",\"reco\":\"1\",\"vreimg\":\"1\",\"floatwin\":\"1\",\"preheatTipsVer\":\"4\",\"exclusivePrice\":\"1\",\"stdSizeVids\":\"\",\"rank\":\"2\",\"couponVer\":\"v2\",\"live\":\"1\"}",
"fdc_area_id": "103102102106",
"mars_cid": "ca80df7a-08a6-3f45-9ed5-212f6205aed2",
"mobile_channel": "kowd7uq2:::",
"mobile_platform": "3",
"other_cps": "",
"page_id": "page_te_commodity_search_1698026503833",
"phone_model": "Pixel 4",
"productIds": "6920468238887599571,6920201626111489235,6920494379145106643,6920505600051864787,6920513277897073875,6920401603189915987,6920260684243911507,6920474112576265683,6920472644902059475,6919453468868617043,6920513277897061587,6920403066637577683,6920442067411766547,6920504144689169619,6920513176371332307,6920482408805473747,6920401603206828371,6920504144689288403,6920516119363553747,6920201626128426195",
"province_id": "103102",
"referer": "com.achievo.vipshop.search.activity.VerticalTabSearchProductListActivity",
"rom": "Dalvik/2.1.0 (Linux; U; Android 10; Pixel 4 Build/QQ2A.200405.005)",
"scene": "search",
"sd_tuijian": "0",
"session_id": "ca80df7a-08a6-3f45-9ed5-212f6205aed2_shop_android_1698026465696",
"skey": "2d30297ff20ec9b7442dc4f3c335abdc",
"source_app": "android",
"standby_id": "kowd7uq2:::",
"sys_version": "29",
"timestamp": "1698026513",
"warehouse": "VIP_SH"
}
string = ""
for key, value in data.items():
string += f"{key}={value}&"
# 去除末尾的"&"符号
string = string[:-1]
str = 'a84c5883206309ad076deea939e850dc'+string
sign1 = hashlib.sha1(str.encode('utf-8')).hexdigest()
api_sign = hashlib.sha1(('a84c5883206309ad076deea939e850dc'+sign1).encode('utf-8')).hexdigest()
print(api_sign)
1出于安全考虑,本章未提供完整流程,调试环节省略较多,只提供大致思路,具体细节要你自己还原,相信你也能调试出来.
2本人写作水平有限,如有讲解不到位或者讲解错误的地方,还请各位大佬在评论区多多指教,共同进步,也可加本人微信lyaoyao__i(两个_)