1. 极验点选验证码逆向流程概述B站登录环节采用的极验点选验证码本质上是通过用户交互行为来区分真人操作和机器请求的风控手段。这套系统会动态生成包含随机文字的图片要求用户在指定时间内点击图中文字的正确顺序。作为爬虫开发者我们需要破解三个核心环节验证码图片获取、坐标定位算法、以及最终提交的w参数加密逻辑。整个逆向过程可以拆解为以下关键步骤从B站接口获取初始化的gt和challenge参数请求极验服务器获取验证码图片及加密因子通过图像识别或人工标注获取点击坐标构造包含轨迹加密的w参数提交验证结果获取登录凭证实测发现极验4代相比3代主要变化在于w参数的生成逻辑更加复杂增加了RSA加密环节和动态密钥机制。不过核心逆向思路仍然是抓包分析 - 关键参数定位 - 加密逻辑还原的技术路线。2. 验证码初始化参数获取首先需要从B站接口获取验证码的初始化参数。通过抓包分析可以发现访问以下接口会返回关键数据def get_challenge(self): url https://passport.bilibili.com/x/passport-login/captcha params { source: main-fe-header, t: str(int(time.time()*1000)) } req self.session.get(urlurl, paramsparams, headersself.headers) data json.loads(req.text) return data.get(data)[token], data[data][geetest][challenge], data[data][geetest][gt]这里返回的三个值非常重要tokenB站本次验证会话的唯一标识challenge极验验证码的动态校验码gt极验验证码的业务标识符在实际项目中我遇到过challenge参数失效的问题。经过调试发现这个参数的有效期只有60秒超时后需要重新获取。建议在代码中加入时间戳校验逻辑。3. 验证码图片与加密因子获取拿到基础参数后需要请求极验的接口获取验证码图片def get_all_info(self): token, challenge, gt self.get_challenge() url https://api.geetest.com/get.php params { is_next: true, type: click, gt: gt, challenge: challenge, lang: zh-cn, https:: false, # 其他必要参数... } req self.session.get(urlurl, paramsparams, headersself.headers) data json.loads(re.findall(r\((.*?)\), req.text)[0])[data] return data[c], data[s], data[pic] # 返回加密因子和图片URL这个接口会返回三个关键数据c和s用于后续w参数加密的动态因子pic验证码图片的CDN地址这里有个坑点极验服务器会对请求频率做限制。我实测发现单个IP连续请求超过5次就会触发验证建议在代码中加入随机延迟和错误重试机制。4. 点选坐标识别方案获取验证码图片后需要识别出需要点击的文字位置。目前主要有三种技术方案4.1 人工标注方案最直接的方式是通过OpenCV显示图片并记录点击坐标def get_pic_xy(self, pic_url): img cv2.imread(temp.jpg) click_points [] def on_mouse_click(event, x, y, flags, param): if event cv2.EVENT_LBUTTONDOWN: click_points.append((x, y)) cv2.circle(img, (x, y), 5, (0,0,255), -1) cv2.imshow(image, img) cv2.namedWindow(image) cv2.setMouseCallback(image, on_mouse_click) cv2.imshow(image, img) cv2.waitKey(0) return click_points这种方案虽然准确率高但无法实现自动化。适合在开发阶段用于验证其他环节的正确性。4.2 深度学习识别方案使用YOLOv5等目标检测模型训练自定义的文字检测模型。需要准备以下步骤收集至少500张极验验证码图片使用LabelImg等工具标注文字位置训练目标检测模型部署模型到生产环境这种方案成本较高但可以全自动化运行。我在实际项目中发现对于简单的文字点选准确率能达到85%以上。4.3 第三方OCR服务使用ddddocr等开源库实现文字识别import ddddocr ocr ddddocr.DdddOcr() with open(captcha.jpg, rb) as f: image f.read() results ocr.detection(image)实测发现ddddocr对简单文字的识别效果不错但对于复杂背景的验证码准确率会下降。建议配合图像预处理二值化、去噪等提升识别率。5. w参数加密逻辑解析w参数是极验验证的核心加密参数包含以下关键信息用户点击坐标操作时间戳设备指纹信息加密随机数通过逆向分析极验的JS代码可以还原出w参数的生成逻辑// 伪代码示例 function generate_w(gt, challenge, c, s, click_points) { // 1. 生成16位随机字符串 const random_str generate_random(16); // 2. RSA加密随机字符串 const rsa_encrypted rsa_encrypt(random_str, public_key); // 3. 构造轨迹参数 const track_params { passtime: 操作耗时, userresponse: 点击坐标, aa: 轨迹加密字符串, // 其他设备参数... }; // 4. AES加密轨迹参数 const aes_encrypted aes_encrypt(JSON.stringify(track_params), random_str); // 5. 最终拼接 return rsa_encrypted aes_encrypted; }在实际逆向过程中需要重点关注以下两个加密函数RSA加密使用固定的公钥模数可在JS中找到AES加密采用CBC模式IV固定为16个06. 完整验证流程实现将上述环节串联起来完整的验证码破解流程如下class GeetestSolver: def __init__(self): self.session requests.Session() # 初始化其他参数... def solve_captcha(self): # 1. 获取初始化参数 token, challenge, gt self._get_init_params() # 2. 获取验证码图片和加密因子 c, s, pic_url self._get_captcha_image(gt, challenge) # 3. 下载图片并识别坐标 click_points self._recognize_points(pic_url) # 4. 生成w参数 w self._generate_w(gt, challenge, c, s, click_points) # 5. 提交验证 validate self._submit_verify(gt, challenge, w) return validate, token在具体实现时我建议注意以下几点使用会话保持技术确保所有请求在同一个TCP连接中添加随机延迟模拟人类操作间隔对关键环节添加日志记录方便调试实现参数缓存机制避免重复获取7. 常见问题与解决方案在实际开发中可能会遇到以下典型问题7.1 验证总是失败可能原因w参数加密逻辑错误坐标未做比例转换时间戳超出有效期解决方案使用浏览器调试工具抓取正常请求的w参数对比自己生成的w参数差异检查坐标转换算法是否正确7.2 请求频率受限现象返回412状态码出现risk control错误提示解决方案降低请求频率添加随机延迟轮换代理IP模拟完整浏览器指纹7.3 坐标识别不准优化建议对图片进行灰度化二值化预处理使用CNN模型替代传统OCR添加多识别结果投票机制8. 进阶优化方向对于需要更高通过率的场景可以考虑以下优化设备指纹模拟完整还原浏览器navigator对象的所有属性轨迹模拟使用贝塞尔曲线生成更真实的鼠标移动路径多识别引擎融合结合多个OCR引擎的结果提高准确率验证码分类针对不同类型的验证码使用不同识别策略我在实际项目中验证过通过综合运用这些技术可以将验证通过率提升到90%以上。不过要注意极验会不定期更新验证算法需要持续跟踪最新的变化。
B站极验点选验证码逆向实战:从JS解密到坐标生成
1. 极验点选验证码逆向流程概述B站登录环节采用的极验点选验证码本质上是通过用户交互行为来区分真人操作和机器请求的风控手段。这套系统会动态生成包含随机文字的图片要求用户在指定时间内点击图中文字的正确顺序。作为爬虫开发者我们需要破解三个核心环节验证码图片获取、坐标定位算法、以及最终提交的w参数加密逻辑。整个逆向过程可以拆解为以下关键步骤从B站接口获取初始化的gt和challenge参数请求极验服务器获取验证码图片及加密因子通过图像识别或人工标注获取点击坐标构造包含轨迹加密的w参数提交验证结果获取登录凭证实测发现极验4代相比3代主要变化在于w参数的生成逻辑更加复杂增加了RSA加密环节和动态密钥机制。不过核心逆向思路仍然是抓包分析 - 关键参数定位 - 加密逻辑还原的技术路线。2. 验证码初始化参数获取首先需要从B站接口获取验证码的初始化参数。通过抓包分析可以发现访问以下接口会返回关键数据def get_challenge(self): url https://passport.bilibili.com/x/passport-login/captcha params { source: main-fe-header, t: str(int(time.time()*1000)) } req self.session.get(urlurl, paramsparams, headersself.headers) data json.loads(req.text) return data.get(data)[token], data[data][geetest][challenge], data[data][geetest][gt]这里返回的三个值非常重要tokenB站本次验证会话的唯一标识challenge极验验证码的动态校验码gt极验验证码的业务标识符在实际项目中我遇到过challenge参数失效的问题。经过调试发现这个参数的有效期只有60秒超时后需要重新获取。建议在代码中加入时间戳校验逻辑。3. 验证码图片与加密因子获取拿到基础参数后需要请求极验的接口获取验证码图片def get_all_info(self): token, challenge, gt self.get_challenge() url https://api.geetest.com/get.php params { is_next: true, type: click, gt: gt, challenge: challenge, lang: zh-cn, https:: false, # 其他必要参数... } req self.session.get(urlurl, paramsparams, headersself.headers) data json.loads(re.findall(r\((.*?)\), req.text)[0])[data] return data[c], data[s], data[pic] # 返回加密因子和图片URL这个接口会返回三个关键数据c和s用于后续w参数加密的动态因子pic验证码图片的CDN地址这里有个坑点极验服务器会对请求频率做限制。我实测发现单个IP连续请求超过5次就会触发验证建议在代码中加入随机延迟和错误重试机制。4. 点选坐标识别方案获取验证码图片后需要识别出需要点击的文字位置。目前主要有三种技术方案4.1 人工标注方案最直接的方式是通过OpenCV显示图片并记录点击坐标def get_pic_xy(self, pic_url): img cv2.imread(temp.jpg) click_points [] def on_mouse_click(event, x, y, flags, param): if event cv2.EVENT_LBUTTONDOWN: click_points.append((x, y)) cv2.circle(img, (x, y), 5, (0,0,255), -1) cv2.imshow(image, img) cv2.namedWindow(image) cv2.setMouseCallback(image, on_mouse_click) cv2.imshow(image, img) cv2.waitKey(0) return click_points这种方案虽然准确率高但无法实现自动化。适合在开发阶段用于验证其他环节的正确性。4.2 深度学习识别方案使用YOLOv5等目标检测模型训练自定义的文字检测模型。需要准备以下步骤收集至少500张极验验证码图片使用LabelImg等工具标注文字位置训练目标检测模型部署模型到生产环境这种方案成本较高但可以全自动化运行。我在实际项目中发现对于简单的文字点选准确率能达到85%以上。4.3 第三方OCR服务使用ddddocr等开源库实现文字识别import ddddocr ocr ddddocr.DdddOcr() with open(captcha.jpg, rb) as f: image f.read() results ocr.detection(image)实测发现ddddocr对简单文字的识别效果不错但对于复杂背景的验证码准确率会下降。建议配合图像预处理二值化、去噪等提升识别率。5. w参数加密逻辑解析w参数是极验验证的核心加密参数包含以下关键信息用户点击坐标操作时间戳设备指纹信息加密随机数通过逆向分析极验的JS代码可以还原出w参数的生成逻辑// 伪代码示例 function generate_w(gt, challenge, c, s, click_points) { // 1. 生成16位随机字符串 const random_str generate_random(16); // 2. RSA加密随机字符串 const rsa_encrypted rsa_encrypt(random_str, public_key); // 3. 构造轨迹参数 const track_params { passtime: 操作耗时, userresponse: 点击坐标, aa: 轨迹加密字符串, // 其他设备参数... }; // 4. AES加密轨迹参数 const aes_encrypted aes_encrypt(JSON.stringify(track_params), random_str); // 5. 最终拼接 return rsa_encrypted aes_encrypted; }在实际逆向过程中需要重点关注以下两个加密函数RSA加密使用固定的公钥模数可在JS中找到AES加密采用CBC模式IV固定为16个06. 完整验证流程实现将上述环节串联起来完整的验证码破解流程如下class GeetestSolver: def __init__(self): self.session requests.Session() # 初始化其他参数... def solve_captcha(self): # 1. 获取初始化参数 token, challenge, gt self._get_init_params() # 2. 获取验证码图片和加密因子 c, s, pic_url self._get_captcha_image(gt, challenge) # 3. 下载图片并识别坐标 click_points self._recognize_points(pic_url) # 4. 生成w参数 w self._generate_w(gt, challenge, c, s, click_points) # 5. 提交验证 validate self._submit_verify(gt, challenge, w) return validate, token在具体实现时我建议注意以下几点使用会话保持技术确保所有请求在同一个TCP连接中添加随机延迟模拟人类操作间隔对关键环节添加日志记录方便调试实现参数缓存机制避免重复获取7. 常见问题与解决方案在实际开发中可能会遇到以下典型问题7.1 验证总是失败可能原因w参数加密逻辑错误坐标未做比例转换时间戳超出有效期解决方案使用浏览器调试工具抓取正常请求的w参数对比自己生成的w参数差异检查坐标转换算法是否正确7.2 请求频率受限现象返回412状态码出现risk control错误提示解决方案降低请求频率添加随机延迟轮换代理IP模拟完整浏览器指纹7.3 坐标识别不准优化建议对图片进行灰度化二值化预处理使用CNN模型替代传统OCR添加多识别结果投票机制8. 进阶优化方向对于需要更高通过率的场景可以考虑以下优化设备指纹模拟完整还原浏览器navigator对象的所有属性轨迹模拟使用贝塞尔曲线生成更真实的鼠标移动路径多识别引擎融合结合多个OCR引擎的结果提高准确率验证码分类针对不同类型的验证码使用不同识别策略我在实际项目中验证过通过综合运用这些技术可以将验证通过率提升到90%以上。不过要注意极验会不定期更新验证算法需要持续跟踪最新的变化。