聲明
本文章中所有內容僅供學習交流计呈,抓包內容、敏感網址征唬、數據接口均已做脫敏處理捌显,嚴禁用于商業(yè)用途和非法用途,否則由此產生的一切后果均與作者無關总寒,若有侵權扶歪,請聯系我立即刪除!
本文章未經許可禁止轉載摄闸,禁止任何修改后二次傳播善镰,擅自使用本文講解的技術而導致的任何意外,作者均不負責年枕,若有侵權炫欺,請在公眾號【K哥爬蟲】聯系作者立即刪除!
逆向目標
- 目標:某驗二代滑塊驗證碼逆向分析
- 主頁:
aHR0cDovL3d3dy5qc2dzai5nb3YuY246NTg4ODgvbWluaS9uZXR3ZWIvU01MaWJyYXJ5LmpzcA==
- 說明:大多數邏輯其實和三四代都一樣熏兄,相同的就簡寫了品洛,有疑惑的地方可以看以前的文章
- 【驗證碼逆向專欄】某驗三代滑塊驗證碼逆向分析
- 【驗證碼逆向專欄】某驗四代滑塊驗證碼逆向分析
抓包情況
主頁點擊搜索就會跳出二代的驗證碼树姨,netWebServlet.json
的請求,會返回 challenge
和 gt
桥状。
有個 get.php
的請求帽揪,返回了一個新的 challenge
,這個請求之后的操作辅斟,都要用這個新的 challenge
转晰,不然是驗證不成功的,其他的還有驗證碼背景圖片士飒、亂序圖片地址查邢、c
、s
等值变汪,之前寫過三代的文章侠坎,都是類似的,這里就不一一分析了裙盾。
然后是 ajax.php
驗證是否通過实胸,通過之后返回一個 validate
,請求里同樣是需要我們逆向的 w
參數:
然后同樣還是 netWebServlet.json
接口番官,帶上 get.php
請求返回的 challenge
以及 ajax.php
返回的 validate
庐完,請求拿到一個 name
的字段。
后續(xù)的搜索數據徘熔,帶上這個 name
就行了:
逆向分析
搞過三门躯、四代的都知道我們可以直接搜索 w
的 Unicode 值 \u0077
即可定位,但是二代則不是 Unicode酷师,而是16進制的編碼讶凉,搜索 \x77
即可定位,當然按照正常流程山孔,跟棧也能很容易找到加密的位置懂讯。
獲取 H7z 值
從上圖中可以知道 w
的值為 r7z + H7z
,先看 H7z
台颠。
跟進這個方法褐望,來到一大串控制流,這里還是推薦用 AST 還原一下串前,后續(xù)可能有一些循環(huán)啥的瘫里,硬跟的話容易出錯,當然直接全部扣一把梭也是可以的荡碾,H7z
的核心其實就是 RSA 加密隨機字符串谨读,三代四代都有,這里就不細講了坛吁。
獲取 r7z 值
然后就是 r7z
劳殖,主要由以下兩句代碼生成:
q7z = n0B[M9r.R8z(699)](h7B[M9r.C8z(105)](Y7z), V7z[M9r.R8z(818)]())
r7z = p7B[M9r.R8z(260)](q7z)
可以看到其中有個變量 Y7z
參與了計算贼邓,先來看看他是怎么來的,直接搜索即可定位闷尿,可以發(fā)現同樣是16進制的編碼,由五個值組成:userresponse
女坑、passtime
填具、imgload
、aa
匆骗、ep
獲取 userresponse 值
挨個分析劳景,首先是 userresponse
,將滑動距離和 challenge
的值傳入一個方法碉就,得到一個 9 位字符串:
上圖中 g7z
就是滑動距離盟广,搜索可以看到定義的地方,尺子量一下對比一下瓮钥,和滑動的距離是一致的:
然后再來看看那個方法筋量,跟進去之后也是一大串 switch-case
控制流:
還原一下代碼如下:
function getUserResponse(L0z, o0z) {
for (var j0z = o0z.slice(32), c0z = [], X0z = 0; X0z < j0z.length; X0z++){
var K0z = j0z.charCodeAt(X0z);
c0z[X0z] = K0z > 57 ? K0z - 87 : K0z - 48;
}
j0z = 36 * c0z[0] + c0z[1];
var k0z = Math.round(L0z) + j0z;
o0z = o0z.slice(0, 32);
var n0z, f0z = [[], [], [], [], []], Q0z = {}, N0z = 0;
X0z = 0;
for (var i0z = o0z.length; i0z > X0z; X0z++){
n0z = o0z.charAt(X0z), Q0z[n0z] || (Q0z[n0z] = 1, f0z[N0z].push(n0z), N0z++, N0z = 5 == N0z ? 0 : N0z);
}
var y0z, v0z = k0z, B0z = 4, x0z = "", I0z = [1, 2, 5, 10, 50];
while ( v0z > 0) {
v0z - I0z[B0z] >= 0 ? (y0z = parseInt(Math.random() * f0z[B0z].length, 10),
x0z += f0z[B0z][y0z], v0z -= I0z[B0z]) : (f0z.splice(B0z, 1),
I0z.splice(B0z, 1), B0z -= 1);
}
return x0z;
}
獲取 passtime 值
passtime
不用考慮是怎么通過函數獲取的,含義就是滑動完成所花費的時間碉熄,直接取軌跡的最后一個值即可桨武,這個也和三四代是一樣的,獲取語句為:var passtime = track[track.length - 1][2]
锈津,如下圖所示呀酸,軌跡的最后一個值時間為 871,passtime
的值同樣也為 871琼梆。
獲取 imgload 值
imgload
也沒啥特別的性誉,從字面意思猜測應該是圖片加載耗時,實測直接寫死即可茎杂,或者整個隨機值就行错览。
獲取 aa 值
aa
的值就是 F7z
,如下圖所示:
搜索 F7z
蛉顽,定位到下圖所示的地方蝗砾,向一個方法中傳入了一個時間戳:
跟進去同樣是 switch-case
控制流,需要注意的是下圖中 c7B[M9r.R8z(781)](M9r.R8z(764), K1z)
的值其實就是軌跡携冤。
這段控制流還原一下就變成這樣了:
function getF7z(track){
var o5r = 6;
for (var N1z, X1z = s6z(track), f1z = [], B1z = [], o1z = [], t1z = 0, j1z = X1z.length; t1z < j1z; t1z++){
if (o5r * (o5r + 1) % 2 + 8) {
N1z = u6z(X1z[t1z]),
N1z ? B1z.push(N1z) : (f1z.push(O6z(X1z[t1z][0])),
B1z.push(O6z(X1z[t1z][1]))),
o1z.push(O6z(X1z[t1z][2]));
o5r = o5r >= 17705 ? o5r / 3 : o5r * 3;
}
}
return f1z.join("") + "!!" + B1z.join("") + "!!" + o1z.join("");
}
function s6z(F6z){
for (var Y6z, g6z, a6z, E6z = [], D6z = 0, P6z = [], J6z = 0, l6z = F6z.length - 1; J6z < l6z; J6z++) {
Y6z = Math.round(F6z[J6z + 1][0] - F6z[J6z][0]),
g6z = Math.round(F6z[J6z + 1][1] - F6z[J6z][1]),
a6z = Math.round(F6z[J6z + 1][2] - F6z[J6z][2]),
P6z.push([Y6z, g6z, a6z]),
0 == Y6z && 0 == g6z && 0 == a6z || (0 == Y6z && 0 == g6z ? D6z += a6z : (E6z.push([Y6z, g6z, a6z + D6z]), D6z = 0));
}
return 0 !== D6z && E6z.push([Y6z, g6z, D6z]), E6z;
}
function O6z(r6z){
var d6z = "()*,-./0123456789:?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqr"
, m6z = d6z.length
, Z6z = ""
, H6z = Math.abs(r6z)
, W6z = parseInt(H6z / m6z);
W6z >= m6z && (W6z = m6z - 1), W6z && (Z6z = d6z.charAt(W6z)), H6z %= m6z;
var q6z = "";
return r6z < 0 && (q6z += "!"), Z6z && (q6z += "$"), q6z + Z6z + d6z.charAt(H6z);
}
function u6z(R6z){
for (var z6z = [[1, 0], [2, 0], [1, -1], [1, 1], [0, 1], [0, -1], [3, 0], [2, -1], [2, 1]], h6z = 0, C6z = z6z.length; h6z < C6z; h6z++){
if (R6z[0] == z6z[h6z][0] && R6z[1] == z6z[h6z][1]){
return "stuvwxyz~"[h6z]
}
}
return 0;
}
以上只是 F7z
第一次生成的地方悼粮,后面還有二次處理,如下圖所示:
同樣跟進去曾棕,三個傳入的參數分別是第一次生成的 F7z
扣猫、get.php
請求返回的 c
和 s
參數。
同樣是一段控制流翘地,還原后如下:
function getF7z2(Q1z, v1z, T1z){
var i1z, x1z = 0, c1z = Q1z, y1z = v1z[0], k1z = v1z[2], L1z = v1z[4];
while (1){
if (i1z = T1z.substr(x1z, 2)){
x1z += 2;
var n1z = parseInt(i1z, 16)
, M1z = String.fromCharCode(n1z)
, I1z = (y1z * n1z * n1z + k1z * n1z + L1z) % Q1z.length;
c1z = c1z.substr(0, I1z) + M1z + c1z.substr(I1z);
}else {
return c1z
}
}
return Q1z
}
至此 aa
參數分析完畢申尤!
獲取 ep 值
ep
的值就是一個版本號癌幕,此處是 {'v': '6.0.9'}
,寫死即可昧穿。
獲取 rp 值
自此 Y7z
的第一步生成就分析完畢了勺远,注意接下來還有一步,向 Y7z
里新增了一個 rp
參數:
這個值的組成看起來很長时鸵,實際上是將 gt胶逢、challenge 前 32 位以及 passtime 相加經過 MD5 加密后得到的。
Y7z["rp"] = md5(gt + challenge.slice(0, 32) + passtime)
上圖中 I0B
就是 MD5 方法饰潜,跟進去其實是可以看到很多 MD5 特征的初坠,如下圖所示:
自此 Y7z
的值就搞定了,然后接著前面的看彭雾,也就是 q7z
的值碟刺,同樣和三四代一樣的,encrypt
是 AES 加密薯酝,Y7z
經過 JSON.stringify()
處理為字符串作為待加密對象半沽,后面是 16 為隨機字符串作為 AES 的 Key,注意這里的隨機字符串應該和獲取 H7z
值時的隨機字符串一致蜜托,不然是驗證不成功的抄囚。
然后下一步就是獲取 r7z
的值,將上一步得到的 q7z
經過一個方法進行處理橄务,跟進方法幔托,又是和三四代一樣的,熟悉的 res + end
蜂挪,如下圖所示:
直接扣代碼重挑,或者直接使用三代的代碼即可:
function $_GJF(e) {
var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()";
return e < 0 || e >= t["length"] ? "." : t["charAt"](e);
}
function $_HBO(e, t) {
return e >> t & 1;
}
function $_HCX(e, o) {
var i = this;
o || (o = i);
for (var t = function(e, t) {
for (var n = 0, r = 24 - 1; 0 <= r; r -= 1)
1 === $_HBO(t, r) && (n = (n << 1) + $_HBO(e, r));
return n;
}, n = "", r = "", s = e.length, a = 0; a < s; a += 3) {
var c;
if (a + 2 < s)
c = (e[a] << 16) + (e[a + 1] << 8) + e[a + 2],
n += $_GJF(t(c, 7274496)) + $_GJF(t(c, 9483264)) + $_GJF(t(c, 19220)) + $_GJF(t(c, 235));
else {
var _ = s % 3;
2 == _ ? (c = (e[a] << 16) + (e[a + 1] << 8),
n += $_GJF(t(c, 7274496)) + $_GJF(t(c, 9483264)) + $_GJF(t(c, 19220)),
r = ".") : 1 == _ && (c = e[a] << 16,
n += $_GJF(t(c, 7274496)) + $_GJF(t(c, 9483264)),
r = "." + ".");
}
}
return {
"res": n,
"end": r
};
}
獲取 w 值
自此 w
的就已經出來了,r7z + H7z
即為 w
的值棠涮。
結果驗證
測試過掉驗證碼抓取數據成功: