Python+Node.js模拟执行绕过腾讯云滑块验证码collect参数校验

发布时间:2026/7/19 5:20:06
Python+Node.js模拟执行绕过腾讯云滑块验证码collect参数校验 1. 项目概述当滑块验证码遇上自动化脚本做爬虫或者自动化测试的朋友对滑块验证码肯定不陌生。它像一道数字世界的旋转门把“真人”和“机器”区分开来。而腾讯云的滑块验证码以其复杂的交互逻辑和动态加密参数一度让很多自动化脚本“望码兴叹”。今天要聊的这个实战项目核心目标就是绕过腾讯云滑块验证码中一个关键的校验环节——collect参数。这不是一个教你“破解”验证码的教程而是从一个非常规的“取巧”角度探讨在特定场景下如何通过Python和JavaScript的配合模拟出近乎真实的用户滑动行为从而让服务器“相信”这是一次合法的人类操作。这个思路的诞生源于一个实际的业务需求我们需要对一个部署在腾讯云防护下的后台进行高频次的、合法的数据监控。每次登录都需要手动滑动验证码显然不现实。完全逆向其整套加密算法工程浩大且随着对方升级会频繁失效。于是我们把目光聚焦在了滑动验证请求的最后一个环节——提交滑动轨迹和验证结果的那个collect参数上。我们发现如果能“欺骗”过这个参数的校验就能以极低的成本实现自动化通过。整个方案的核心就是用Python作为调度中心调用一个“头号功臣”——一个经过特殊处理的JavaScript环境比如PyExecJS或Node.js来生成能够通过校验的collect参数值。2. 核心思路拆解为什么是collect参数要理解这个“取巧”思路首先得明白腾讯云滑块验证码以常见的腾讯云防水墙Captcha为例的一次完整验证流程。它大致分为几个阶段初始化获取验证码图片和参数、前端渲染并收集用户滑动行为、后端验证滑动轨迹。而collect参数通常出现在最后一步“提交验证”的请求中它是一个经过多重加密和混淆的字符串里面打包了本次滑动的核心“证据”比如滑动轨迹、时间戳、验证码会话ID、以及一些防伪的加密签名。2.1 传统逆向的难点传统的完全逆向思路是去剖析前端JavaScript代码找到生成collect参数的完整算法链包括各种AES、RSA加密Base64编码以及可能存在的代码混淆和动态执行。这条路走通后固然一劳永逸但面临几个严峻挑战代码混淆与动态加载腾讯云的前端JS代码混淆程度高变量名、函数名难以阅读且关键逻辑可能被动态生成或延迟加载。环境依赖检测生成算法可能依赖浏览器特有的环境变量如window、document、navigator等在纯Node.js或Python环境中直接执行会报错。快速迭代与对抗一旦你的逆向算法被识别对方可以快速更换加密密钥或调整算法结构导致你的脚本短期内失效维护成本高。2.2 “取巧”思路的突破口我们的“取巧”思路正是为了规避上述难点。我们不追求完全逆向整个加密算法而是采用“模拟执行”的策略。核心思想是在一个受控的、能模拟浏览器基础环境JavaScript引擎中直接运行腾讯云验证码前端用于生成collect参数的那部分原始JS代码。这听起来有点“暴力”但逻辑是成立的。既然浏览器能执行这些代码并生成合法的collect那么我们创造一个具备类似能力的JS执行环境喂给它正确的输入如滑动轨迹数组、会话ID等理论上它就能输出同样合法的collect值。这个方法的优势很明显绕过算法分析我们不需要理解collect具体是怎么算出来的只需要知道如何调用生成它的函数。抗变更能力强只要前端生成collect的核心JS函数名和调用方式没有大变我们的脚本就依然有效。算法内部的升级对我们透明。开发效率高重点从“逆向密码学”转向了“环境模拟和函数调用”技术栈更集中。当然难点也随之而来如何从海量混淆的JS代码中定位到那个关键的生成函数如何构造一个足以“骗过”这段JS代码的执行环境3. 环境准备与关键JS代码提取工欲善其事必先利其器。这个项目的成功一半取决于前期对目标网站的分析和关键资源的提取。3.1 工具链选择Python 3.7: 项目主语言负责网络请求调度、数据处理和逻辑控制。Requests库: 用于发送HTTP请求获取验证码初始化参数和提交最终验证。浏览器开发者工具 (Chrome DevTools): 这是我们的“眼睛”。主要使用Network网络面板和Sources源代码面板。JS执行环境这里有两个主流选择各有优劣PyExecJS: Python库可以调用系统安装的JavaScript引擎如Node.js。优点是集成在Python中调用方便。缺点是性能一般且某些非常新的JS特性或浏览器特有对象支持可能不全。直接调用Node.js: 通过Python的subprocess模块调用独立的Node.js进程执行JS代码。优点是执行环境纯净、性能好、支持最新的ECMAScript标准。缺点是交互稍复杂需要处理进程间通信。 对于腾讯云滑块这种可能涉及复杂环境检测的我更推荐使用Node.js方案因为它更容易模拟和补全必要的浏览器对象。3.2 定位并提取生成collect参数的JS代码这是整个项目最考验耐心和技巧的环节。录制一次完整的验证过程打开目标网站进入验证码触发页面。开启开发者工具的Network面板勾选Preserve log保留日志。手动完成一次成功的滑块验证。找到提交验证的请求在Network面板中过滤XHR或Fetch请求。寻找一个在滑动完成后发出的、包含collect参数的请求。它的URL可能包含verify、validate、check等关键词。点击这个请求在Headers或Payload里找到collect参数它通常是一长串看似随机的字符。逆向查找调用栈在该请求的Initiator发起者标签页可以查看是哪个JS文件、哪行代码发起了这个请求。点击调用栈逐步向上回溯直到找到一个看起来像是封装了生成collect逻辑的函数。这个函数可能被命名为getCollect、encrypt、serialize等但在混淆代码中更可能是一个匿名函数或单字母变量。提取关键代码块找到疑似函数后需要将其连同其所有依赖一起提取出来。在Sources面板中找到这个JS文件使用{}美化代码功能让代码可读。然后你需要仔细分析函数定义找到生成collect的核心函数。依赖变量这个函数内部使用了哪些外部变量或函数这些变量可能来自更上层的闭包或者全局对象如window、Object等。外部依赖是否引入了其他JS库或模块通常腾讯云的验证码会有一个主JS文件我们需要的是这个文件的大部分或全部内容因为内部函数相互引用非常紧密。一个实用的技巧尝试将整个主要的、混淆的JS文件可能叫captcha.js或一串数字.js保存到本地。然后写一个简单的Node.js脚本去require这个文件并尝试通过全局对象比如你分析出的函数名去调用它。如果调用失败通常是因为缺少浏览器环境。这时我们需要进行“环境补全”。3.3 构建Node.js模拟环境浏览器中的JS代码运行在拥有window、document、location、navigator等丰富对象的沙箱中。我们的Node.js环境是“裸”的需要手动补全这些对象让提取的JS代码能够正常运行而不报错。创建一个名为mock_browser.js的文件// mock_browser.js - 模拟浏览器全局环境 const jsdom require(jsdom); const { JSDOM } jsdom; // 使用jsdom创建一个虚拟的window对象 const dom new JSDOM(!DOCTYPE htmlhtmlbody/body/html, { url: https://目标网站域名.com, // 这里替换成实际目标网站的域名 pretendToBeVisual: true, resources: usable }); const window dom.window; const document window.document; const location window.location; const navigator window.navigator; // 将模拟的全局对象挂载到global使得被执行的混淆JS能访问到 global.window window; global.document document; global.location location; global.navigator navigator; global.HTMLElement window.HTMLElement; // 可能需要的DOM类 global.XMLHttpRequest window.XMLHttpRequest; // 如果代码中有Ajax请求通常会被拦截重写 // 补全一些常见的、但jsdom可能没有完全模拟的对象或属性 navigator.userAgent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36; navigator.platform Win32; navigator.language zh-CN; // 注意腾讯云验证码JS可能会检测更细的属性如 screen.width/height, devicePixelRatio 等。 // 如果遇到相关错误需要在这里继续补全。 // global.screen { width: 1920, height: 1080 }; // global.devicePixelRatio 1; // 导出这些对象供主脚本使用 module.exports { window, document, navigator };然后在你的主执行文件例如generate_collect.js中// generate_collect.js require(./mock_browser.js); // 首先引入模拟环境 // 然后引入我们提取的、混淆的腾讯云验证码核心JS文件 // 假设我们将其保存为 tc_captcha_core.js并稍微修改了它的结尾将其导出为一个全局函数 window.getEncryptedCollect require(./tc_captcha_core.js); // 现在我们可以从全局window对象上访问到关键函数 function generateCollect(slideTrack, captchaId, otherParams) { // slideTrack: 模拟的滑动轨迹数组例如 [{x:10, y:20, t:100}, ...] // captchaId: 从初始化请求中获取的验证码会话ID // otherParams: 其他必要的参数如csrf token等需从初始化响应中提取 try { // 调用混淆JS中暴露的函数。这里的函数名‘window.xxxx’需要你在分析代码时确定 const collectParam window.getEncryptedCollect(slideTrack, captchaId, otherParams); return collectParam; } catch (error) { console.error(生成collect参数失败:, error); return null; } } // 导出函数供Python调用 module.exports { generateCollect };实操心得环境补全是这个环节最大的“坑”。错误信息是唯一的向导。当Node.js执行报错“XXX is not defined”时你就需要去mock_browser.js里补全这个XXX。它可能是window的一个属性也可能是某个浏览器特有的API。这个过程需要反复试验。一个更高效的方法是在浏览器控制台里在执行生成collect的代码行之前打印出Object.keys(window)看看有哪些关键对象然后在模拟环境里选择性添加。4. Python与JS的协同模拟滑动与参数生成有了能生成collect的JS引擎Python端的工作就是当好“导演”协调整个验证流程。4.1 整体流程设计我们的Python脚本将按以下步骤工作初始化会话使用requests.Session()保持Cookie连贯性。获取验证码参数向验证码初始化接口发送请求获取包含captchaId或appid、nonce、sig等关键参数的响应。同时会获得背景图和缺口图的URL。图片下载与缺口识别下载图片使用图像处理库如OpenCV或深度学习模型识别缺口位置。这里假设我们已经通过其他方式如模板匹配得到了需要滑动的像素距离distance。生成模拟滑动轨迹这是“模拟人类”的关键。不能简单地将滑块从0匀速移动到distance。需要生成一个包含加速度、减速、轻微抖动和停顿的轨迹数组。每个点包含x坐标水平位置、y坐标垂直偏移模拟手抖、和时间戳t。调用JS引擎生成collect将轨迹数组、captchaId以及其他必要参数传递给我们的Node.js脚本或PyExecJS封装函数得到加密后的collect字符串。提交验证将collect参数与其他必要参数一起构造表单数据或JSON发送到验证接口。处理结果解析验证接口的返回判断是否成功。4.2 模拟人类滑动轨迹的生成一个简单的、但相对有效的轨迹生成函数示例import random import time def generate_slide_track(distance_pixels): 生成模拟人类滑动的轨迹数组。 :param distance_pixels: 需要滑动的总像素距离 :return: 轨迹列表每个元素为 {x: int, y: int, t: int} track [] current_x 0 current_y 0 # 初始垂直偏移为0 current_t int(time.time() * 1000) # 起始时间戳毫秒 # 分段模拟加速、匀速、减速 # 加速段 (约30%路程) accelerate_distance int(distance_pixels * 0.3) for i in range(accelerate_distance): current_t random.randint(15, 25) # 时间间隔模拟速度变化 current_x 1 # 模拟轻微的手部垂直抖动 current_y random.randint(-2, 2) track.append({x: current_x, y: current_y, t: current_t}) # 匀速段 (约50%路程) uniform_distance int(distance_pixels * 0.5) for i in range(uniform_distance): current_t random.randint(18, 22) current_x 1 current_y random.randint(-1, 1) track.append({x: current_x, y: current_y, t: current_t}) # 减速段 (剩余路程) decelerate_distance distance_pixels - len(track) for i in range(decelerate_distance): # 越接近终点时间间隔越长模拟减速 interval random.randint(25, 35) i * 0.5 current_t int(interval) current_x 1 # 终点附近抖动减小 current_y random.randint(-1, 1) track.append({x: current_x, y: current_y, t: current_t}) # 最后确保终点x坐标精确等于目标距离并可能有一个微小的回弹或过冲人类操作常见 if track[-1][x] ! distance_pixels: # 简单调整最后一个点的x值 track[-1][x] distance_pixels # 或者添加一个“过冲-回调”的点 # track.append({x: distance_pixels random.randint(-3, 0), y: track[-1][y], t: current_t random.randint(30,50)}) return track注意事项轨迹的逼真度直接影响成功率。不同的验证码系统可能对轨迹的“人性化”模型有不同的侧重点。有的关注加速度曲线有的关注总时间有的会检测轨迹点的均匀性。上述代码只是一个基础模板你可能需要根据实际验证结果失败时的返回码或日志来调整随机数范围、分段比例和时间间隔。一个高级技巧是“录制-回放”用浏览器插件录制几次真实人手滑动的鼠标事件坐标和时间分析其模式然后用算法拟合出更逼真的轨迹。4.3 Python调用Node.js执行JS这里展示通过subprocess调用Node.js的稳健方法import subprocess import json class CollectGenerator: def __init__(self, node_js_pathnode, js_script_pathgenerate_collect.js): self.node_js_path node_js_path self.js_script_path js_script_path def generate(self, slide_track, captcha_id, other_params_dict): 调用Node.js脚本生成collect参数。 :param slide_track: 轨迹列表 :param captcha_id: 验证码ID :param other_params_dict: 其他参数字典 :return: collect字符串 或 None # 构造输入数据 input_data { track: slide_track, captchaId: captcha_id, otherParams: other_params_dict } try: # 启动Node.js进程将输入数据通过stdin传入 process subprocess.Popen( [self.node_js_path, self.js_script_path], stdinsubprocess.PIPE, stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue, # 使用文本模式 encodingutf-8 ) # 发送数据 stdout, stderr process.communicate(inputjson.dumps(input_data)) # 等待进程结束 return_code process.wait() if return_code 0: # 假设Node.js脚本将结果以JSON格式输出到stdout如 {collect: xxx} result json.loads(stdout.strip()) return result.get(collect) else: print(fNode.js脚本执行错误返回码{return_code}: {stderr}) return None except Exception as e: print(f调用JS引擎失败: {e}) return None # 使用示例 if __name__ __main__: generator CollectGenerator() fake_track generate_slide_track(250) captcha_id 从初始化请求获取的id other_params {nonce: xxx, sig: yyy} collect_str generator.generate(fake_track, captcha_id, other_params) print(f生成的collect: {collect_str})对应的Node.js脚本 (generate_collect.js) 需要稍作修改以支持从stdin读取参数// generate_collect.js (修改版) require(./mock_browser.js); require(./tc_captcha_core.js); function main() { // 从标准输入读取Python传递过来的JSON数据 let inputData ; process.stdin.on(data, chunk { inputData chunk; }); process.stdin.on(end, () { try { const params JSON.parse(inputData); const { track, captchaId, otherParams } params; // 调用核心函数 const collect window.getEncryptedCollect(track, captchaId, otherParams); // 将结果以JSON格式输出到标准输出 process.stdout.write(JSON.stringify({ collect })); } catch (error) { process.stderr.write(JSON.stringify({ error: error.message })); process.exit(1); } }); } if (require.main module) { main(); }5. 实战整合与请求模拟现在我们将所有模块整合到一个完整的Python自动化流程中。5.1 完整的Python自动化脚本框架import requests import json import time import cv2 import numpy as np from collect_generator import CollectGenerator # 导入我们上面写的JS调用类 from track_generator import generate_slide_track # 导入轨迹生成函数 class TencentSlideCaptchaSolver: def __init__(self, target_url, sessionNone): self.session session or requests.Session() self.target_url target_url # 初始化JS参数生成器 self.collect_gen CollectGenerator() # 从实际请求中分析出的关键API端点以下为示例需替换 self.api_init https://captcha.tencent.com/api/v1/init self.api_verify https://captcha.tencent.com/api/v1/verify self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: self.target_url, } def get_captcha_params(self): 步骤1获取验证码初始化参数 init_payload { aid: 你的应用ID, # 通常固定 AppSecretKey: 你的密钥, # 可能不需要在前端但服务端需要 callback: callback123, # 可能用于JSONP # 其他必要参数通过分析浏览器请求获得 } resp self.session.post(self.api_init, datainit_payload, headersself.headers) # 解析响应可能是JSON或JSONP # 这里需要根据实际响应格式处理提取captcha_id, nonce, sig等 # 示例 # result json.loads(resp.text.strip(callback123().rstrip())) # captcha_id result[data][captcha_id] # return result[data] raise NotImplementedError(请根据实际接口实现解析逻辑) def download_and_recognize(self, bg_url, slice_url): 步骤23下载图片并识别缺口距离简化示例实际更复杂 # 下载背景图和滑块图 bg_resp self.session.get(bg_url) slice_resp self.session.get(slice_url) bg_img cv2.imdecode(np.frombuffer(bg_resp.content, np.uint8), cv2.IMREAD_COLOR) slice_img cv2.imdecode(np.frombuffer(slice_resp.content, np.uint8), cv2.IMREAD_COLOR) # 使用OpenCV进行模板匹配这是最简单的方法实际中可能被对抗 result cv2.matchTemplate(bg_img, slice_img, cv2.TM_CCOEFF_NORMED) _, _, _, max_loc cv2.minMaxLoc(result) distance max_loc[0] # 缺口左上角的x坐标即为需要滑动的距离 # 注意实际距离可能需要缩放因为前端显示的图片大小可能与下载的原图不同 return distance def solve(self): 主解决流程 # 1. 初始化 print([*] 正在获取验证码参数...) init_data self.get_captcha_params() captcha_id init_data[captcha_id] bg_url init_data[bg_url] slice_url init_data[slice_url] other_params {k: v for k, v in init_data.items() if k not in [captcha_id, bg_url, slice_url]} # 2. 识别距离 print([*] 正在下载图片并识别缺口...) slide_distance self.download_and_recognize(bg_url, slice_url) print(f[*] 识别出的滑动距离: {slide_distance} 像素) # 3. 生成轨迹 print([*] 正在生成模拟滑动轨迹...) slide_track generate_slide_track(slide_distance) # 4. 生成collect参数 print([*] 正在调用JS引擎生成加密参数...) collect_str self.collect_gen.generate(slide_track, captcha_id, other_params) if not collect_str: print([!] 生成collect参数失败) return False # 5. 提交验证 print([*] 正在提交验证请求...) verify_payload { captcha_id: captcha_id, collect: collect_str, other_param1: other_params.get(param1), # ... 其他必要参数 } verify_resp self.session.post(self.api_verify, dataverify_payload, headersself.headers) verify_result json.loads(verify_resp.text) # 6. 处理结果 if verify_result.get(code) 0 or verify_result.get(success): print([] 验证码通过) # 通常这里会返回一个验证成功的ticket用于后续业务请求 ticket verify_result.get(ticket) return ticket else: print(f[-] 验证失败: {verify_result}) return False if __name__ __main__: solver TencentSlideCaptchaSolver(https://你的目标网站.com) ticket solver.solve() if ticket: print(f获得的Ticket: {ticket}) # 使用这个ticket继续你的业务逻辑...5.2 关键请求参数与响应处理在实际操作中get_captcha_params和提交验证的verify_payload构造是最容易出错的地方。你必须使用浏览器开发者工具仔细比对你的脚本发出的请求和浏览器发出的请求确保每个参数名、参数值、请求头如Content-Type、Origin、Referer都完全一致。参数来源aid、AppSecretKey等可能硬编码在JS中或由后端首次渲染时生成。nonce、sig、captcha_id等通常由初始化接口返回。动态参数有些参数可能是当前时间戳、随机数需要模拟生成。Cookie与Session务必使用requests.Session()来保持会话很多验证码的captcha_id是和Session绑定的。6. 常见问题排查与进阶优化即使按照上述流程你也可能会遇到各种问题。下面是一些常见坑点和排查思路。6.1 常见错误与解决方案问题现象可能原因排查与解决思路Node.js脚本报错XXX is not defined浏览器环境模拟不全。1. 在浏览器控制台执行相关代码前查看XXX是什么如window.abc。2. 在mock_browser.js中将abc挂载到global.window对象上例如global.window.abc {}。3. 如果XXX是一个函数可能需要从混淆代码中分析其来源并导出。生成的collect参数提交后返回“签名错误”或“参数无效”1. 输入参数不对。2. 轨迹数据格式不符。3. JS执行环境差异导致加密结果微变。1.核对所有输入确保传给JS函数的captcha_id、nonce、sig等与浏览器请求中的完全一致。2.轨迹格式确保轨迹数组的结构键名x,y,t和数据类型数字与前端代码期望的一致。用浏览器调试工具捕获一次真实轨迹进行对比。3.环境补全检查是否缺少Math.random、Date.now等函数的模拟某些加密算法依赖这些随机源。可以在Node.js中重写它们以确保确定性但可能被检测。更安全的方法是确保环境足够真实。验证成功率低如10%-30%滑动轨迹被识别为机器行为。1.优化轨迹算法使用更复杂的人类行为模型加入随机停顿、小幅回滑、变速曲线。2.增加轨迹点浏览器采集的鼠标轨迹点非常密集确保你的轨迹点数量足够多例如距离250像素轨迹点应有150-250个。3.引入“学习”机制记录失败和成功的轨迹分析其特征差异。初始化请求失败或被拒绝请求头、Cookie或风控检测。1.完善请求头补全User-Agent、Accept、Accept-Language、Accept-Encoding等。2.处理Cookie在初始化前可能需要先访问一下主页面获取必要的Cookie。3.模拟更早的请求有些验证码在初始化前页面会发送一个report或collect此collect非彼collect请求来收集设备指纹需要一并模拟。6.2 进阶优化思路设备指纹模拟腾讯云验证码很可能收集浏览器指纹Canvas, WebGL, Fonts, AudioContext等。虽然我们的“取巧”思路不直接处理这些但初始化请求可能携带了基于这些指纹生成的参数。你可以使用像pyppeteer或selenium控制一个无头浏览器来获取一次完整的初始化参数和指纹信息然后用在我们的requestsNode.js方案中实现“半无头”模式提高隐蔽性。JS代码动态更新监控目标网站的JS可能会更新。可以写一个简单的监控脚本定期下载主要的验证码JS文件计算其哈希值。如果哈希值变化则触发告警提醒你需要重新分析代码。容错与重试机制在网络请求、JS调用等环节加入重试逻辑。当验证失败时不是直接放弃而是分析返回的错误码如果是轨迹问题可以微调轨迹参数后重试。分布式与池化管理如果需要极高的通过率或应对更严格的风控可以考虑使用IP代理池、以及管理多个不同的轨迹生成配置文件模拟不同用户的操作习惯轮流使用。这个“取巧”的思路本质是在安全攻防的夹缝中寻找一个自动化的平衡点。它不追求绝对的技术胜利而是追求在特定场景下稳定、可维护的解决方案。它要求你对前端JS调试、网络请求分析有扎实的功底同时也考验你的耐心和解决问题的能力。记住任何自动化绕过验证码的行为都应确保在服务条款允许和合法合规的范围内进行用于学习与研究目的。