逆向工程实战:破解AES加密滑块验证码的完整方案

📅 2026/7/14 19:00:02
逆向工程实战:破解AES加密滑块验证码的完整方案
1. 项目概述当滑块验证码遇上AES加密最近在做一个自动化项目时遇到了一个相当“顽固”的对手——一个采用了AES加密的滑块验证码。这玩意儿不像那些简单的图片缺口识别它的核心验证逻辑尤其是用户滑动轨迹的校验数据被一层AES加密牢牢包裹直接提交原始的滑动距离和轨迹数据是行不通的。这本质上是一场攻防演练防御方验证码服务试图通过加密来保护其核心验证算法而作为进攻方自动化脚本我们的目标就是逆向分析出它的加密密钥和模式从而能够模拟出合法的请求。这个过程就是一次典型的Web逆向工程实战。这个项目非常适合那些已经接触过基础爬虫和自动化但在遇到加密参数时感到无从下手的朋友。它不涉及任何灰色或非法操作其核心价值在于学习如何系统性地分析一个前端加密逻辑理解现代Web应用如何保护其接口并掌握一套可复用的逆向工程方法论。通过拆解这个案例你将能学到如何从纷繁的浏览器网络请求中定位关键接口如何一步步追踪JavaScript代码找到加密函数以及如何最终在Python环境中复现整个加密流程实现验证码的自动化破解。接下来我就把自己趟过的路、踩过的坑以及最终成功的方案毫无保留地分享给你。2. 逆向工程的核心思路与准备工作2.1 目标分析与技术栈选型我们的终极目标是让程序能自动完成滑块验证码的识别、滑动和结果提交。拆解开来需要解决三个子问题图像识别获取带缺口的背景图和滑块图计算出需要滑动的距离。轨迹模拟生成一个模拟人类滑动行为的鼠标移动轨迹。加密破解将滑动距离和轨迹数据按照目标网站的加密方式本例是AES进行加密生成能被服务端验证的token或validate参数。对于问题1和2已有非常成熟的方案。图像识别可以用OpenCV的模板匹配轨迹模拟可以使用符合正态分布的加速度模型来生成坐标点序列。真正的难点和本项目的核心在于问题3如何找到并复现那个AES加密函数。因此我们的技术栈很明确分析工具Chrome/Edge开发者工具特别是Sources和Network面板、可能用到的浏览器插件如EditThisCookie用于查看Cookie。调试与追踪依赖于浏览器内置的JavaScript调试器设置断点、单步执行、查看调用栈是基本功。实现语言Python。因为它有丰富的库支持后续的自动化操作如requests、selenium、opencv-python以及加密解密pycryptodome。注意整个逆向过程必须在法律和网站服务条款允许的范围内进行仅用于学习与研究目的切勿对目标网站造成负载压力。2.2 关键接口的抓取与链路分析一切分析始于网络请求。打开浏览器的开发者工具F12切换到Network网络面板并勾选“Preserve log”保留日志。然后手动完成一次滑块验证码操作。操作完成后仔细审视网络请求列表。通常一个完整的滑块验证码流程会包含以下几个关键接口这与我们搜索到的信息是吻合的初始化/获取验证码接口通常以captcha、verify或auth为路径关键词。它负责初始化一个验证会话返回本次验证的唯一标识如session_id或captcha_id、背景图、滑块图的URL有时还会包含一些用于后续加密的初始参数一个key或iv。提交验证接口通常以validate、verify或user_verify为路径关键词。这是最核心的接口我们生成的滑动距离和轨迹数据经过加密后就通过这个接口提交。我们的主要目标就是分析这个接口的请求参数是如何生成的。你需要找到那个提交验证结果的POST请求。点击它查看它的Headers尤其是Form Data或Payload和Response。你可能会看到类似这样的提交数据{ captcha_id: xxxx, validate: aes_encrypted_long_string_here, 轨迹数据或其他信息: ... }这个validate参数就是一串长长的、看似随机的密文它就是经过AES加密后的验证信息。我们的任务就是搞清楚明文是什么以及加密的细节密钥、模式、填充方式。3. 深入JavaScript定位与解析加密函数3.1 全局搜索与断点调试找到提交接口后下一步是找出生成validate参数的JavaScript代码。有以下几种常用方法搜索关键词在Sources面板下按CtrlShiftF进行全局文件搜索。可以尝试搜索validate、encrypt、AES、CryptoJS一个常用的前端加密库、enc等关键词。XHR/Fetch断点在Sources面板的XHR/Fetch Breakpoints中可以添加一个断点URL包含提交接口的关键部分如user_verify。这样当JavaScript代码发起这个请求时执行会自动暂停。堆栈追踪在Network面板中点击那个提交请求在右侧标签页选择Initiator发起者它会显示是哪个函数发起了这个请求点击对应的行号可以直接跳转到源代码。一旦成功在加密或提交相关代码处暂停你就进入了调试环节。这时要充分利用Call Stack调用堆栈查看函数调用链向上追溯找到加密函数的入口。Scope作用域查看当前函数局部变量、闭包变量、全局变量的值。密钥key、偏移量iv等关键参数很可能就在这里单步执行F10/F11一步步执行代码观察变量值的变化理解数据流转过程。3.2 逆向常见的AES实现在前端AES加密通常通过以下方式实现使用CryptoJS库非常普遍。你可能会看到CryptoJS.AES.encrypt(plainText, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })这样的代码。这几乎给了你所有信息明文、密钥、IV、模式CBC、填充Pkcs7。使用Web Crypto API较新的标准。代码会涉及window.crypto.subtle.encrypt参数配置在algorithm对象中。自定义或混淆的实现最棘手的情况。开发者可能自己实现了AES算法或者对CryptoJS进行了深度混淆。这时需要更大的耐心去分析。实操心得在调试时重点关注一个明文字符串可能是滑动距离和轨迹的JSON字符串是如何一步步变成最终的密文的。记录下转换过程中的每一个关键变量值。一个非常有效的方法是在加密函数执行前通过控制台Console手动修改传入的明文为你自己构造的已知字符串例如{distance: 100}然后执行加密对比结果。这能帮你确认加密函数是否正确以及后续在Python中复现时是否一致。4. 在Python中复现加密流程4.1 环境搭建与库的选择分析完成后我们就需要在Python中还原这个加密过程。首先安装必要的库pip install requests opencv-python numpy pycryptodomepycryptodome这是Python下功能强大的加密库完美支持AES。注意它通常以Crypto模块导入与旧的pycrypto冲突请确保正确安装和导入from Crypto.Cipher import AES。4.2 复现AES加密的关键步骤假设我们通过逆向分析得知了以下信息明文plain_text是一个JSON字符串例如{distance: 185, track: [[0,0],[10,5],...], timestamp: 1648886400000}。密钥key可能是从初始化接口返回的一个字符串长度为16、24或32字节对应AES-128, AES-192, AES-256。有时密钥需要经过某种编码如Base64解码或哈希处理才能使用。偏移量iv可能是一个固定值也可能是动态生成的。长度必须为16字节。模式mode最常见的是CBC模式。填充padding最常见的是PKCS7填充。下面是在Python中使用pycryptodome进行AES-CBC加密的示例代码import json from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 def aes_encrypt(plain_text, key, iv): 模拟前端AES-CBC加密 :param plain_text: 明文字符串 :param key: 密钥字节串 :param iv: 偏移量字节串 :return: Base64编码后的密文字符串 # 确保明文是bytes data plain_text.encode(utf-8) # 进行PKCS7填充 padded_data pad(data, AES.block_size) # 创建加密器 cipher AES.new(key, AES.MODE_CBC, iv) # 加密 encrypted_bytes cipher.encrypt(padded_data) # 通常前端输出是Base64字符串 encrypted_b64 base64.b64encode(encrypted_bytes).decode(utf-8) return encrypted_b64 # 示例用法 if __name__ __main__: # 这些值需要从逆向分析中获取 mock_plain_text json.dumps({distance: 185, ts: 1648886400000}) # 密钥和IV必须是16/24/32字节的bytes对象 # 如果分析得到的是Base64字符串需要先解码 key_from_analysis thisis16byteskey.encode(utf-8) # 或 base64.b64decode(key_b64) iv_from_analysis 1234567890123456.encode(utf-8) # 或 base64.b64decode(iv_b64) validate aes_encrypt(mock_plain_text, key_from_analysis, iv_from_analysis) print(f生成的validate: {validate})注意事项编码问题前端JavaScript的字符串到字节的转换可能与Python默认的UTF-8不同。如果遇到问题可以尝试其他编码如latin-1或者检查密钥/IV是否经过了特殊的处理如CryptoJS.enc.Utf8.parse。动态参数密钥和IV不一定是固定的。它们很可能在“初始化接口”的响应中返回或者由前端JavaScript根据时间戳、会话ID等动态计算生成。务必确保你在Python中使用的key/iv与单次验证会话中前端使用的完全一致。完整流程集成这个加密函数需要嵌入到你的自动化流程中先调用初始化接口获取captcha_id和可能的key/iv然后用OpenCV计算滑动距离生成轨迹组装明文加密最后提交。5. 图像识别与轨迹生成实战5.1 使用OpenCV计算滑动距离获取到背景图和滑块图后我们使用OpenCV的模板匹配功能。这里有一个技巧滑块图通常是带透明通道的PNG而背景图是JPG。直接匹配效果可能不好。通常我们需要对滑块图进行一些预处理比如提取其不透明部分alpha通道作为模板。import cv2 import numpy as np def calculate_slide_distance(bg_path, slice_path): 计算滑块需要滑动的距离 :param bg_path: 背景图路径或numpy数组 :param slice_path: 滑块图路径或numpy数组 :return: 滑动距离像素 # 读取图片 bg_img cv2.imread(bg_path) if isinstance(bg_path, str) else bg_path slice_img cv2.imread(slice_path, cv2.IMREAD_UNCHANGED) if isinstance(slice_path, str) else slice_path # 处理滑块图的透明背景 # 如果滑块图有4个通道RGBA则分离出alpha通道作为掩码 if slice_img.shape[2] 4: b, g, r, a cv2.split(slice_img) slice_rgb cv2.merge([b, g, r]) # 前三个通道是颜色 mask a # 第四个通道是透明度 # 将模板匹配限制在非透明区域 # 但TM_CCOEFF_NORMED方法本身对均匀区域不敏感有时直接用RGB效果更好 template slice_rgb else: template slice_img # 执行模板匹配使用归一化相关系数匹配法效果较好 result cv2.matchTemplate(bg_img, template, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc cv2.minMaxLoc(result) # max_loc是匹配位置左上角的点对于缺口在右侧的滑块这就是缺口左边缘的位置 # 注意有些验证码的滑块图是缺口本身匹配到的是缺口位置有些滑块图是凸起部分匹配到的是滑块位置。 # 需要根据实际情况判断。通常匹配到的x坐标就是需要滑动的距离。 slide_distance max_loc[0] # 可视化调试用 # h, w template.shape[:2] # top_left max_loc # bottom_right (top_left[0] w, top_left[1] h) # cv2.rectangle(bg_img, top_left, bottom_right, (0, 255, 0), 2) # cv2.imshow(Match, bg_img) # cv2.waitKey(0) return slide_distance避坑技巧匹配方法cv2.TM_CCOEFF_NORMED是最常用的它对光照变化有一定鲁棒性。多尺度与旋转极少数验证码会对背景图进行轻微缩放或扭曲。如果常规匹配失败可以考虑多尺度模板匹配。二次校验计算出的距离可以尝试加减几个像素模拟人类误差并分别加密提交看哪个能成功。5.2 模拟人类滑动轨迹直接以匀速将滑块拖到目标位置太“机器”了容易被识别。我们需要生成一个包含加速、减速、轻微抖动和过冲回拉的轨迹。import random import time def generate_track(distance): 生成模拟人类的滑动轨迹 :param distance: 目标滑动距离像素 :return: 轨迹列表每个元素为[时间偏移量, x坐标, y坐标] track [] current_x 0 current_y random.randint(5, 15) # 初始y坐标有一些随机偏移 t 0 # 总时间在0.5秒到2秒之间随机 total_t random.uniform(0.8, 1.5) # 将滑动过程分为三段加速、匀速、减速 mid_distance distance * random.uniform(0.6, 0.8) # 第一阶段加速 v 0 a random.uniform(1.5, 3.0) while current_x mid_distance: t random.uniform(0.01, 0.02) # 时间步长 v a * 0.02 delta_x v * 0.02 random.uniform(-1, 1) # 加入微小抖动 current_x delta_x current_y random.uniform(-0.5, 0.5) # y轴轻微晃动 track.append([round(t, 3), round(current_x, 2), round(current_y, 2)]) # 第二阶段匀速或微减速 v v * random.uniform(0.8, 1.0) while current_x distance: t random.uniform(0.01, 0.02) delta_x v * 0.02 random.uniform(-0.5, 0.5) current_x delta_x current_y random.uniform(-0.3, 0.3) track.append([round(t, 3), round(current_x, 2), round(current_y, 2)]) # 第三阶段过冲与回拉模拟人手抖 overshoot random.uniform(0, distance * 0.03) current_x overshoot track.append([round(t0.05, 3), round(current_x, 2), round(current_y, 2)]) # 回拉到精确位置 current_x distance track.append([round(t0.1, 3), round(current_x, 2), round(current_y, 2)]) # 确保最后一个点是精确的目标距离 if track[-1][1] ! distance: track.append([round(t0.12, 3), distance, track[-1][2]]) return track这个轨迹生成函数模拟了基本的物理运动并加入了随机性使其更接近真人操作。生成的轨迹数据需要被整合到上一步的明文JSON中。6. 全流程集成与自动化脚本现在我们将所有模块组合起来形成一个完整的自动化破解脚本。这里以使用requests和session为例假设目标网站接口清晰没有强反爬。import requests import json import time import cv2 from io import BytesIO from PIL import Image import numpy as np # ... 导入前面定义的 aes_encrypt, calculate_slide_distance, generate_track 函数 ... class SliderCaptchaCracker: def __init__(self, base_url): self.session requests.Session() self.base_url base_url self.session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: base_url, }) self.captcha_id None self.encrypt_key None self.encrypt_iv None def init_captcha(self): 步骤1初始化验证码获取图片和加密参数 init_url f{self.base_url}/api/captcha/init resp self.session.post(init_url).json() # 假设返回格式{“code”:0, “data”:{“id”:”abc123”, “bg_img”:”url1, “slice_img”:”url2, “key”:”base64key”, “iv”:”base64iv”}} self.captcha_id resp[data][id] bg_img_url resp[data][bg_img] slice_img_url resp[data][slice_img] self.encrypt_key base64.b64decode(resp[data][key]) # 解码得到bytes self.encrypt_iv base64.b64decode(resp[data][iv]) # 下载图片 bg_content self.session.get(bg_img_url).content slice_content self.session.get(slice_img_url).content # 转换为OpenCV格式 bg_img cv2.imdecode(np.frombuffer(bg_content, np.uint8), cv2.IMREAD_COLOR) slice_img cv2.imdecode(np.frombuffer(slice_content, np.uint8), cv2.IMREAD_UNCHANGED) return bg_img, slice_img def crack_and_submit(self): 主流程破解并提交 # 1. 初始化 print(正在初始化验证码...) bg_img, slice_img self.init_captcha() # 2. 计算滑动距离 print(正在计算滑动距离...) distance calculate_slide_distance(bg_img, slice_img) print(f计算出的滑动距离: {distance} 像素) # 3. 生成轨迹 print(正在生成模拟轨迹...) track generate_track(distance) # 4. 构造明文并加密 print(正在构造验证数据...) timestamp int(time.time() * 1000) plain_data { captchaId: self.captcha_id, distance: distance, track: track, timestamp: timestamp, # 可能还有其他固定或动态参数需根据逆向分析补充 } plain_text json.dumps(plain_data, separators(,, :)) # 紧凑格式与前端一致 validate aes_encrypt(plain_text, self.encrypt_key, self.encrypt_iv) # 5. 提交验证 print(正在提交验证...) submit_url f{self.base_url}/api/captcha/verify payload { captcha_id: self.captcha_id, validate: validate, } resp self.session.post(submit_url, jsonpayload).json() return resp if __name__ __main__: # 替换成目标网站的基地址 cracker SliderCaptchaCracker(https://target-website.com) result cracker.crack_and_submit() print(f验证结果: {result})这个类提供了一个完整的框架。在实际使用中你需要根据目标网站的具体接口地址、参数名和响应格式进行适配。7. 常见问题排查与进阶技巧即使按照上述流程你也可能会遇到各种问题。下面是一些常见坑点及解决方案7.1 加密结果不一致这是最常遇到的问题表现为Python加密出的validate和服务端不认可。检查密钥和IV确认从初始化接口获取的key/iv是否经过了额外的处理如字符串截取、哈希。用浏览器调试工具在加密函数处打印出原始的key和iv可能是CryptoJS的WordArray对象并查看其十六进制或Base64表示与你的Python代码获取的进行逐字节对比。检查明文格式对比你的明文JSON字符串和前端的明文字符串是否完全一致。包括字段顺序、空格、缩进通常前端会使用JSON.stringify的紧凑模式。可以尝试在浏览器控制台执行JSON.stringify(your_data)来获取标准格式。检查编码和填充确认AES的模式CBC/ECB等和填充方式PKCS7/ZeroPadding等是否正确。pycryptodome的默认填充是PKCS7。动态密钥生成key/iv可能不是直接来自接口而是由接口返回的某个种子参数经过前端JavaScript计算得出。你需要逆向这个计算过程。7.2 轨迹验证与行为检测除了加密参数服务端可能还会验证轨迹数据本身。轨迹合理性确保生成的轨迹在总时间、速度变化、移动路径上符合人类行为。过于完美或随机的轨迹都可能被拒绝。轨迹加密有些网站会将轨迹数据也作为明文的一部分进行整体加密。有些则可能单独加密或签名轨迹。需要仔细分析提交的数据结构。鼠标事件监听高级的验证码可能会监听真实的鼠标移动事件mousemove。如果使用requests纯发包模拟缺少这些事件可能导致失败。此时可能需要使用selenium或playwright这类浏览器自动化工具来真实地拖动滑块并在拖动过程中注入JavaScript来获取所需的加密参数。7.3 应对代码混淆与反调试现代网站会使用Webpack打包、变量名混淆、代码压缩甚至加入反调试技巧如无限debugger循环。格式化代码在Sources面板点击{}按钮可以美化压缩的代码。Hook关键函数在加密函数可能被调用的地方如Function.prototype.call,CryptoJS.AES.encrypt设置条件断点或者使用浏览器插件如Tampermonkey注入脚本直接Hook这些函数打印出输入输出。绕过反调试对于无限debugger可以通过在debugger语句上右键选择“Never pause here”或者使用setInterval钩子来绕过。7.4 提升识别成功率OpenCV模板匹配不是万能的。图像预处理对背景图和滑块图进行灰度化、高斯模糊、边缘检测如Canny有时能提升匹配精度尤其是当图片有噪声或颜色干扰时。深度学习方案对于极其复杂或动态的验证码可以考虑使用深度学习模型如YOLO做目标检测来识别缺口位置。但这需要收集和标注数据成本较高。第三方打码平台作为备选方案可以接入商业打码平台将图片发送给他们返回识别结果。这适用于项目稳定性和成功率要求高且愿意支付一定费用的场景。逆向工程破解加密验证码是一个需要耐心、细心和系统方法的过程。它没有一成不变的公式每个网站都可能有自己的“花样”。核心思路永远是观察 - 假设 - 验证 - 实现。从网络请求出发定位关键代码理解其数据流和加密逻辑最后在自动化环境中复现。成功破解的那一刻带来的不仅是项目上的进展更是对Web前端安全机制深入理解的成就感。希望这份详细的实战指南能为你打开逆向工程的大门。