961 words
5 minutes
小黑盒web端逆向
小黑盒web端逆向
Intro
最近想写个小黑盒爬虫,研究了下,
是有加密的,发送请求必须要携带hkey和nonce这两个参数。 定位到生成这两个参数的代码,
是在native层,在接下去逆向有些难度。
然后又发现小黑盒的部分模块是有web端的。比如,在一个游戏详情界面,点分享,然后得到的链接,就是小黑盒的网页版。
网页端逆向起来就方便多了。
算法分析
f12开发者工具随便点开一个请求,然后查看请求堆栈,
虽然经过了混淆,但是大致能猜出来这是axios,并且参数里面没有我们要找的nonce和hkey,尝试了直接在js文件里搜nonce和hkey,都没搜到。
最后通过axios的拦截器,找到了生成这两个参数的位置。
原来是用了字符拼接的方法,怪不得搜不到。
通过对代码的简单整理
a = ~~(new Date().getTime/1000)) //获取秒级时间戳
o = md5(a + Math.random((new Date).getTime()).toString()) //a的值加上一个随机数,然后进行md5运算
n['nonce'] = o
n['hkey'] = v(path,a,0); //path为请求路径
只要解决了v函数,就算是破解完了
function v(t, e, n) {
t = "/".concat(t.split("/").filter((function(t) {
return t
}
)).join("/"), "/");
for (var a = "", i = "JKMNPQRTX1234OABCDFG56789H", r = g()((n + i).split("").filter((function(t) {
return /[0-9]/.test(t)
}
)).join("")), o = g()(e + t + r).split("").filter((function(t) {
return /[0-9]/.test(t)
}
)).slice(0, 9).join(""), c = 0; c < 9 - o.length; c++)
o += "0";
for (var s = Number(o), l = 0; l < 5; l++) {
var u = s % 26;
s = ~~(s / 26),
a += i[u]
}
var d = "".concat(function(t) {
return t.reduce((function(t, e, n) {
return t + e
}
))
}(p(a.slice(-4).split("").map((function(t) {
return t.charCodeAt()
}
)))) % 100);
return d.length < 2 && (d = 0 + d),
d = a + d
}
手动简化后如下
function hkey(path, time, n) {
// t = /game/wx_share/info/v2/
var i = 'JKMNPQRTX1234OABCDFG56789H'
var r = md5(trimNumber(n + i).join(''))
var o = trimNumber(md5(time + path + r)).slice(0, 9).join('')
var s = Number(o)
var a = ''
for (var l = 0; l < 5; l++) {
var u = s % 26;
s = ~~(s / 26), //取整数
a = a.concat(i[u])
}
var arr = p(a.slice(-4).split("").map((function (t) {
return t.charCodeAt()
}
))) //取a的后四位的Unicode值作为数组的四个元素传给p函数,把经过处理的数组求和再对100求余 即为d的值
var d = (arr[0] + arr[1] + arr[2] + arr[3]) % 100
return a + d
}
function trimNumber(n) {
return n.split("").filter(t => {
return /[0-9]/.test(t) //提取数字
})
}
完整代码
function u(t) {
return 128 & t ? 255 & (t << 1 ^ 27) : t << 1
}
function d(t) {
return u(t) ^ t
}
function h(t) {
return d(u(t))
}
function f(t) {
return h(d(u(t)))
}
function m(t) {
return f(t) ^ h(t) ^ d(t)
}
function p(t) {
var e = [0, 0, 0, 0];
return e[0] = m(t[0]) ^ f(t[1]) ^ h(t[2]) ^ d(t[3]),
e[1] = d(t[0]) ^ m(t[1]) ^ f(t[2]) ^ h(t[3]),
e[2] = h(t[0]) ^ d(t[1]) ^ m(t[2]) ^ f(t[3]),
e[3] = f(t[0]) ^ h(t[1]) ^ d(t[2]) ^ m(t[3]),
t[0] = e[0],
t[1] = e[1],
t[2] = e[2],
t[3] = e[3],
t
}
import { createHash } from 'node:crypto'
function md5(content) {
return createHash('md5').update(content).digest('hex')
}
function hkey(path, time, n) {
// t = /game/wx_share/info/v2/
var i = 'JKMNPQRTX1234OABCDFG56789H'
var r = md5(trimNumber(n + i).join(''))
var o = trimNumber(md5(time + path + r)).slice(0, 9).join('')
var s = Number(o)
var a = ''
for (var l = 0; l < 5; l++) {
var u = s % 26;
s = ~~(s / 26), //取整数
a = a.concat(i[u])
}
var arr = p(a.slice(-4).split("").map((function (t) {
return t.charCodeAt()
}
))) //取a的后四位的Unicode值作为数组的四个元素传给p函数,把经过处理的数组求和再对100求余 即为d的值
var d = (arr[0] + arr[1] + arr[2] + arr[3]) % 100
return a + d
}
function trimNumber(n) {
return n.split("").filter(t => {
return /[0-9]/.test(t) //提取数字
})
}
var time = ~~(new Date().getTime() / 1000)
var nonce = md5(time + Math.random((new Date).getTime()).toString()).toLocaleUpperCase()
console.log(time)
console.log(hkey('/game/get_game_detail/', time+1, nonce))
console.log(nonce)
nonce的计算 是当前时间加上一个随机浮点数做md5运算,其实这个nonce可以是任意md5值,hkey的计算用到了nonce,只要nonce和hkey的nonce参数对的上就可以