图源:AI绘画
Y29tLnlsdHgub2lsLnBhcnRuZXI=
没有壳,直接抓包分析
这里分析的是两个请求
第一个是【油品贸易】- 【商品】列表的请求,请求头中一个 X-sign
第二个是登录请求中的 password 加密
用 jadx 直接打开 app,然后搜索关键词 x-sign
进入文件,搜索 PARAM_SIGN
可以找到 x-sign 是由 sign(sb.toString()); 的得到的
进一步看 sign 的逻辑
可以看到就是一个 md5,参与加密的是 token + 请求时间 + 盐值+传入参数
当然静态分析的结果不一定可信,之后使用动态 hook 做进一步确认,同时还要确认一其它几个参与加密的值是上面
frida 启动,hook 一下刚刚定位到的 sign 方法
Java.perform(
function(){
var RequestParamsWrapper = Java.use('xxx.xxx.xxx.xxx.xxx.xxx.interceptors.xxxx');
RequestParamsWrapper.sign.implementation = function(str1){
console.log("i am RequestParamsWrapper.sign")
console.log("str1:"+str1);
var xsign = this.sign(str1);
console.log("xsign:" + xsign);
return xsign;
}
}
);
刷新的【商品】的下一页,可以看到这里的 sign 方法的入参就是请求的参数拼接
接下来 hook md5 找到全部参与加密的字符串内容
Java.perform(
function(){
var Md5 = Java.use('xxx.xxx.xxxx.xxxx');
Md5.md5.implementation = function(str1){
console.log("i am utils.Md5")
console.log("str1:"+str1);
var xsign = this.md5(str1);
console.log("xsign:" + xsign);
return xsign;
}
}
);
通过日志配合抓包
可以知道最后传入加密的字符串的值分别是token 为空+当前的时间戳+盐值是 3456 +请求参数拼接
再通过在线网站验证一下结果,确实是 md5
再用 Python 还原如下
import time
import requests
from hashlib import md5
def x_sign(time_str, data):
x_sign = f"{time_str}3456{data}"
result = md5(x_sign.encode('utf8')).hexdigest()
return result
def get_shop_list(page):
time_str = str(int(round(time.time() * 1000)))
data = f"pageNo={page}"
sign_result = x_sign(time_str, data)
headers = {
"X-App": "native",
"X-Noncestr": "123456",
"X-OS": "partnerApp_android",
"X-Req-Time": time_str,
"X-Sign": sign_result,
"X-Token": "",
"X-UserID": "",
"Host": "chinayltx.com",
"User-Agent": "okhttp/3.10.0"
}
url = "xxxxxxxxxxxx"
data = {
"pageNo": f"{page}"
}
response = requests.post(url, headers=headers, data=data)
print(response.text)
print(response)
请求结果如下
全局搜索 "password",可以找到下面的位置
接下来通过抓包中的链接对比apk 这里定义的请求链接找到下面的位置
然后通过【查找用例】找到下面的位置
通过全局搜索 loginWithToken
可以找到下面的位置,可以看到这里调用了 setPwd 写入密码
继续【查找用例】
可以定位到下面的代码位置
这个就没有啥好动态分析的了 就是标准的 md5 ,原文就密码
同样的在这个请求的 header 中也有一个 x-sign
我们重复上面的步骤可以看到如下的两个日志
同样的使用 Python 请求验证一下
上就是今天的全部内容了,咱们下次再会~