找回密码
 立即注册
首页 业界区 安全 python&js逆向 破解滑动验证码

python&js逆向 破解滑动验证码

恃液 昨天 15:40
现在的滑动验证码防盗等级都比较高,之前的是一张完整的图片带缺口,现在返回的图片是打乱顺序拼接而成的,所以现在破解不仅要识别滑块的缺口,同时还需要复原完整的图片
  
1.png
    
2.png

 一.伪造请求获取验证码图片
    
3.png

    
4.png

  可以看到请求中主要的两个参数ctxid和request,所以我们只需要找到这两个参数的生成逻辑并进行请求就可以获取到验证码图片
5.png

   根据谷歌浏览器关键字搜索便可以找到相关字段的生成逻辑,我们只需要仿照着他的逻辑进行生成即可,值得注意的是request参数进行了双重加密,base64Encode中嵌套一个encrypt。这块我们不需要关注他具体是怎么加密的,我们只需要找到他对应的js方法,通过python直接调用即可
  1. function a() {
  2.                     for (var n, e = "6_11_7_10_4_12_3_1_0_5_2_9_8".split("_"), t = [], r = 0; r < 52; r++)
  3.                         n = 2 * parseInt(e[parseInt(r % 26 / 2)]) + r % 2,
  4.                         parseInt(r / 2) % 2 || (n += r % 2 ? -1 : 1),
  5.                         n += r < 26 ? 26 : 0,
  6.                         t.push(n);
  7.                     return t
  8.                 }
  9. function base64Encode(n) {
  10.         var e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");
  11.         return function(n) {
  12.             var t, r, i, o, a, s, c;
  13.             for (r = i = 0,
  14.             o = n.length,
  15.             s = (o -= a = o % 3) / 3 << 2,
  16.             a > 0 && (s += 4),
  17.             t = new Array(s); r < o; )
  18.                 c = n.charCodeAt(r++) << 16 | n.charCodeAt(r++) << 8 | n.charCodeAt(r++),
  19.                 t[i++] = e[c >> 18] + e[c >> 12 & 63] + e[c >> 6 & 63] + e[63 & c];
  20.             return 1 == a ? (c = n.charCodeAt(r++),
  21.             t[i++] = e[c >> 2] + e[(3 & c) << 4] + "==") : 2 == a && (c = n.charCodeAt(r++) << 8 | n.charCodeAt(r++),
  22.             t[i++] = e[c >> 10] + e[c >> 4 & 63] + e[(15 & c) << 2] + "="),
  23.             t.join("")
  24.         }(n)
  25.     }
  26.    
  27. function encrypt(n) {
  28.         var e, t = "e98ae8878c264a7e";
  29.         function r(n) {
  30.             if (/^[\x00-\x7f]*$/.test(n))
  31.                 return n;
  32.             for (var e = [], t = n.length, r = 0, i = 0; r < t; ++r,
  33.             ++i) {
  34.                 var o = n.charCodeAt(r);
  35.                 if (o < 128)
  36.                     e[i] = n.charAt(r);
  37.                 else if (o < 2048)
  38.                     e[i] = String.fromCharCode(192 | o >> 6, 128 | 63 & o);
  39.                 else {
  40.                     if (!(o < 55296 || o > 57343)) {
  41.                         if (r + 1 < t) {
  42.                             var a = n.charCodeAt(r + 1);
  43.                             if (o < 56320 && 56320 <= a && a <= 57343) {
  44.                                 var s = 65536 + ((1023 & o) << 10 | 1023 & a);
  45.                                 e[i] = String.fromCharCode(240 | s >> 18 & 63, 128 | s >> 12 & 63, 128 | s >> 6 & 63, 128 | 63 & s),
  46.                                 ++r;
  47.                                 continue
  48.                             }
  49.                         }
  50.                         throw new Error("Malformed string")
  51.                     }
  52.                     e[i] = String.fromCharCode(224 | o >> 12, 128 | o >> 6 & 63, 128 | 63 & o)
  53.                 }
  54.             }
  55.             return e.join("")
  56.         }
  57.         function i(n) {
  58.             return 4294967295 & n
  59.         }
  60.         function o(n, e, t, r, i, o) {
  61.             return (t >>> 5 ^ e << 2) + (e >>> 3 ^ t << 4) ^ (n ^ e) + (o[3 & r ^ i] ^ t)
  62.         }
  63.         function a(n, e) {
  64.             var t, r = n.length, i = r >> 2;
  65.             0 != (3 & r) && ++i,
  66.             e ? (t = new Array(i + 1))[i] = r : t = new Array(i);
  67.             for (var o = 0; o < r; ++o)
  68.                 t[o >> 2] |= n.charCodeAt(o) << ((3 & o) << 3);
  69.             return t
  70.         }
  71.         return null == n || 0 === n.length ? n : (n = r(n),
  72.         t = r(t),
  73.         function(n, e) {
  74.             var t = n.length
  75.               , r = t << 2;
  76.             if (e) {
  77.                 var i = n[t - 1];
  78.                 if (i < (r -= 4) - 3 || i > r)
  79.                     return null;
  80.                 r = i
  81.             }
  82.             for (var o = 0; o < t; o++)
  83.                 n[o] = String.fromCharCode(255 & n[o], n[o] >>> 8 & 255, n[o] >>> 16 & 255, n[o] >>> 24 & 255);
  84.             var a = n.join("");
  85.             return e ? a.substring(0, r) : a
  86.         }(function(n, e) {
  87.             var t, r, a, s, c, l, d = n.length, u = d - 1;
  88.             for (r = n[u],
  89.             a = 0,
  90.             l = 0 | Math.floor(6 + 52 / d); l > 0; --l) {
  91.                 for (s = (a = i(a + 2654435769)) >>> 2 & 3,
  92.                 c = 0; c < u; ++c)
  93.                     t = n[c + 1],
  94.                     r = n[c] = i(n[c] + o(a, t, r, c, s, e));
  95.                 t = n[0],
  96.                 r = n[u] = i(n[u] + o(a, t, r, u, s, e))
  97.             }
  98.             return n
  99.         }(a(n, !0), ((e = a(t, !1)).length < 4 && (e.length = 4),
  100.         e)), !1))
  101.     }
