破解验证码详解:环境模拟与算法解析(Python实现)

破解验证码常常需要模拟复杂的环境和应用特定的算法。本文将详细介绍如何通过Python模拟验证码环境并理解关键算法,从而成功破解验证码。这些技术和技巧经过总结归纳,旨在帮助大家更好地理解和应用这些方法。更多内容联系1436423940

1. 模拟环境中的 window.crypto.getRandomValues()
在某些验证码中,window.crypto.getRandomValues() 被用于生成随机数。我们可以通过以下Python代码来模拟该方法:

python

import random

class Crypto:
    @staticmethod
    def get_random_values(buffer):
        min_val, max_val = 0, 255
        if len(buffer) > 65536:
            raise ValueError("Failed to execute 'getRandomValues': The ArrayBufferView's byte length exceeds the number of bytes of entropy available via this API (65536).")
        if isinstance(buffer, list):
            max_val = 4294967295
        for i in range(len(buffer)):
            buffer[i] = random.randint(min_val, max_val)
        return buffer

# 测试代码
buffer = [0] * 256
Crypto.get_random_values(buffer)
print(buffer)
2. 模拟 window.performance.timing
验证码还会使用 window.performance.timing 来获取一些性能指标。我们可以用以下Python代码来模拟这些性能数据:

python

import time

def performance_timing():
    now = int(time.time() * 1000)
    timing = {
        "navigationStart": now,
        "unloadEventStart": now + 200,
        "unloadEventEnd": now + 200,
        "redirectStart": 0,
        "redirectEnd": 0,
        "fetchStart": now + 100,
        "domainLookupStart": now + 150,
        "domainLookupEnd": now + 250,
        "connectStart": now + 30,
        "connectEnd": now + 50,
        "secureConnectionStart": now + 52,
        "requestStart": now + 72,
        "responseStart": now + 91,
        "responseEnd": now + 92,
        "domLoading": now + 99,
        "domInteractive": now + 105,
        "domContentLoadedEventStart": now + 105,
        "domContentLoadedEventEnd": now + 111,
        "domComplete": now + 111,
        "loadEventStart": now + 111,
        "loadEventEnd": now + 111
    }
    return timing

# 测试代码
print(performance_timing())
3. 动态获取 window.gct
某些验证码会动态生成一些随机键值对,这些值会被用于加密或其他校验。我们可以通过以下Python代码动态获取这些键值对:

python

import re
import execjs
import requests

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
}

# 获取 gct.js 文件内容
gct_path = "https://static.geetest.com/static/js/gct.b71a9027509bc6bcfef9fc6a196424f5.js"
gct_js = requests.get(gct_path, headers=headers).text

# 提取需要调用的方法名称
function_name = re.findall(r"\)\)\{return (.*?)\(", gct_js)[0]

# 查找插入全局导出代码的位置
break_position = gct_js.find("return function(t){")

# 修改 gct.js 内容以导出全局方法
gct_js_new = gct_js[:break_position] + "window.gct=" + function_name + ";" + gct_js[break_position:]
gct_js_new = "window = global;" + gct_js_new + """
function getGct(){
    var e = {"lang": "zh", "ep": "test data"};
    window.gct(e);
    delete e["lang"];
    delete e["ep"];
    return e;
}"""

# 执行修改后的 JS 代码获取键值对
gct = execjs.compile(gct_js_new).call("getGct")
print(gct)  # 输出动态生成的键值对
4. 破解滑块验证码的轨迹生成
滑块验证码需要模拟用户拖动滑块的轨迹。我们可以利用缓动函数生成逼真的轨迹,例如 easeOutExpo:

python

import random
import math

def ease_out_expo(x):
    """缓动函数 easeOutExpo"""
    return 1 if x == 1 else 1 - math.pow(2, -10 * x)

def get_slide_track(distance):
    """根据滑动距离生成滑动轨迹"""
    if distance < 0:
        raise ValueError("Distance must be a non-negative integer.")
    
    slide_track = [[random.randint(-50, -10), random.randint(-50, -10), 0], [0, 0, 0]]
    count = 30 + int(distance / 2)
    t = random.randint(50, 100)
    previous_x = 0

    for i in range(count):
        x = round(ease_out_expo(i / count) * distance)
        t += random.randint(10, 20)
        if x == previous_x:
            continue
        slide_track.append([x, 0, t])
        previous_x = x

    slide_track.append(slide_track[-1])
    return slide_track

# 测试代码
distance = 100
track = get_slide_track(distance)
print(track)
5. 验证码识别方法
验证码识别主要有三种方法:

深度学习:使用OpenCV等工具进行验证码识别。
第三方开源库:如 ddddocr。
打码平台:如云码打码。
以下是使用OpenCV识别滑块缺口的示例代码:

python

import cv2
import numpy as np
from PIL import Image

def imshow(img, winname='test', delay=0):
    """cv2展示图片"""
    cv2.imshow(winname, img)
    cv2.waitKey(delay)
    cv2.destroyAllWindows()

def pil_to_cv2(img):
    """pil转cv2图片"""
    img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
    return img

def bytes_to_cv2(img):
    """二进制图片转cv2"""
    img_buffer_np = np.frombuffer(img, dtype=np.uint8)
    img_np = cv2.imdecode(img_buffer_np, 1)
    return img_np

def cv2_open(img, flag=None):
    """统一输出图片格式为cv2图像"""
    if isinstance(img, bytes):
        img = bytes_to_cv2(img)
    elif isinstance(img, (str, Path)):
        img = cv2.imread(str(img))
    elif isinstance(img, np.ndarray):
        img = img
    elif isinstance(img, Image.Image):
        img = pil_to_cv2(img)
    else:
        raise ValueError(f'输入的图片类型无法解析: {type(img)}')
    if flag is not None:
        img = cv2.cvtColor(img, flag)
    return img

def get_distance(bg, tp, im_show=False, save_path=None):
    """计算滑块缺口位置"""
    bg_img = cv2_open(bg)
    tp_gray = cv2_open(tp, flag=cv2.COLOR_BGR2GRAY)
    bg_shift = cv2.pyrMeanShiftFiltering(bg_img, 5, 50)
    tp_gray = cv2.Canny(tp_gray, 255, 255)
    bg_gray = cv2.Canny(bg_shift, 255, 255)
    result = cv2.matchTemplate(bg_gray, tp_gray, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    distance = max_loc[0]
    if save_path or im_show:
        tp_height, tp_width = tp_gray.shape[:2]
        x, y = max_loc
        _x, _y = x + tp_width, y + tp_height
        bg_img = cv2_open(bg)
        cv2.rectangle(bg_img, (x, y), (_x, _y), (0, 0, 255), 2)
        if save_path:
            cv2.imwrite(save_path, bg_img)
        if im_show:
            imshow(bg_img)
    return distance

# 测试代码
bg_img_path = "background.png"
tp_img_path = "template.png"
distance = get_distance(bg_img_path, tp_img_path, im_show=True)
print(f"滑块缺口位置:{distance}")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值