如何做阿里巴巴的网站首页,新加坡网站后缀,国土局网站建设经验,wordpress在哪登陆文章目录 1. 写在前面2. 接口分析3. 验证轨迹4. 算法还原 【#x1f3e0;作者主页】#xff1a;吴秋霖 【#x1f4bc;作者介绍】#xff1a;擅长爬虫与JS加密逆向分析#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致… 文章目录 1. 写在前面2. 接口分析3. 验证轨迹4. 算法还原 【作者主页】吴秋霖 【作者介绍】擅长爬虫与JS加密逆向分析Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作 【作者推荐】对爬虫领域以及JS逆向分析感兴趣的朋友可以关注《爬虫JS逆向实战》《深耕爬虫领域》 未来作者会持续更新所用到、学到、看到的技术知识包括但不限于各类验证码突防、爬虫APP与JS逆向分析、RPA自动化、分布式爬虫、Python领域等相关文章 作者声明文章仅供学习交流与参考严禁用于任何商业与非法用途否则由此产生的一切后果均与作者无关如有侵权请联系作者本人进行删除 1. 写在前面 作者在使用红薯的时候经常会选择通过手机验证的方式去登录。但是天公不作美我甚至有时候第一次登录就给我弹出一个验证码…有风控固然是好的但是略微的影响到了用户的交互体验一般用户只能选择是手动拖动滑块进行验证… 但是作为一名科技行业的程序员。肯定是不会屈服的于是带着对技术的好奇心在想过这个旋转的验证码是否可以自动化呢当然自动化没有挑战作者选择通过协议算法的方式去通过这个验证码
2. 接口分析
这里我们浅拉一下旋转的滑块然后监测register这个接口的发包如下所示 这几个参数中verifyUuid是重要的它在每一次生成弹出验证码的时候给出的一个新且唯一的验证ID在后续的验证接口同样需要携带获取方式如下所示 在输入手机号点击发送短信验证码的时候假设失败被风控了。短信接口会正常给到你成功的响应如下所示
{code:0,success:true,msg:成功,data:{}}但是如果你光通过接口去检验是不知道出验证的。得看请求状态失败出现验证码则是471这个时候再去头部分析拿到验证码的UID
通过register的接口获取到验证码提交所需要的rid跟captchaInfo字段信息 请求同样是需要头部带X-s、X-s-common参数的 第二个我们需要分析的接口则是check这个是提交验证检测的。如下所示 可以看到提交的参数中captchaInfo是最重要的通过字段信息能够看到提交的鼠标轨迹相关的一些东西而且看这样子应该还是加密的另外的rid、uid在前面环节都能够获取到checkCount参数则是验证次数通不过就会自增
3. 验证轨迹
接下来如果没有解决头部X系列的两个参数加密建议先去研究这个两个参数再来研究滑块当然如果你不知道怎么还原可以去看作者以前的文章现在开始还原captchaInfo这个参数
JS调试发现轨迹验证参数的值采用了DES加密可以拿浏览器内的加密轨迹来解密看看如下所示 轨迹分析后开始实现算法如下所示
function generate_Track(slideDistance) {var trackList [];var x 0;while (x slideDistance) {x 2;var y -(Math.floor(x / 10));var z 2 * (x - 1) Math.floor(Math.random() * 7) 1;trackList.push([x, y, z]);}return JSON.stringify(trackList);
}function get_mousetrack(distance,time){var mousetrack1get_trace(distance,time);var mousetrack2generate_Track(distance);return DES_Encrypt(mousetrack2,PYrm8rMk)
}function get_trace(distance,time) {distance Math.floor(distance);var trace [];var sy [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0];var st [15, 16, 17, 18, 15, 16, 17, 18, 15, 16, 17, 18, 15, 16, 17, 18, 15, 16, 17, 18, 15, 16, 17, 18, 15, 16, 17,18, 15, 16, 17, 18, 15, 16, 17, 18, 15, 16, 17, 18, 14, 16, 17, 18, 16, 17, 18, 19, 20, 17];if (distance 95) {var sx [1, 2, 1, 2, 1, 2, 1, 1, 2, 1];}else{var sx [1, 2, 1, 2, 1, 2, 2, 2, 3, 4];}var zt RandomNum(10, 100);var zx 0,zy 0;var random_x RandomNum(9, 14);var n 0, x 0, y 0, t 0;while (true){n 1;if (n 5){x 1;}else{x RandomChoice(sx)}if (distance 125 random_x n){x RandomNum(14, 18)}y RandomChoice(sy);t RandomChoice(st);zx x;zy y;zt t;trace.push([zx, zy, zt]);if (distance - zx 6){break;}}var value distance - zx;for (var i 0; i value; i){t RandomChoice(st);if (value i 1){t RandomNum(42, 56)}if (value i 2){t RandomNum(32, 38)}if (value i 3){t RandomNum(30, 36)}x 1;zx x;zt t;trace.push([zx, zy, zt]);}let cszRandomNum(1, 10)let elementToInsert0 [0, 0, csz];let elementToInsert1 [0, 0, csz2];trace.unshift(elementToInsert1);trace.unshift(elementToInsert0);return JSON.stringify(trace);
}4. 算法还原
首先需要封装register接口的协议请求核心请求提交封参如下所示
json_data {secretId: 000,verifyType: 102,verifyUuid: v_id,verifyBiz: 471,sourceSite: ,captchaVersion: 1.1.0
}
jmurlurl/api/redcaptcha/v2/captcha/registerjson.dumps(json_data).replace( ,)a1ck[a1]
xts self.ctx.call(get_x_s,jmurl,a1)
xtscommon self.ctx.call(get_x_s_common,xts,a1)
self.headers[x-s] xts[X-s]
self.headers[x-s-common]xtscommon
self.headers[x-t]str(xts[X-t])datajson.dumps(json_data,separators(,,:))
response requests.post(url, headersself.headers, cookiesck, datadata)check验证接口的提交核心封参请求如下所示
chainfo{mouseEnd:self.ctx.call(DES_Encrypt,mouseend,WquqhEkd),time:self.ctx.call(DES_Encrypt,time,vPMvCY4K),track:self.ctx.call(get_mousetrack,mouseend,time),width:self.ctx.call(DES_Encrypt,width,WquqhEkd)}json_data{rid:rid,verifyType:102,verifyBiz:471,verifyUuid:v_id,sourceSite:,captchaVersion:1.1.0,checkCount:str(check_count),captchaInfo:json.dumps(chainfo)}当然请求完成后的过程中我们还需要对旋转图片角度的分析与图像处理包括裁剪主要就是通过对两个图像进行分析处理来找出最佳的角度。使得合并后的图像在梯度上的差异最小这里我们采用了CV2通过核心Py。源码如下所示
def perform_angle_analysis(self,query_image_path, background_image_path):def calculate_gradient_difference(image, cx, cy, circle_radius):circle_inner_mask np.zeros_like(image, dtypenp.uint8)cv2.circle(circle_inner_mask, (cx, cy), circle_radius, 255, -1)circle_outer_mask np.zeros_like(image, dtypenp.uint8)cv2.circle(circle_outer_mask, (cx, cy), circle_radius 30, 255, -1)inner_pixels cv2.bitwise_and(image, circle_inner_mask)outer_pixels cv2.bitwise_and(image, circle_outer_mask)inner_sobel_x cv2.Sobel(inner_pixels, cv2.CV_64F, 1, 0)inner_sobel_y cv2.Sobel(inner_pixels, cv2.CV_64F, 0, 1)inner_gradient_magnitude cv2.magnitude(inner_sobel_x, inner_sobel_y)outer_sobel_x cv2.Sobel(outer_pixels, cv2.CV_64F, 1, 0)outer_sobel_y cv2.Sobel(outer_pixels, cv2.CV_64F, 0, 1)outer_gradient_magnitude cv2.magnitude(outer_sobel_x, outer_sobel_y)gradient_diff np.sum(outer_gradient_magnitude) - np.sum(inner_gradient_magnitude)return gradient_diffdef merge_images(query_result, bg_image, radius, angle):query_height, query_width query_result.shape[:2]rotation_matrix cv2.getRotationMatrix2D((query_width / 2, query_height / 2), angle, 1)rotated_result cv2.warpAffine(query_result, rotation_matrix, (query_width, query_height))center (bg_image.shape[1] // 2, bg_image.shape[0] // 2)cv2.circle(bg_image, center, radius, (0, 0, 0), -1)bg_height, bg_width bg_image.shape[:2]circle_height, circle_width rotated_result.shape[:2]x (bg_width - circle_width) // 2y (bg_height - circle_height) // 2overlay np.zeros_like(bg_image)overlay[y:y circle_height, x:x circle_width] rotated_resultresult cv2.bitwise_or(bg_image, overlay)return resultdef split_circular_region(image, radius):height, width, _ image.shapecenter (width // 2, height // 2)mask np.zeros_like(image)cv2.circle(mask, center, radius, (255, 255, 255), -1)result cv2.bitwise_and(image, mask)return resultdef enlarge_image(image, scale_factor):circle_height, circle_width image.shape[:2]new_height int(circle_height * scale_factor)new_width int(circle_width * scale_factor)result cv2.resize(image, (new_width, new_height), interpolationcv2.INTER_LINEAR)return resultquery_image cv2.imread(query_image_path)bg_image cv2.imread(background_image_path)query_result split_circular_region(query_image, 89)query_result enlarge_image(query_result, 1.1)min_difference float(inf)min_angle 0for angle in range(0, 360, 5):result merge_images(query_result, bg_image, 89, angle)gradient_difference calculate_gradient_difference(result, bg_image.shape[1] // 2, bg_image.shape[0] // 2, 70)if gradient_difference min_difference:min_difference gradient_differencemin_angle angleresult merge_images(query_result, bg_image, 89, min_angle)cv2.imwrite(./jpg/result.png, result)return min_angle最后完成所有的编码后把滑块验证部署成一个API服务这样的话更加方便调用如下 本地直接通过检测471验证码拿到uid、cookie调用纯协议滑块验证服务效果如下