技术文章大纲HU航司App逆向与验签协议算法分析背景与目标目标网址aHR0cHM6Ly9uZXcuaG5haXIuY29tL2hhaW5hbmFpci9pYmUvY29tbW9uL2xvZ2luLmRvJS逆向分析流程流程分析获取验证码打开登录页面输入手机号点击获取验证码触发验证码里面需要还原的值有q1-q2,p3-p9清除浏览器cookie刷新网页重新请求并打上断点往下跟栈此时数据已生成。q1在文件里面搜索QO00Qo可以看到这个值是一个变量fmopt.tokenfmopt.partnerfopt.appname, fmopt的属性值都可以写死搜索这个变量oOO0OQ是个定值。回到最初这几个值拼接后进行url编码就是q1q2:全局搜索o00ooQ这个可以写死有两种状态一种|^^1^^1^^1 然后url编码一种 blackbox替换|然后url编码var o00ooQ (O0oOoQ.blackBox || |) ^^ O0oOoQ.requestType ^^ (QoQ0oO(OOOQoQ.lang) 1) ^^ (Q0QQO0.support_css3_value(QOOO0o(usbotgpsn-tuzmf), QOOO0o(qsftfswf-3e)) !(!!window.ActiveXObject || QOOO0o(AdujwfXOckfdu)in window) ? 1 : 2);p3经过位置加密函数生成未加密数据搜索这个值a函数(p1p2)validateCodeObj/| userAnsewr/| 函数(变量b)var o00OQ0 oOQoOo(QO00Qo ^^ o00ooQ) ^^ (O0oOoQ.validateCodeObj || |) ^^ (O0oOoQ.userAnswer || |) ^^ (O0oOoQ.validateToken || |) oOQoOo(QoQQ0O);搜索变量b b变量c/| true/| true/|时间戳var QoQQ0O (Q00oo0 || |) ^^ (Oo0O0O || |) ^^ (O0Oo0O || |) ^^ new Date().getTime();搜索变量c还原一下代码 cargumentss[callee][caller][toString][length]var Q00oo0 function() { return arguments[callee]caller][toString]()[length]; }();秘钥 de(stq67pv9)p1.substring(10,18)这里后面的属性是p1进行截取充当var o0o0oo QOOO0o(stq67pv9) Q0QOoQ.substring(10, 18);e函数直接扣下来转成pythonASCII编码每个小写的ASCII编码-1相当于小写字母循环向前移动一位p4sensorInfo/| mouseInfo/| usedTime/|然后p3和p4进行同一个函数加密var oQooQQ (O0oOoQ.sensorInfo || |) ^^ (O0oOoQ.mouseInfo || |) ^^ (O0oOoQ.usedTime || |);跟栈进入方法可以明显看到是AES加密p5固定为 webp6 OOQ00O(8);window.location.href秘钥 OOQ00O(8);是一个八位的随机数var OQoQOQ OOQ00O(8); var oO0oQo Qo0QO0(OQoQOQ window.location.href, o0o0oo);p7另外一个是函数(p6)函数(p2)QooQQO.p7 QO0QOQ OOQ00O(32); var QO0QOQ oOQoOo(oO0oQo) oOQoOo(QoQQ0O);OOQ00O(32);是一个32随机数p8: 八位随机数QooQQO.p8 OQoQOQ; var OQoQOQ OOQ00O(8);p9 函数(p2,秘钥)函数---》以上的统称全局搜索这个变量发现是md5验证接口触发接口流程和获取一样注重关注以下几点p2初始化状态是1验证的状态是3p3里面的validateCodeObj是初始化返回的数据里面存放id背景图的url等信息。userAnswer里面存放移动距离时间戳往上再跟一层里面的userAnswer是图片的缺口距离固定10当前时间mouserInfo:里面存放的是页面的xy。所以这个鼠标轨迹可以固定验证码图片还原保存完图片是这种切片所以需要还原一下重新触发验证码获取初始化返回了一个imageType可以全局搜一下这个字段这里就是把返回的两行八列的图片转成原图。丢给ai转一下就行签名参数构造流程时间戳、设备信息、密钥派生算法还原常见加密模式AES、RSA、HMAC-SHA256等OPENVC识别距离def extract_slider_mask_and_bbox(slider_img): 从滑块图片提取精确掩码和边界框。 优先使用 alpha 通道否则用阈值二值化。 Returns: mask: 二值掩码 (h, w), 255滑块区域 bbox: (x, y, w, h) 滑块有效区域的边界框 piece_bgr: 裁剪后的滑块 BGR 图像 piece_mask: 裁剪后的掩码 h, w slider_img.shape[:2] channels slider_img.shape[2] if len(slider_img.shape) 2 else 1 if channels 4: # PNG 有 alpha 通道直接使用 alpha slider_img[:, :, 3] _, mask cv2.threshold(alpha, 128, 255, cv2.THRESH_BINARY) bgr slider_img[:, :, :3] else: # 没有 alpha 通道用阈值提取 gray cv2.cvtColor(slider_img, cv2.COLOR_BGR2GRAY) if channels 3 else slider_img _, mask cv2.threshold(gray, 30, 255, cv2.THRESH_BINARY) # 获取最大连通域去除噪声 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(mask, connectivity8) if num_labels 1: largest_label 1 np.argmax(stats[1:, cv2.CC_STAT_AREA]) mask np.where(labels largest_label, 255, 0).astype(np.uint8) bgr slider_img[:, :, :3] if channels 3 else cv2.cvtColor(slider_img, cv2.COLOR_GRAY2BGR) # 获取边界框 coords cv2.findNonZero(mask) if coords is None: return mask, None, None, None x, y, w_box, h_box cv2.boundingRect(coords) # 裁剪 piece_bgr bgr[y:y h_box, x:x w_box] piece_mask mask[y:y h_box, x:x w_box] bbox (x, y, w_box, h_box) return mask, bbox, piece_bgr, piece_mask验证结果法律与伦理边界逆向技术的合法使用范围企业防护与用户隐私的平衡个人观点禁止商业行为侵权请联系下架