极验验证码(6.0.9)破解(三) 之 'userresponse' 参数调试和分析

回顾

  1. 上篇文章主要介绍极验6.0.9的三种不同方式的混淆方式,并进行简单的分析
  2. 利用python,将三种混淆方式,替换成了可读文本
  3. 我们到现在为止,已经完成了抓包分析,混淆分析和解读,准备工作算是完成了,下面开始解密各个参数
  4. 这次,我们将利用Google浏览器(下面记作Google)的断点调试功能以及反混淆后的js文件(反混淆文件),说一下’userresponse’参数的调试和分析,并最终python化程序

1. 为什么是userresponse参数?

系列篇第一篇抓包分析我们就讲到,破解w参数,是我们破解极验的关键,抓包过程也没有发现userresponse参数的踪迹,那为什么我们要介绍userresponse参数生成呢?如果你之前有过极验(一代)的破解经验,对这个参数肯定不陌生。我们在第二篇反混淆解密十六进制字符串的过程中,尝试搜索gt,曾经意外发现主了人公w,那我们在利用Google浏览器进行js调试之前,先简单看一下这段反混淆代码(第1424行)
极验验证码(6.0.9)破解(三) 之 'userresponse' 参数调试和分析_第1张图片
这块代码很简单,‘w’ = r7z + H7z,而r7z 和 H7z 是由变量Y7z和V7z经过相关函数处理生成的,我们先尝试找下Y7z和V7z这两个变量的定义

首先是Y7z(第1383行)
极验验证码(6.0.9)破解(三) 之 'userresponse' 参数调试和分析_第2张图片
很明显了吧?我为什么要去研究’userresponse’参数

其次是V7z(第1346行)
极验验证码(6.0.9)破解(三) 之 'userresponse' 参数调试和分析_第3张图片
我们发现变量V7z是其他地方调用的i7B[“tb”]时候写入的参数,因此这次暂不研究

2. 针对userresponse参数的js断点调试

根据前面的分析,我们需要在geetest.6.0.9.js文件的第1384行处打断点,具体调试过程不再演示
极验验证码(6.0.9)破解(三) 之 'userresponse' 参数调试和分析_第4张图片根据调试过程,整理出’userresponse’参数生成的js代码

'\x75\x73\x65\x72\x72\x65\x73\x70\x6f\x6e\x73\x65': i7B[M9r.R8z(722)](g7z, V7z[M9r.C8z(190)][M9r.R8z(425)]);

