【驗證碼逆向專欄】某驗二代滑塊驗證碼逆向分析

聲明

本文章中所有內容僅供學習交流计呈,抓包內容、敏感網址征唬、數據接口均已做脫敏處理捌显,嚴禁用于商業(yè)用途和非法用途,否則由此產生的一切后果均與作者無關总寒,若有侵權扶歪,請聯系我立即刪除!

本文章未經許可禁止轉載摄闸,禁止任何修改后二次傳播善镰,擅自使用本文講解的技術而導致的任何意外,作者均不負責年枕,若有侵權炫欺,請在公眾號【K哥爬蟲】聯系作者立即刪除!

逆向目標

抓包情況

主頁點擊搜索就會跳出二代的驗證碼树姨,netWebServlet.json 的請求,會返回 challengegt桥状。

01

有個 get.php 的請求帽揪,返回了一個新的 challenge,這個請求之后的操作辅斟,都要用這個新的 challenge转晰,不然是驗證不成功的,其他的還有驗證碼背景圖片士飒、亂序圖片地址查邢、cs 等值变汪,之前寫過三代的文章侠坎,都是類似的,這里就不一一分析了裙盾。

02

然后是 ajax.php 驗證是否通過实胸,通過之后返回一個 validate,請求里同樣是需要我們逆向的 w 參數:

03
04

然后同樣還是 netWebServlet.json 接口番官,帶上 get.php 請求返回的 challenge 以及 ajax.php 返回的 validate庐完,請求拿到一個 name 的字段。

05
06

后續(xù)的搜索數據徘熔,帶上這個 name 就行了:

07

逆向分析

搞過三门躯、四代的都知道我們可以直接搜索 w 的 Unicode 值 \u0077 即可定位,但是二代則不是 Unicode酷师,而是16進制的編碼讶凉,搜索 \x77 即可定位,當然按照正常流程山孔,跟棧也能很容易找到加密的位置懂讯。

08

獲取 H7z 值

從上圖中可以知道 w 的值為 r7z + H7z,先看 H7z台颠。

09

跟進這個方法褐望,來到一大串控制流,這里還是推薦用 AST 還原一下串前,后續(xù)可能有一些循環(huán)啥的瘫里,硬跟的話容易出錯,當然直接全部扣一把梭也是可以的荡碾,H7z 的核心其實就是 RSA 加密隨機字符串谨读,三代四代都有,這里就不細講了坛吁。

10

獲取 r7z 值

然后就是 r7z劳殖,主要由以下兩句代碼生成:

11
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填具、imgloadaa匆骗、ep

12

獲取 userresponse 值

挨個分析劳景,首先是 userresponse,將滑動距離和 challenge 的值傳入一個方法碉就,得到一個 9 位字符串:

13

上圖中 g7z 就是滑動距離盟广,搜索可以看到定義的地方,尺子量一下對比一下瓮钥,和滑動的距離是一致的:

14
15

然后再來看看那個方法筋量,跟進去之后也是一大串 switch-case 控制流:

16

還原一下代碼如下:

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琼梆。

17
18

獲取 imgload 值

imgload 也沒啥特別的性誉,從字面意思猜測應該是圖片加載耗時,實測直接寫死即可茎杂,或者整個隨機值就行错览。

獲取 aa 值

aa 的值就是 F7z,如下圖所示:

19

搜索 F7z蛉顽,定位到下圖所示的地方蝗砾,向一個方法中傳入了一個時間戳:

20

跟進去同樣是 switch-case 控制流,需要注意的是下圖中 c7B[M9r.R8z(781)](M9r.R8z(764), K1z) 的值其實就是軌跡携冤。

21

這段控制流還原一下就變成這樣了:

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 第一次生成的地方悼粮,后面還有二次處理,如下圖所示:

22

同樣跟進去曾棕,三個傳入的參數分別是第一次生成的 F7z扣猫、get.php 請求返回的 cs 參數。

23

同樣是一段控制流翘地,還原后如下:

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'},寫死即可昧穿。

24

獲取 rp 值

自此 Y7z 的第一步生成就分析完畢了勺远,注意接下來還有一步,向 Y7z 里新增了一個 rp 參數:

25

這個值的組成看起來很長时鸵,實際上是將 gt胶逢、challenge 前 32 位以及 passtime 相加經過 MD5 加密后得到的。

Y7z["rp"] = md5(gt + challenge.slice(0, 32) + passtime)
26

上圖中 I0B 就是 MD5 方法饰潜,跟進去其實是可以看到很多 MD5 特征的初坠,如下圖所示:

27

自此 Y7z 的值就搞定了,然后接著前面的看彭雾,也就是 q7z 的值碟刺,同樣和三四代一樣的,encrypt 是 AES 加密薯酝,Y7z 經過 JSON.stringify() 處理為字符串作為待加密對象半沽,后面是 16 為隨機字符串作為 AES 的 Key,注意這里的隨機字符串應該和獲取 H7z 值時的隨機字符串一致蜜托,不然是驗證不成功的抄囚。

28

然后下一步就是獲取 r7z 的值,將上一步得到的 q7z 經過一個方法進行處理橄务,跟進方法幔托,又是和三四代一樣的,熟悉的 res + end蜂挪,如下圖所示:

29

直接扣代碼重挑,或者直接使用三代的代碼即可:

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 的值棠涮。

30

結果驗證

測試過掉驗證碼抓取數據成功:

31
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末谬哀,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子严肪,更是在濱河造成了極大的恐慌史煎,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驳糯,死亡現場離奇詭異篇梭,居然都是意外死亡,警方通過查閱死者的電腦和手機酝枢,發(fā)現死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門恬偷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帘睦,你說我怎么就攤上這事袍患√箍担” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵诡延,是天一觀的道長滞欠。 經常有香客問我,道長肆良,這世上最難降的妖魔是什么仑撞? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮妖滔,結果婚禮上,老公的妹妹穿的比我還像新娘桶良。我一直安慰自己座舍,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布陨帆。 她就那樣靜靜地躺著曲秉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疲牵。 梳的紋絲不亂的頭發(fā)上承二,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音纲爸,去河邊找鬼亥鸠。 笑死,一個胖子當著我的面吹牛识啦,可吹牛的內容都是我干的负蚊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼颓哮,長吁一口氣:“原來是場噩夢啊……” “哼家妆!你這毒婦竟也來了?” 一聲冷哼從身側響起冕茅,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤伤极,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姨伤,有當地人在樹林里發(fā)現了一具尸體哨坪,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年姜挺,在試婚紗的時候發(fā)現自己被綠了齿税。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡炊豪,死狀恐怖凌箕,靈堂內的尸體忽然破棺而出拧篮,到底是詐尸還是另有隱情,我是刑警寧澤牵舱,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布串绩,位于F島的核電站,受9級特大地震影響芜壁,放射性物質發(fā)生泄漏礁凡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一慧妄、第九天 我趴在偏房一處隱蔽的房頂上張望顷牌。 院中可真熱鬧,春花似錦塞淹、人聲如沸窟蓝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽运挫。三九已至,卻和暖如春套耕,著一層夾襖步出監(jiān)牢的瞬間谁帕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工冯袍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留匈挖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓康愤,卻偏偏與公主長得像关划,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子翘瓮,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容