复制代码
  1. mainJs = execjs.compile(open(r"./Utils/main.js",encoding="utf-8").read())
  2. def getCaptcha(contextid):
  3.     o = "appid=202503141611|ctxid=" + contextid+ "|a=quoteapi|p=|r=" + str(random.random())
  4.     oEncrypt=mainJs.call('encrypt',o)
  5.     # 对字符串进行URL编码
  6.     reqStr=quote(mainJs.call('base64Encode',oEncrypt))
  7.     print(reqStr)
  8.     url='api/captcha/get?callback=&ctxid='+contextid+'&request='+reqStr+'&_='+str(int(time.time()*1000))
  9.     print(url)
  10.     initResponse = http.request('GET', url)
  11.     print('initResponse:')
  12.     print(initResponse.data)
  13.     initResult = initResponse.data.decode('UTF-8')
  14.     initData = json.loads(initResult)
  15.     if initData["ReturnCode"]=="0" and initData["Data"]["CaptchaType"]=="init":
  16.         slideResponse = http.request('GET', url)
  17.         print('slideResponse:')
  18.         print(slideResponse.data)
  19.         slideResult = slideResponse.data.decode('UTF-8')
  20.         slideData = json.loads(slideResult)
  21.         print(slideData)
  22.         if slideData["ReturnCode"]=="0" and slideData["Data"]["CaptchaType"]=="slide":
  23.             print("getCaptcha slide success")
  24.             captchaInfo={}
  25.             captchaInfoObj=json.loads(slideData["Data"]['CaptchaInfo'])
  26.             captchaInfo["bg"]="https://"+captchaInfoObj['static_servers'][0]+captchaInfoObj['bg']
  27.             captchaInfo["fullbg"]="https://"+captchaInfoObj['static_servers'][0]+captchaInfoObj['fullbg']
  28.             captchaInfo["slice"]="https://"+captchaInfoObj['static_servers'][0]+captchaInfoObj['slice']
  29.             return captchaInfo
  30.         else:
  31.              print("getCaptcha slide error")
  32.              return None
  33.     else:
  34.         print("getCaptcha init error")
  35.         return None
复制代码
 二.重新拼接验证码图片
6.png
  
7.png

 
通过上边的操作,我们就能获得一张顺序错乱的验证码,接下来我们就需要重新复原这张图片。接口返回一张错乱的验证码图片,但页面上为什么显示正常呢?那肯定是前端js逻辑把这块给处理了一下,页面就得到了一张正常的验证码图片,那么我们就需要找一下他前端js的处理逻辑了,只要按照他的逻辑处理,我们同样也能复原这张错乱验证码
8.png

 
  1. def get_image(image_url):
  2.     e = "6_11_7_10_4_12_3_1_0_5_2_9_8".split("_")
  3.     location_list = []
  4.     for r in range(52):
  5.         location = {}
  6.         # 计算索引
  7.         index = int(r % 26 / 2)
  8.         n = 2 * int(e[index]) + (r % 2)
  9.         
  10.         # 条件判断
  11.         if (int(r / 2) % 2) == 0:
  12.             n += -1 if (r % 2) else 1
  13.         
  14.         # 添加偏移量
  15.         if r < 26:
  16.             n += 26
  17.         location['x'] = n % 26 * 12 + 1
  18.         location['y'] = (0 - 160)/ 2 if n <= 25 else 0
  19.         location_list.append(location)
  20.     print('==================================')
  21.     image_result = requests.get(image_url).content
  22.     image_file = BytesIO(image_result)
  23.     image = merge_image(image_file,location_list)
  24.     return image