i7B[M9r.C8z(722)] = function(L0z, o0z) {
    var c1r = M9r.k9r()[2][7][21];
    while (c1r !== M9r.L9r()[18][12][0]) {
        switch (c1r) {
        case M9r.L9r()[15][39][27]:
            X0z++;
            c1r = M9r.k9r()[13][28][42];
            break;
        case M9r.L9r()[6][22][30]:
            return x0z;
            c1r = M9r.L9r()[44][40][0];
            break;
        case M9r.k9r()[32][7][37][17]:
            X0z++;
            c1r = M9r.L9r()[31][47][3];
            break;
        case M9r.L9r()[20][9][15]:
            n0z = o0z[M9r.R8z(169)](X0z),
            Q0z[n0z] || (Q0z[n0z] = 1,
            f0z[N0z][M9r.C8z(340)](n0z),
            N0z++,
            N0z = 5 == N0z ? 0 : N0z);
            Y4r = Y4r >= 19614 ? Y4r / 7 : Y4r * 7;
            c1r = M9r.L9r()[29][13][28][35];
            break;
        case M9r.L9r()[16][14][18]:
            c1r = v0z > 0 && g4r * (g4r + 1) * g4r % 2 == 0 ? M9r.k9r()[19][35][39] : M9r.k9r()[18][46][30];
            break;
        case M9r.L9r()[26][25][3]:
            c1r = X0z < i0z && Y4r * (Y4r + 1) * Y4r % 2 == 0 ? M9r.k9r()[31][19][15] : M9r.k9r()[20][33][39];
            break;
        case M9r.L9r()[14][47][21]:
            var n0z, f0z = [[], [], [], [], []], Q0z = {}, N0z = 0;
            X0z = 0;
            c1r = M9r.L9r()[15][19][15];
            break;
        case M9r.k9r()[20][42][30]:
            var j0z = o0z[M9r.C8z(504)](32)
              , c0z = []
              , X0z = 0;
            c1r = M9r.L9r()[43][16][42];
            break;
        case M9r.L9r()[43][27][15]:
            var i0z = o0z[M9r.C8z(40)];
            c1r = M9r.L9r()[0][45][3];
            break;
        case M9r.L9r()[27][8][1][20]:
            o0z = o0z[M9r.C8z(504)](0, 32);
            c1r = M9r.L9r()[1][9][21];
            break;
        case M9r.L9r()[42][40][36]:
            var K0z = j0z[M9r.R8z(458)](X0z);
            c0z[X0z] = K0z > 57 ? K0z - 87 : K0z - 48;
            F4r = F4r >= 10020 ? F4r / 5 : F4r * 5;
            c1r = M9r.L9r()[0][21][27];
            break;
        case M9r.k9r()[3][26][0]:
            g4r = g4r > 32264 ? g4r / 5 : g4r * 5;
            c1r = M9r.k9r()[25][44][18];
            break;
        case M9r.k9r()[44][23][33]:
            j0z = 36 * c0z[0] + c0z[1];
            var k0z = Math[M9r.R8z(47)](L0z) + j0z;
            c1r = M9r.k9r()[47][0][18];
            break;
        case M9r.L9r()[35][3][39]:
            var y0z, v0z = k0z, B0z = 4, x0z = M9r.C8z(346), I0z = [1, 2, 5, 10, 50];
            c1r = M9r.k9r()[35][40][18];
            break;
        case M9r.k9r()[26][7][17][13]:
            var g4r = 6;
            var Y4r = 7;
            var F4r = 3;
            c1r = M9r.k9r()[46][22][30];
            break;
        case M9r.L9r()[21][44][42]:
            c1r = X0z < j0z[M9r.R8z(40)] && F4r * (F4r + 1) % 2 + 6 ? M9r.k9r()[6][16][36] : M9r.k9r()[36][7][33];
            break;
        case M9r.L9r()[8][25][39]:
            v0z - I0z[B0z] >= 0 ? (y0z = parseInt(Math[M9r.R8z(12)]() * f0z[B0z][M9r.R8z(40)], 10),
            x0z += f0z[B0z][y0z],
            v0z -= I0z[B0z]) : (f0z[M9r.C8z(448)](B0z, 1),
            I0z[M9r.R8z(448)](B0z, 1),
            B0z -= 1);
            c1r = M9r.L9r()[31][34][0];
            break;
        }
    }
}

是不是一脸懵逼??????我们来看下反混淆之后的

'userresponse': i7B["C"](g7z, V7z["d"]["challenge"]);
i7B["C"] = function(L0z, o0z) {
    var c1r = 27;
    while (c1r !== 0) {
        switch (c1r) {
        case 21:
            X0z++;
            c1r = 38;
            break;
        case 34:
            return x0z;
            c1r = 0;
            break;
        case 39:
            X0z++;
            c1r = 45;
            break;
        case 17:
            n0z = o0z["charAt"](X0z),
            Q0z[n0z] || (Q0z[n0z] = 1,
            f0z[N0z]["push"](n0z),
            N0z++,
            N0z = 5 == N0z ? 0 : N0z);
            Y4r = Y4r >= 19614 ? Y4r / 7 : Y4r * 7;
            c1r = 39;
            break;
        case 30:
            c1r = v0z > 0 && g4r * (g4r + 1) * g4r % 2 == 0 ? 9 : 34;
            break;
        case 45:
            c1r = X0z < i0z && Y4r * (Y4r + 1) * Y4r % 2 == 0 ? 17 : 41;
            break;
        case 43:
            var n0z, f0z = [[], [], [], [], []], Q0z = {}, N0z = 0;
            X0z = 0;
            c1r = 1;
            break;
        case 2:
            var j0z = o0z["slice"](32)
              , c0z = []
              , X0z = 0;
            c1r = 38;
            break;
        case 1:
            var i0z = o0z["length"];
            c1r = 45;
            break;
        case 14:
            o0z = o0z["slice"](0, 32);
            c1r = 43;
            break;
        case 28:
            var K0z = j0z["charCodeAt"](X0z);
            c0z[X0z] = K0z > 57 ? K0z - 87 : K0z - 48;
            F4r = F4r >= 10020 ? F4r / 5 : F4r * 5;
            c1r = 21;
            break;
        case 32:
            g4r = g4r > 32264 ? g4r / 5 : g4r * 5;
            c1r = 30;
            break;
        case 31:
            j0z = 36 * c0z[0] + c0z[1];
            var k0z = Math["round"](L0z) + j0z;
            c1r = 14;
            break;
        case 41:
            var y0z, v0z = k0z, B0z = 4, x0z = "", I0z = [1, 2, 5, 10, 50];
            c1r = 30;
            break;
        case 27:
            var g4r = 6;
            var Y4r = 7;
            var F4r = 3;
            c1r = 2;
            break;
        case 38:
            c1r = X0z < j0z["length"] && F4r * (F4r + 1) % 2 + 6 ? 28 : 31;
            break;
        case 9:
            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);
            c1r = 32;
            break;
        }
    }
}

