哈希加密逆向案例学习-模拟登录返回及后续
目标地址:红人点集
模拟登录返回
正常与非正常登录响应
登录API及负载示例:
POST https://user.hrdjyun.com/wechat/phonePwdLogin HTTP/1.1
content-type: application/json
{
"phoneNum": "18912345678",
"pwd": "25f9e794323b453885f5181f1b624d0b",
"t": 1669724122701,
"tenant": 1,
"sig": "6ae516c833cd58624d42f3351edef581"
}
由于逆向目的在于,破解并生成加密参数,以正常请求API,因此以下为正常响应示例:
{status: 3, message: "手机号还未注册", data: null}
非正常响应:
{status: 1, message: "参数错误", data: null}
XHR断点
直接开始使用XHR断点,根据栈查找指定的代码区域:
涉及到pwd
参数加密代码:
Object(S["a"])(t.loginForm.password)
对比加密结果:
>Object(S["a"])('1')
<'c4ca4238a0b923820dcc509a6f75849b'
与在线工具生成一致,为MD5加密
在本地我们使用以下代码代替,免于扣代码:
const crypto = require('crypto-js')
function md5(text){
return crypto.MD5(text).toString()
}
现在再来锁定sig
(通过直接搜索事半功倍),找到以下代码:
var l = Object(g["a"])(P(S(e)));
return Object.assign(e, {
sig: l
}),
通过控制台调用:
> Object(g["a"])('1')
< 'c4ca4238a0b923820dcc509a6f75849b'
可以发现依然是MD5加密
扣代码:
function P(n) {
var e = []
, t = "";
for (var a in n)
e.push(n[a]);
for (var i = 0; i < e.length; i++)
t += e[i] + "";
return t += "JzyqgcoojMiQNuQoTlbR5EBT8TsqzJ",
t
}
function S(n) {
for (var e = Object.keys(n).sort(), t = {}, a = 0; a < e.length; a++)
t[e[a]] = n[e[a]];
return t
}
function md5(text){
return crypto.MD5(text).toString()
}
function getSig(e){
return md5(P(S(e)))
}
基本不需要处理...
所以包装一下的代码为:
const crypto = require('crypto-js')
function P(n) {
var e = []
, t = "";
for (var a in n)
e.push(n[a]);
for (var i = 0; i < e.length; i++)
t += e[i] + "";
return t += "JzyqgcoojMiQNuQoTlbR5EBT8TsqzJ",
t
}
function S(n) {
for (var e = Object.keys(n).sort(), t = {}, a = 0; a < e.length; a++)
t[e[a]] = n[e[a]];
return t
}
function md5(text){
return crypto.MD5(text).toString()
}
function getSig(e){
return md5(P(S(e)))
}
function getForm(account,pswd){
let form = {
phoneNum: "18912345678",
pwd: md5(pswd),
t: new Date().getTime().toString(),
tenant: 1
}
form.sig = getSig(form)
return form
}
// 测试
console.log(getForm('18912345678','123456'))
后续测试
使用Python测试一下:
import os
import execjs
import requests
import json
os.environ["NODE_PATH"] = os.path.join('../' + "/node_modules")
def get_form(account: str, pswd: str) -> dict:
with open('./hrdj.js') as f:
js_code = f.read()
js = execjs.compile(js_code)
return js.call('getForm', account, pswd)
def main(account:str,pswd:str)->dict:
api = 'https://user.hrdjyun.com/wechat/phonePwdLogin'
header = {
"Accept": "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Length": "137",
"Content-Type": "application/json",
"Host": "user.hrdjyun.com",
"Origin": "https://www.hh1024.com",
"Pragma": "no-cache",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.56"
}
# 注意使用json.dumps()
res = requests.post(url=api,data=json.dumps(get_form(account,pswd)),headers=header)
res_json = res.json()
return res_json
if __name__ == '__main__':
print(main('18912345678','123456'))
输出结果:
{'status': 3, 'message': '手机号还未注册', 'data': None}
模拟验证成功
商品目录API加密参数
API:https://ucp.hrdjyun.com:60359/api/dy
其中token
是登录后返回,因此特别关注 sign
XHR断点
同理,直接尝试使用XHR断点定位
下断点再请求一遍发现为此处,开始扣代码:
function E(n, e) {
return k("param=" + JSON.stringify(n) + "×tamp=" + e + "&tenant=1&salt=" + C)
}
function getSign(params){
return E(params,new Date().getTime())
}
其中测试k()
,发现其是标准SHA-256加密函数
我们使用crypto.SHA256().toString()
代替,最终代码为:
function k(text){
return crypto.SHA256(text).toString()
}
function E(n, e) {
return k("param=" + JSON.stringify(n) + "×tamp=" + e + "&tenant=1&salt=" + C)
}
function getSign(params){
return E(params,new Date().getTime())
}
这里还有一个C
未解决,控制台输出为:
'kbn%&)@<?FGkfs8sdf4Vg1*+;`kf5ndl$'
相关代码:
var C = z();
function z() {
for (var n = Object(s["a"])().concat(Object(A["a"])(), Object(h["a"])(), Object(p["a"])(), Object(f["a"])(), Object(m["b"])()), e = "", t = 0; t < n.length; t++)
e += String.fromCharCode(parseInt(n[t], 16));
return e
}
但是,经过多次重新请求发现,C
始终不变,所以暂时以固定值处理(如果后续无法爬取,再来处理),因此最终代码为:
function k(text){
return crypto.SHA256(text).toString()
}
function E(n, e) {
return k("param=" + JSON.stringify(n) + "×tamp=" + e + "&tenant=1&salt=" + 'kbn%&)@<?FGkfs8sdf4Vg1*+;`kf5ndl$')
}
function getSign(params){
return E(params,new Date().getTime())
}
请求一次,手动生成参数对比:
参数一致