复制代码
 三.根据缺口距离生成滑动轨迹
  1. def get_distance(url1,url2):
  2.     '''
  3.       拿到滑动验证码需要移动的距离
  4.       :param image1:没有缺口的图片对象
  5.       :param image2:带缺口的图片对象
  6.       :return:需要移动的距离
  7.       '''
  8.     # print('size', image1.size)
  9.     #print(type(image1))
  10.     len=0
  11.     threshold = 50
  12.     image1=get_image(url1)
  13.     image2=get_image(url2)
  14.    
  15.     # 创建绘图对象
  16.     draw = ImageDraw.Draw(image2)
  17.     for i in range(0,image1.size[0]):  # 260
  18.         for j in range(0,image1.size[1]):  # 160
  19.             pixel1 = image1.getpixel((i,j))
  20.             pixel2 = image2.getpixel((i,j))
  21.             res_R = abs(pixel1[0]-pixel2[0]) # 计算RGB差
  22.             res_G = abs(pixel1[1] - pixel2[1])  # 计算RGB差
  23.             res_B = abs(pixel1[2] - pixel2[2])  # 计算RGB差
  24.             if res_R > threshold and res_G > threshold and res_B > threshold:
  25.                 len=i  #
  26.                 print(f'{len}-({res_R},{res_G},{res_B})')
  27.                 # 绘制一条线
  28.                 draw.line((130, 0, len, 20), fill="red", width=3)
  29.                 break
  30.     image2.show()
  31.     #image2.save('./Image/code.jpg')
  32.     return len
复制代码
 四.提交验证
9.png

 通过逆向网站js,我们可以查看到他提交时的逻辑,其中userresponse为缺口距离,data便是轨迹数据,我们只需要根据他的逻辑来实现我们的python逻辑即可
  1. def generate_sliding_track(x):
  2.     # 初始化轨迹列表
  3.     slide_track = [
  4.         [random.randint(-50, -20), random.randint(-200, -100), 0],
  5.         [0, 0, 0],
  6.     ]
  7.     if x < 100:
  8.         move_section = 1 #如果移动距离小于100 那么move次数为x加上 7到20之间的数
  9.     else:
  10.         move_section = 2 #如果移动距离小于100 那么move次数为x加上 2乘 7到20之间的数
  11.     up_down = random.randint(0, 1) #确定一个方向 x大于0或x小于0
  12.     y = 0  #数组的y值
  13.     time = random.randint(100,180)  #初始时间 即为第二个数组的时间  后续时间累加操作就可以了
  14.     count = 0
  15.     flag = 0
  16.     repetition = int(x/4)  #重复x出现的个数
  17.     frist_count = random.randint(6,10) #前面y为0的数组个数
  18.     for i in range(x*random.randint(move_section*7,move_section*21)): #move_section 在这里起作用
  19.         if i+1 > x: #如果i+1要等于x 或者小于x 但这里基本上都是等于x
  20.             break
  21.         if up_down == 0:  #up_down如果大于0 那么这个轨迹就是y增轨迹
  22.             if i >frist_count:
  23.                 if count==0:
  24.                     y += random.randint(0, 1)
  25.                     count +=1
  26.                 if flag>random.randint(8,10):
  27.                     count = 0
  28.                     flag = 0
  29.                 if i + 1 > int(x / 5)*4:
  30.                     time += random.randint(20, 70)
  31.                 elif i+1 > x-3:
  32.                     time += random.randint(80, 180)
  33.                 else:
  34.                     time += random.randint(0,5)
  35.                 slide_track.append([i+1,y,time])
  36.                 flag+=1
  37.                 if random.randint(0,1):
  38.                     if repetition:
  39.                         slide_track.append([i + 1, y, time+random.randint(0,3)])
  40.                         flag += 1
  41.                         repetition -= 1
  42.             else:  #前面几个数组y都为0
  43.                 time += random.randint(0, 5)
  44.                 slide_track.append([i + 1, y, time])
  45.                 if random.randint(0,1):
  46.                     if repetition:
  47.                         slide_track.append([i + 1, y, time+random.randint(0,3)])
  48.                         repetition -= 1
  49.         if up_down == 1:  #up_down如果小于0 那么这个轨迹就是y减轨迹
  50.             if i > frist_count:
  51.                 if count==0:
  52.                     y -= random.randint(0, 1)
  53.                     count +=1
  54.                 if flag>random.randint(8,10):
  55.                     count = 0
  56.                     flag = 0
  57.                 if i + 1 > int(x / 5)*4:
  58.                     time += random.randint(7, 40)
  59.                 elif i+1 > x-3:
  60.                     time += random.randint(80, 180)
  61.                 else:
  62.                     time += random.randint(0, 5)
  63.                 slide_track.append([i+1,y,time])
  64.                 flag +=1
  65.                 if random.randint(0,1):
  66.                     if repetition:
  67.                         slide_track.append([i + 1, y, time+random.randint(0,3)])
  68.                         flag += 1
  69.                         repetition -= 1
  70.             else:
  71.                 time += random.randint(0, 5)
  72.                 slide_track.append([i + 1, y, time])
  73.                 if random.randint(0,1):
  74.                     if repetition:
  75.                         slide_track.append([i + 1, y, time+random.randint(0,3)])
  76.                         repetition -= 1
  77.     return slide_track
复制代码
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册