很明显,反混淆之后,代码逻辑很容易就可以读明白,我们把代码用python改写下

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# // var Y7z = {
# //     'userresponse': i7B["C"](42, V7z["d"]["challenge"]),
# //     'passtime': c7B["a"]("endTime", V7z["b"])["getTime"]() - c7B["a"]("startTime", V7z["b"]),
# //     'imgload': c7B["a"]("imgload", V7z["b"]),
# //     'aa': F7z,
# //     'ep': i7B["Vb"](V7z)
# // };


import random

def get_userresponse(L0z, o0z):
    '''TODO
        获取userresponse
        参数:
            L0z 滑动的最后x坐标位置
            o0z 极验配置的challenge参数 eg '22ff2f326624b638791dfdef7892700d9w'
        返回值:
            userresponse  eg 4114411168e
    '''
    g4r = 6
    Y4r = 7
    F4r = 3
    j0z = o0z[32:]
    c0z = []

    for j in j0z:
        K0z = ord(j)
        c0z.append(K0z - 87 if K0z > 57 else K0z - 48)
        F4r = F4r / 5 if F4r >= 10020 else F4r * 5

    j0z = 36 * c0z[0] + c0z[1]
    k0z = round(L0z) + j0z

    o0z = o0z[:32]

    n0z = 0
    f0z = [[], [], [], [], []]
    Q0z = {}
    N0z = 0
    i0z = len(o0z)

    for i in o0z:
        n0z = i
        if not Q0z.get(n0z):
            Q0z[n0z] = 1
            f0z[N0z].append(n0z)
            N0z += 1
            N0z = 0 if 5 == N0z else N0z
        Y4r = Y4r / 7 if Y4r >= 19614 else Y4r * 7

    y0z, v0z = 0, k0z
    B0z = 4
    x0z = ""
    I0z = [1, 2, 5, 10, 50]

    while 1:
        if v0z > 0:
            if v0z - I0z[B0z] >= 0:
                y0z = int(random.random() * len(f0z[B0z]))
                x0z += f0z[B0z][y0z]
                v0z -= I0z[B0z]
            else:
                f0z.pop(B0z)
                I0z.pop(B0z)
                B0z -= 1
            g4r = g4r / 5 if g4r > 32264 else g4r * 5
        else:
            return x0z

def main():
    o0z = '22ff2f326624b638791dfdef7892700d9w'
    L0z = 58
    print(get_userresponse(L0z,o0z))


if __name__ == '__main__':
    main()

总结

  1. 我们首先简单看下反混淆之后的代码,能够很快的帮助我们定位调试断点的位置
  2. 'userresponse’参数的生成相对简单,js反混淆之后也能很容易的读明白其中的逻辑
  3. 至于生成userresponse值的两个参数L0z和 o0z,可以到后期在做分析,这里主要是说明userresponse的生成,其他的值,可看做常量
  4. js和python代码的执行结果有差异,是因为代码中有用到随机值,拿到的结果,即便写入的L0z和 o0z参数值相同,也会得到不同的结果,是正常情况
  5. 其他参数’passtime’, ‘imgload’, ‘aa’, ‘ep’,调试方式和 'userresponse’大差不差,后续在介绍的时候,不再这么详细的赘述,感兴趣的伙伴后续的可以自行研究了。
  6. 下一篇我们将介绍’aa’参数值的生成。

文档仅作学习和记录使用,请不要用于商业和违法用途,如有侵权,请告知删除。

你可能感兴趣的:(技术学习与分享)