破解验证码常常需要模拟复杂的环境和应用特定的算法。本文将详细介绍如何通过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}")