为什么说是好笑,看到最后就明白了。
目标链接:url
打xhr断点并发起请求:
查看调用栈,发现了发起request的位置:
向上查看代码,发现了这个东西:
知道ajax请求里的signature参数和rs参数是从哪里来的。看一下这部分代码,可以知道构造url时的u,d,t来自哪里
u,d,t三个参数里,u和d很容易理解,主要是t参数如何获取,所以在这里重新开始打断点。s其实是一个函数,[u, d, l, window.couid, e, c, h["type" + e]].sort().join("")是将u,d等参数的列表变为一个字符串,作为s函数的参数。继续单步调试进入s函数:
这里的t就是前面说的字符串。继续调试,一顿调试后,找到最重要的一个函数doProcessBlock,然后基本就能知道如何获取加密后signature参数的值了。
整理了一下,获取加密后的链接,主要经过下面几个过程:
获取一个时间参数u:
import time
u = int(time.time()*1000)
获取参数d:
def get_d():
un = '''
function(t) {
var n = "",
e = false,
r = undefined,
i = t,
a = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
e && (i = Math.round(Math.random() * (r - t)) + t);
for (var o = 0; o < i; o++) {
n += a[Math.round(Math.random() * (a.length - 1))]
}
return n;}'''
t = 16
return js2py.eval_js(un)(t)
下面的代码是还原:[u, d, l, window.couid, e, c, h["type" + e]].sort().join(""),最后得到一个字符
lis = [u, d, l, couid, e, c, h]
lis = [str(i) for i in lis]
lis.sort()
old = ''.join(lis)
除了u和d参数,其余参数实际为固定的参数,需要自行逆向获取。这个old就是前面提到的那一长串字符串t
整个加密过程就是对几个数组的操作,首先是得到一个8位初始数组:
def get_words():
un = '''
function f1() {
var f = [], s = [];
function jisuan() {
function t(t) {
for (var r = Math.sqrt(t), n = 2; n <= r; n++) if (! (t % n)) return ! 1;
return ! 0
}
function r(e) {
return 4294967296 * (e - (0 | e)) | 0
}
for (var n = 2,
i = 0; i < 64;) t(n) && (i < 8 && (f[i] = r(Math.pow(n, .5))), s[i] = r(Math.pow(n, 1 / 3)), i++),
n++
}
jisuan();
return f;
}
'''
return js2py.eval_js(un)()
通过old字符串得到16位的数组1:
def get_new_words(t):
un = '''
function(e) {
for (var t = e.length,
r = [], n = 0; n < t; n++) r[n >>> 2] |= (255 & e.charCodeAt(n)) << 24 - n % 4 * 8;
return r;}'''
r = js2py.eval_js(un)(t)
return r
通过数组1得到32位的数组2:
def doFinalize(new_words):
un = '''
function(t){
var r=t, n=496, i=496;
r[i >>> 5] |= 128 << 24 - i % 32;
r[14 + (i + 64 >>> 9 << 4)] = Math.floor(n / 4294967296);
r[15 + (i + 64 >>> 9 << 4)] = n;
sigBytes = 4 * r.length;
h = r.splice(0, 32)
return h;
}
'''
result1 = js2py.eval_js(un)(new_words)
return result1
使用最开始的8位数组和32位数组经过doProcessBlock函数得到新的8位数组,然后使用新的8位数组和32位数组又得到一个新的8位数组,doProcessBlock函数如下:
def doProcessBlock(e, t, words):
un = '''
function(e, t, words, s) {
var c = [];
for (var r = words,
n = r[0], i = r[1], a = r[2], o = r[3], f = r[4], h = r[5], u = r[6], d = r[7], l = 0; l < 64; l++) {
if (l < 16) c[l] = 0 | e[t + l];
else {
var p = c[l - 15],
b = (p << 25 | p >>> 7) ^ (p << 14 | p >>> 18) ^ p >>> 3,
y = c[l - 2],
m = (y << 15 | y >>> 17) ^ (y << 13 | y >>> 19) ^ y >>> 10;
c[l] = b + c[l - 7] + m + c[l - 16]
}
var v = n & i ^ n & a ^ i & a,
g = (n << 30 | n >>> 2) ^ (n << 19 | n >>> 13) ^ (n << 10 | n >>> 22),
_ = d + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & h ^ ~f & u) + s[l] + c[l];
d = u,
u = h,
h = f,
f = o + _ | 0,
o = a,
a = i,
i = n,
n = _ + (g + v) | 0
}
r[0] = r[0] + n | 0,
r[1] = r[1] + i | 0,
r[2] = r[2] + a | 0,
r[3] = r[3] + o | 0,
r[4] = r[4] + f | 0,
r[5] = r[5] + h | 0,
r[6] = r[6] + u | 0,
r[7] = r[7] + d | 0
return r;
}
'''
s = get_s()
result = js2py.eval_js(un)(e, t, words, s)
return result
代码里的s其实就是get_words函数里那个s,修改get_words函数中js代码的返回值即可。
使用得到新的8位数组,就能获取最后的signature参数的值了:
def get_str(l, count):
un = '''
function(words, sigBytes) {
for (var t = words,
r = sigBytes,
n = [], i = 0; i < r; i++) {
var a = t[i >>> 2] >>> 24 - i % 4 * 8 & 255;
n.push((a >>> 4).toString(16)),
n.push((15 & a).toString(16))
}
return n.join("")
}
'''
count = 32
return js2py.eval_js(un)(l, count)
以上,就是整个逆向过程。
最后说一下为什么好笑,因为对js还是了解的不深,所以经过了漫长的调试才获取到最后的signature参数。但实际上对js足够了解的人,或者js逆向经验老到的人,看到signature参数的值基本就能猜到,signature参数是经过sha256加密后得到。。。。。。
费劲巴累的,最后把人家加密代码抠出来才解决问题。。。。。。