若有侵权请联系作者删除。
注意:这是其实是一个某美的点选验证。
https://secure.elong.com/passport/login_cn.html?nexturl=https://www.elong.com/
https://www.ishumei.com/trial/captcha.html
1.必备知识点
在开始逆向案例之前,先来学一些前置必备的技能。
1.1 动态无法调试
网页定位到某个js文件中的位置,想要断点调试,但由于每次js的网址都会更新(返回内容虽然一样),导致断点无法执行,这种情况下怎么办?
解决方案:找到返回此js地址的页面,将返回值其替换为固定的值,具体步骤:
-
安装抓包工具Charles,让Charles代理网页的请求
-
在Charles中配置,当请求网址时,返回我们指定的HTML数据(网页上加载js地址就是固定的了)
Charles页面替换
当charles可以作为代理之后,可以在Charles中配置:如果浏览器上访问某网址,给他返回特定的内容。
网页访问和调试
1.2 数美示例
文本替换
1.单独替换
import subprocess
res = subprocess.check_output("node part.js 0x8fa")
char_string = res.decode('utf-8').strip()
print(char_string)
2.整体替换
import re
import subprocess
def exec_value(hex_string):
res = subprocess.check_output(f"node part.js {hex_string}")
char_string = res.decode('utf-8').strip()
return char_string
def get_total_set():
data_set = set()
with open("v1.js", mode='r', encoding='utf-8') as f1:
for line in f1:
if not line:
continue
match_list = re.findall(r"var (.*) = _0x2942", line)
if match_list:
data_set.add(match_list[0])
return data_set
def loop_re_match(data_set, line):
for func in data_set:
match_list = re.findall(f"({func}\((.*?)\))", line)
if not match_list:
continue
for total, arg in match_list:
real_value = exec_value(arg)
line = line.replace(total, f'"{real_value}"')
return line
def run():
data_set = get_total_set()
counter = 0
with open("v1.js", mode='r', encoding='utf-8') as f1, open("v2.js", mode='w', encoding='utf-8') as f2:
for line in f1:
counter += 1
print(counter)
if not line:
f2.write(line)
continue
line = loop_re_match(data_set, line)
f2.write(line)
if __name__ == '__main__':
run()
2.整体请求分析
登录?
2.1 配置
https://captcha1.fengkongcloud.cn/ca/v1/conf?organization=xQsKB7v2qSFLFxnvmjdO&lang=zh-cn&sdkver=1.1....
2.2 获取验证码
点击登录或验证失败,出现验证码。
https://captcha1.fengkongcloud.cn/ca/v1/register?rversion=1.0.4&model=select&captchaUuid=20231209163703YF....
2.3 提交验证
点击4个验证码后,自动提交。
https://captcha1.fengkongcloud.cn/ca/v2/fverify?qd=tbJHTr9SEFvL5eG%2B...
3.逆向:配置
3.1 请求分析
organization: xQsKB7v2qSFLFxnvmjdO 固定,应该是购买数美产品给的ID
lang: zh-cn
sdkver: 1.1.3
captchaUuid: 20231209163703YFQE2TFkjH2k28pffR 动态,每次刷新不一样【需逆向】
callback: sm_1702111031681
appId: default
model: select 应该是验证模式:点选、滑块
channel: DEFAULT
rversion: 1.0.4
可以分析出,CaptchaUuid的生成其实就是:时间 + 随机字符串(例如:"20231209202804MKhadf765PiJNRwYAG")
3.2 发送请求
requests.get(
url="https://captcha1.fengkongcloud.cn/ca/v1/conf",
params={
"captchaUuid": captcha_uuid,
"model": "select",
"appId": "default",
"rversion": "1.0.4",
"sdkver": "1.1.3",
"callback": f"sm_{int(time.time())}",
"organization": "xQsKB7v2qSFLFxnvmjdO",
"channel": "DEFAULT",
"lang": "zh-cn"
}
)
4.逆向:获取验证码
无需逆向,直接请求:
5.逆向:提交指纹
https://captcha1.fengkongcloud.cn/ca/v2/fverify
{
"organization": "xQsKB7v2qSFLFxnvmjdO",
"tb": "3jSn4gNaAVM%3D",
"qd": "C0bn16G5KCfL5eG%2BcsX4D2zVk5OJj0v9yp%2FobhabiHPSaRWTFZHwqEESQabyF%2Bifwqvw%2FubnvtTyjoquNCrB09JpFZMVkfCoT3%2BuLCX%2BA5yyOD%2FPFvS%2B51EucGdmqC6MjXlEf1fmqIBRKHWSYzFoY7qCDMDWbBnp0mkVkxWR8Kg1lrHsO5vreY15RH9X5qiA49vPYosSTsacp4eCSlsHhGkN0N7e7dNRy%2BXhvnLF%2BA9BEkGm8hfon663Z%2B5nHqIc",
"ostype": "web",
"xy": "YabT6nmJOC0%3D",
"dy": "Rfpr5oqb5y4%3D",
"en": "y%2Bugz9NIWys%3D",
"rversion": "1.0.4",
"sdkver": "1.1.3",
"nu": "C0kH%2FbWLjw8%3D",
"mu": "8LlSNopqsqQ97nxg3tdIZ6XUAHUd9ZXRv1tmsN36Xi4fSW76upB2jGHRX517zf%2Bb%2Bc1ERqOHAyjeY2BarYayPR9Jbvq6kHaMYZZm%2FG7Dqv70lf%2BNyEwjJti6s6NuBhD3X3AJlc40WilxEDbwniT602l%2BVxoLs71sH0lu%2BrqQdozaurDbYgC84l9wCZXONFopdgBBtTGOrxytgM41ZQbVteQJ8SC%2F%2FSvbPe58YN7XSGdh0V%2Bde83%2Fm7RQACrkbzLP",
"captchaUuid": "20231209214506ff2iMsMEYx24tjQdzD",
"jo": "l3aEINYnwpY%3D",
"act.os": "web_pc",
"protocol": "180",
"rid": "20231209214510fa3c46caca0a508076",
"callback": "sm_1702130040080",
"mp": "WYfkIZp7GoA%3D",
"oc": "h9oFKi8cHpg%3D",
"ww": "jYqq0U9Wjos%3D",
"kq": "mtlOTdT5LOE%3D"
}
5.1 定位
当在图片上点击第4标记时,触发请求。
5.2 特殊替换
5.3 第4次请求
5.4 getEncryptContent
5.5 DES加密
npm install crypto-js -g
var CryptoJS = require("crypto-js")
function DESEncrypt(key, word) {
var key_ = CryptoJS.enc.Utf8.parse(key);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.DES.encrypt(srcs, key_, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
});
return encrypted.toString();
}
function DESDecrypt(key, word) {
var key_ = CryptoJS.enc.Utf8.parse(key);
var decrypt = CryptoJS.DES.decrypt(word, key_, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
});
return decrypt.toString(CryptoJS.enc.Utf8);
}
var result = DESEncrypt("6f5e9847","1");
console.log(result);
5.6 意外收获-其他参数
在调试getEncryptContent时,发现除了 _0x298b01
相关的参数也走了 getEncryptContent
。
{
"mp":"WYfkIZp7GoA=",
"oc":"h9oFKi8cHpg=",
"xy":"YabT6nmJOC0=",
"jo":"l3aEINYnwpY=",
"qd":"iUUzUpbSrVLL5eG+cs....",
"mu":"Gcxg3RiVeoM97nxg3tdIZ3nzK3R7...",
"ww":"iLL8K/VHX50=",
"nu":"C0kH/bWLjw8=",
"dy":"Rfpr5oqb5y4=",
"tb":"3jSn4gNaAVM=",
"en":"y+ugz9NIWys=",
"kq":"mtlOTdT5LOE=",
"rid":"2023121111394173034a940e0c5d82cc",
"organization":"xQsKB7v2qSFLFxnvmjdO",
"rversion":"1.0.4",
"sdkver":"1.1.3",
"protocol":"180",
"ostype":"web",
"callback":"随便写",
"act.os":"web_pc",
"captchaUuid":"20231211113934kRf2pzsG3e7ACCJzDt"
}
5.7 替换再观察
5.8 selectData
接下来就是搞定:this['_data']["selectData"]或 this['_data']['mouseData']
两个值在点选时是一样的。
在刚开始定位有相关赋值:
这其实是 mousedown事件,每次点击都会执行,在这里生成相应的数据:
6.识别和测试
6.1 验证码识别
7.登录
点选搞定之后,提交登录就简单了,直接携带 rid
发送请求就行了
以上就是x美的点选验证大致流程
有需求请联系作者获取源码 aHVhcXUwNzI3