书接上文,还是讲滑块方面,本次案例与极验案例过程相似,可以进行反复对比观看
网页接口(base64编码):aHR0cHM6Ly9kdW4uMTYzLmNvbS90cmlhbC9zZW5zZQ==
先讲整体大思路:
网页一共发送4次请求,而对我们有用的请求只有后面那三次。
第二次请求:
响应返回一个token,对后面的两个请求都有用
仅一个请求参数需要逆向:cb(类似于uuid的作用,后面的请求也会用到,每一次请求都不一样)
第三次请求:
响应回来背景图片的url(重要信息仅此一个)
请求参数也是只用逆向cb,token(第二次请求返回的)
第四次请求:
响应会来validate值,就说明滑块验证通过
请求参数逆向:
cb
token(第二次请求返回的)
data :涉及滑块轨迹及其加密
OK,话不多说,开始逆向
Part-1: 参数 cb的破解
先抓包
注意看,这就是第一第二次发送的请求,我们的中心在第二次
这是他的响应,对我们有用的是token值
这是它的请求参数,需要我们破解的是cb值
下面开始cb值的逆向:
先接口定位,使用关键字搜索 'cb' (可以试试cb: cb=,但都没有结果)
搜索结果有4个,都打上断点(建议先做一下文件的本地替换,不会的看我文章结尾)
刷新页面(只有刷新页面才可以触发第一二次请求)
停住了,控制台打印一下,确实是这里生成的cb
那开始逆向f3,经过我的多次尝试,扣JS代码太麻烦了,还是沿用我上期文章的思路,利用webpack(为什么可以用呢?可以看看文件的开头,如果是一个自执行函数,多半可以用,但终点还是找到加载器函数)
将文件全部保存到本地,开始调用webpack
先做一些前置操作做:
加载器全局化,日志打印
写一个main.js文件调用 loader.js,并且附上env.js(补环境文件)
找到f3所属于的函数作用域(闭包),在本地文件搜索一下
经过我的手数它在第72个函数中(如果有什么更好的方法,欢迎在评论区留言!),index是71,所以代码如下:
require('./env')
require('./loader')
x = window.loader(71)
console.log('x:::', x)
运行,报错,再补环境,env.js代码如下:
function setProxyArr(proxyObjArr) {
for (let i = 0; i < proxyObjArr.length; i++) {
const handler = `{
get: function(target, property, receiver) {
console.log("方法:", "get ", "对象:", "${proxyObjArr[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", target[property], ", 属性值类型:", typeof target[property]);
return target[property];
},
set: function(target, property, value, receiver) {
console.log("方法:", "set ", "对象:", "${proxyObjArr[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]);
return Reflect.set(...arguments);
}
}`;
eval(`try {
${proxyObjArr[i]};
${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});
} catch (e) {
${proxyObjArr[i]} = {};
${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});
}`);
}
}
window = self = top = global
delete global; // 记得用 _global = global 备份一下
delete Buffer;
delete __filename;
delete __dirname;
screen = {}
location = {
"ancestorOrigins": {},
"href": "https://dun.163.com/trial/sense",
"origin": "https://dun.163.com",
"protocol": "https:",
"host": "dun.163.com",
"hostname": "dun.163.com",
"port": "",
"pathname": "/trial/sense",
"search": "",
"hash": ""
}
navigator = {
appCodeName: "Mozilla",
appName: "Netscape",
appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
userAgent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
}
history = {}
div = {
addEventListener: function (){},
getAttribute: function (a){
// console.log("div getAttribute:::",a)
}
}
body = {}
document = {
createElement :function (ele){
// console.log("document createElement:::", ele)
if(ele==="div"){
return div
}
},
body : body,
getElementById: function (ele){
// console.log('document getElementById:::', ele)
}
}
// setProxyArr(['window', 'navigator', 'screen', 'document', 'div', 'body'])
ActiveXObject = function () {
}
XMLHttpRequest = function () {
}
addEventListener = function () {
}
attachEvent = function () {
}
setTimeout = function (){}
setInterval = function (){}
运行,控制台日志打印如下:
x::: {
state: {
version: '2.28.0',
fingerprint: '',
config: null,
langPkg: null,
smsNew: false,
captchaType: null,
type: '',
load: null,
verifyStatus: '',
token: '',
previousToken: '',
countsOfVerifyError: 0,
startTimestamp: null,
getApiCount: 0,
coreOffsetWidth: null
},
mutations: {
INVOKE_HOOK: [Function (anonymous)],
EVENT_CLOSE: [Function (anonymous)],
EVENT_RESET: [Function (anonymous)],
EVENT_RESET_CLASSIC: [Function (anonymous)],
SWITCH_TYPE: [Function (anonymous)],
SET_TYPE: [Function (anonymous)],
SWITCH_LOAD_STATUS: [Function (anonymous)],
UPDATE_VERIFY_STATUS: [Function (anonymous)],
REFRESH: [Function (anonymous)],
UPDATE_COUNTS_OF_VERIFYERROR: [Function (anonymous)],
SET_TOKEN: [Function (anonymous)],
UPDATE_LINK_TIME: [Function (anonymous)],
UPDATE_CORE_WIDTH: [Function (anonymous)]
},
actions: {
RESET_STATE: [Function (anonymous)],
FETCH_CAPTCHA: [Function (anonymous)],
FETCH_INTELLISENSE_CAPTCHA: [Function (anonymous)],
VERIFY_INTELLISENSE_CAPTCHA: [Function (anonymous)],
VERIFY_CAPTCHA: [Function (anonymous)]
}
}
发现好像并没有f3这个函数,那怎么办呢?去看看源码
发现它导出的是fC,而fC中并没有直接导出f3这个函数,所以我选择魔改一下源码(有风险,比如别的地方调用index=71的函数,它只返回f3函数)
看看效果:
有值,那cb参数就解决了(可以自己去验证一下),下面来获取token
Part-2:发送第二次请求获取token,并发送第三次请求得到bg_url
获取token代码:
import requests
import execjs
import json
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Referer": "https://dun.163.com/",
"Sec-Fetch-Dest": "script",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"sec-ch-ua": "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\""
}
url = "https://c.dun.163.com/api/v3/get"
cb = execjs.compile(open('main.js', encoding='utf-8').read()).call('get_cb')
params = {
"referer": "https://dun.163.com/trial/sense",
"zoneId": "CN31",
"dt": "zQijXPUjJgBERgVVBQaSNzvUtqMaOUd2",
"id": "5a0e2d04ffa44caba3f740e6a8b0fa84",
# "fp": "n44S/96DBCqkZK74/8NWVVUBmwUxPPh3nhqISBTuPp90GXvjNlsMH7xIsmYmT5DwK5\\L0YGJKDJLy7kR1+BfyA\\HVVmCR/P2n4n44qixhrZSTYgfgLm68IUWGeg1fJ0L51E68J8YH\\9U4/3zIKp7uUuv2G2nMIYHPTXm0xTicPfWjxBE:1741501721582",
"https": "true",
"type": "",
"width": "",
"sizeType": "undefined",
"version": "2.28.0",
"dpr": "1.5",
"dev": "1",
"cb": cb,
"ipv6": "false",
"runEnv": "10",
"group": "",
"scene": "",
"sdkVersion": "",
"loadVersion": "2.5.3",
"iv": "4",
"user": "",
# "irToken": "JEbnid6cWahANgBEEUPScnqDlh4lZacT",
"smsVersion": "v3",
"callback": "__JSONP_3b83yve_13"
}
response = requests.get(url, headers=headers, params=params)
response = "{" + response.text.split('({')[1].split('})')[0] + '}'
response = json.loads(response)
token = response['data']['token']
获取bg_url代码,并把背景图保存到本地:
token = '上面代码返回的token'
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Referer': 'https://dun.163.com/',
'Sec-Fetch-Dest': 'script',
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
'sec-ch-ua': '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
# 'Cookie': '_ga=GA.1.22e37b3006ff0.98176d9cabfc959599b3; Hm_lvt_4671c5d502135636b837050ec6d716ce=1741489509; __root_domain_v=.163.com; _qddaz=QD.741141489509450; hb_MA-93D5-9AD06EA4329A_source=cn.bing.com; Hm_lpvt_4671c5d502135636b837050ec6d716ce=1741514096',
}
cb = execjs.compile(open('main.js', encoding='utf-8').read()).call('get_cb')
params = {
'referer': 'https://dun.163.com/trial/sense',
'zoneId': 'CN31',
'dt': 'zQijXPUjJgBERgVVBQaSNzvUtqMaOUd2',
'irToken': 'HJAQbzuAy1RFNgBBQRbWcn6ZKVAhpKsi',
'id': '5a0e2d04ffa44caba3f740e6a8b0fa84',
# 'fp': 'n3Y4iD3JoinePcGGSYGjRO0vJr7c1+N2/ji0BOZtzuTyG3nuimqmEIOt37w3k13wLL5BNs8V0/G6v3BrPfn1HETPuQe4k4ZYdpxQUqlWZKqtpK1A2oozhJCy7XLsJbo\\n+2ISYCS9hdkwK\\Qv/r3KplIH5Nfcrh+57MHDZSk5dLkqMiY:1741514987530',
'https': 'true',
'type': '',
'version': '2.28.0',
'dpr': '1.5',
'dev': '1',
'cb': cb,
'ipv6': 'false',
'runEnv': '10',
'group': '',
'scene': '',
'lang': 'zh-CN',
'sdkVersion': '',
'loadVersion': '2.5.3',
'iv': '4',
'user': '',
'width': '286',
'audio': 'false',
'sizeType': '10',
'smsVersion': 'v3',
'token': token,
'callback': '__JSONP_r7ksz63_8',
}
response = requests.get('https://c.dun.163.com/api/v3/get', params=params, headers=headers)
response = "{" + response.text.split('({')[1].split('})')[0] + '}'
response = json.loads(response)
data = response['data']
bg_url = data['bg'][0]
with open('bg.jpg', 'wb') as f:
response = requests.get(url=bg_url).content
f.write(response)
Part-3:破解第四次请求参数data
这是第四次请求的请求参数:
先是接口定位:
关键字搜索:'ext'(搜索'data'也行),打上断点
停住了,确实在这里,既然用了webpack,那就一不做二不休,沿用到底
和之前一样的操作,先确定是哪个函数,在调用它,下面是整理后的代码:
但直接调用get_data_是会报错的,而且缺少很多环境,我的做法还是选择魔改源码
还是先看看源码部分,了解一下函数内部逻辑
'onMouseUp': function(I) {
var JB = BZ
, O = this[JB(0x31e)];
// if (JB(0x78f) === O[JB(0x327)])
// return void Object[JB(0x3a9)](O, {
// 'beginTime': 0x0
// });
// Object[JB(0x3a9)](O, this['initialDrag']); // 注释掉的部分大部分是初始化,对函数作用不大,会报环境错误
var Z = X[JB(0x3f2)](this['traceData'], q) // this['traceData']是基于滑块轨迹生成的, q 是定值50
, z = this['$store'][JB(0x2fa)][JB(0x167)] // z 是 token
, H = R(j(z, parseInt(this[JB(0x178)][JB(0x60c)][JB(0x7af)], 0xa) / this[JB(0x758)] * 0x64 + '')) // this[JB(0x178)][JB(0x60c)][JB(0x7af)] 疑似滑块终点x坐标(eg.116px); this[JB(0x758)] 定值286(width,可能每个电脑不一定都一样)
, f0 = P(X[JB(0x5da)](this['atomTraceData'], 0x2)); // this['atomTraceData'] 基于滑块轨迹生成的
this['onVerifyCaptcha']({
'data': JSON[JB(0x3b4)]({
'd': R(Z[JB(0x6a5)](':')),
'm': '',
'p': H,
'f': R(j(z, f0['join'](','))),
'ext': R(j(z, this['mouseDownCounts'] + ',' + this['traceData']['length'])) // this['mouseDownCounts'] 定值 1
})
});
},
注意看我的注释,看完源码发现,我们还有一些东西要解决:
1.traceData
2.atomTraceData
3.滑块的终点x坐标
控制台打印看看情况:
明显,atomTraceData为真轨迹,而 traceData是基于前者生成的。OK,找traceData的生成逻辑
搜索一下
打上断点,调试
var f2 = this[JY(0x69d)][JY(0x2fa)][JY(0x167)] // toekn值
, f3 = [Math['round'](z[JY(0x570)] < 0x0 ? 0x0 : z['dragX']), Math[JY(0xd7)](z[JY(0x7a6)] - z[JY(0x727)]), X[JY(0x3af)]() - z[JY(0x6ae)]]; // 滑块轨迹
this[JY(0x6a1)][JY(0x375)](f3); // atomTraceData.push(f3)
var f4 = j(f2, f3 + ''); // 这里生成的traceData的元素
this['traceData'][JY(0x375)](f4),
经过细心观察,发现,f3是滑块轨迹数组,我生成traceData的关键在于j函数,先搞定j函数再考虑滑块轨迹
和之前一样,找到调用的函数,再讲j函数导出
代码如下:
require('./env')
require('./loader')
x = window.loader(71)
console.log('x:::', x)
console.log(x())
function get_cb(){
return x()
}
J_f8 = window.loader(10)['xorEncode']
function get_track_encrypt(token, arr){
var track_encrypt = []
for(i of arr){
track_encrypt.push( J_f8(token, i + ''))
}
return track_encrypt
}
get_data_ = window.loader(36)["_options"]['methods']['onMouseUp']
console.log("get_data_:::", get_data_)
接下来考虑atomtraceData的生成,下面是我的代码(基本和之前一样,不多赘述),毕竟滑块轨迹的生成的基本上不是过滑块的重点,还是用打码平台(得塔云)
from PIL import Image
from io import BytesIO
import base64
import random
class DeTaYun:
def __init__(self, key, verify_idf_id, img_path):
self.key = key
self.img_path = img_path
self.verify_idf_id = verify_idf_id
# PIL图片保存为base64编码
def PIL_base64(self, img, coding='utf-8'):
img_format = img.format
if img_format == None:
img_format = 'JPEG'
format_str = 'JPEG'
if 'png' == img_format.lower():
format_str = 'PNG'
if 'gif' == img_format.lower():
format_str = 'gif'
if img.mode == "P":
img = img.convert('RGB')
if img.mode == "RGBA":
format_str = 'PNG'
img_format = 'PNG'
output_buffer = BytesIO()
img.save(output_buffer, quality=100, format=format_str)
byte_data = output_buffer.getvalue()
base64_str = 'data:image/' + img_format.lower() + ';base64,' + \
base64.b64encode(byte_data).decode(coding)
return base64_str
# 验证码识别接口
def parse_verify(self):
url = "https://bq1gpmr8.xiaomy.net//openapi/verify_code_identify/"
header = {"Content-Type": "application/json"}
data = {
# 用户的key
"key": self.key,
# 验证码类型
"verify_idf_id": self.verify_idf_id,
# 样例图片
"img_base64": self.PIL_base64(Image.open(self.img_path)),
}
# 发送请求调用接口
response = requests.post(url=url, json=data, headers=header)
# 获取响应数据,识别结果
return response.text
def get_slide_x():
key = "用自己的key"
verify_idf_id = '6'
img_path = './bg.jpg'
detayun = DeTaYun(key, verify_idf_id, img_path)
distance = int(json.loads(detayun.parse_verify())['data']['distance'] / 320 * 286) + 6
return distance
x_end = get_slide_x()
x = 0
y = 0
t = random.randint(70, 100)
count = 0
track = []
while x <= x_end:
track_item = [x, y, t]
track.append(track_item)
x += 1
t += random.randint(5, 10)
if count % 6 == 0:
y -= 1
count += 1
这样traceData,atomtraceData都有了(注意这里的x_end不是js中的,js中的x_end,要再减7),最后来解决data的生成
经过我们上面的操作,我们已经有了魔改get_data_函数的资本
下面是魔改后的代码:
'onMouseUp': function(traceData, atomTraceData, token, x_end) {
var JB = BZ
, O = this[JB(0x31e)];
// if (JB(0x78f) === O[JB(0x327)])
// return void Object[JB(0x3a9)](O, {
// 'beginTime': 0x0
// });
// Object[JB(0x3a9)](O, this['initialDrag']); // 注释掉的部分大部分是初始化,对函数作用不大,会报环境错误
var Z = X[JB(0x3f2)](traceData, q) // this['traceData']是基于滑块轨迹生成的, q 是定值50
, z = token // z 是 token
, H = R(j(z, parseInt(x_end + 'px', 0xa) / 286 * 0x64 + '')) // this[JB(0x178)][JB(0x60c)][JB(0x7af)] 疑似滑块终点x坐标(eg.116px); this[JB(0x758)] 定值286(width,可能每个电脑不一定都一样)
, f0 = P(X[JB(0x5da)](atomTraceData, 0x2)); // this['atomTraceData'] 基于滑块轨迹生成的
// this['onVerifyCaptcha']({
// 'data': JSON[JB(0x3b4)]({
// 'd': R(Z[JB(0x6a5)](':')),
// 'm': '',
// 'p': H,
// 'f': R(j(z, f0['join'](','))),
// 'ext': R(j(z, this['mouseDownCounts'] + ',' + this['traceData']['length'])) // this['mouseDownCounts'] 定值 1
// })
// });
return {
'data': JSON[JB(0x3b4)]({
'd': R(Z[JB(0x6a5)](':')),
'm': '',
'p': H,
'f': R(j(z, f0['join'](','))),
'ext': R(j(z, 1 + ',' + traceData['length'])) // this['mouseDownCounts'] 定值 1
})
}
},
这是main.js中的代码:
require('./env')
require('./loader')
x = window.loader(71)
console.log('x:::', x)
console.log(x())
function get_cb(){
return x()
}
J_f8 = window.loader(10)['xorEncode']
function get_track_encrypt(token, arr){
var track_encrypt = []
for(i of arr){
track_encrypt.push( J_f8(token, i + ''))
}
return track_encrypt
}
get_data_ = window.loader(36)["_options"]['methods']['onMouseUp']
function get_data(track, track_enc, token, x_end){
return get_data_(track, track_enc, token, x_end)
}
最后再进行代码整合:
python代码:
main.py
import requests
import execjs
import json
from PIL import Image
from io import BytesIO
import base64
import random
class DeTaYun:
def __init__(self, key, verify_idf_id, img_path):
self.key = key
self.img_path = img_path
self.verify_idf_id = verify_idf_id
# PIL图片保存为base64编码
def PIL_base64(self, img, coding='utf-8'):
img_format = img.format
if img_format == None:
img_format = 'JPEG'
format_str = 'JPEG'
if 'png' == img_format.lower():
format_str = 'PNG'
if 'gif' == img_format.lower():
format_str = 'gif'
if img.mode == "P":
img = img.convert('RGB')
if img.mode == "RGBA":
format_str = 'PNG'
img_format = 'PNG'
output_buffer = BytesIO()
img.save(output_buffer, quality=100, format=format_str)
byte_data = output_buffer.getvalue()
base64_str = 'data:image/' + img_format.lower() + ';base64,' + \
base64.b64encode(byte_data).decode(coding)
return base64_str
# 验证码识别接口
def parse_verify(self):
url = "https://bq1gpmr8.xiaomy.net//openapi/verify_code_identify/"
header = {"Content-Type": "application/json"}
data = {
# 用户的key
"key": self.key,
# 验证码类型
"verify_idf_id": self.verify_idf_id,
# 样例图片
"img_base64": self.PIL_base64(Image.open(self.img_path)),
}
# 发送请求调用接口
response = requests.post(url=url, json=data, headers=header)
# 获取响应数据,识别结果
return response.text
def get_slide_x():
"""获取滑块终点位置的x坐标"""
key = "用自己的key"
verify_idf_id = '6'
img_path = './bg.jpg'
detayun = DeTaYun(key, verify_idf_id, img_path)
distance = int(json.loads(detayun.parse_verify())['data']['distance'] / 320 * 286) + 6
return distance
def get_token():
"""发送 ’get?referer‘ 请求,获取token值"""
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Referer": "https://dun.163.com/",
"Sec-Fetch-Dest": "script",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"sec-ch-ua": "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\""
}
url = "https://c.dun.163.com/api/v3/get"
cb = execjs.compile(open('main.js', encoding='utf-8').read()).call('get_cb')
params = {
"referer": "https://dun.163.com/trial/sense",
"zoneId": "CN31",
"dt": "zQijXPUjJgBERgVVBQaSNzvUtqMaOUd2",
"id": "5a0e2d04ffa44caba3f740e6a8b0fa84",
"https": "true",
"type": "",
"width": "",
"sizeType": "undefined",
"version": "2.28.0",
"dpr": "1.5",
"dev": "1",
"cb": cb,
"ipv6": "false",
"runEnv": "10",
"group": "",
"scene": "",
"sdkVersion": "",
"loadVersion": "2.5.3",
"iv": "4",
"user": "",
"smsVersion": "v3",
"callback": "__JSONP_3b83yve_13"
}
response = requests.get(url, headers=headers, params=params)
response = "{" + response.text.split('({')[1].split('})')[0] + '}'
response = json.loads(response)
return response['data']['token']
def get_bg_url(token):
"""获取背景图网址"""
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Referer': 'https://dun.163.com/',
'Sec-Fetch-Dest': 'script',
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
'sec-ch-ua': '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
cb = execjs.compile(open('main.js', encoding='utf-8').read()).call('get_cb')
params = {
'referer': 'https://dun.163.com/trial/sense',
'zoneId': 'CN31',
'dt': 'zQijXPUjJgBERgVVBQaSNzvUtqMaOUd2',
'irToken': 'HJAQbzuAy1RFNgBBQRbWcn6ZKVAhpKsi',
'id': '5a0e2d04ffa44caba3f740e6a8b0fa84',
'https': 'true',
'type': '',
'version': '2.28.0',
'dpr': '1.5',
'dev': '1',
'cb': cb,
'ipv6': 'false',
'runEnv': '10',
'group': '',
'scene': '',
'lang': 'zh-CN',
'sdkVersion': '',
'loadVersion': '2.5.3',
'iv': '4',
'user': '',
'width': '286',
'audio': 'false',
'sizeType': '10',
'smsVersion': 'v3',
'token': token,
'callback': '__JSONP_r7ksz63_8',
}
response = requests.get('https://c.dun.163.com/api/v3/get', params=params, headers=headers)
response = "{" + response.text.split('({')[1].split('})')[0] + '}'
response = json.loads(response)
data = response['data']
return data['bg'][0]
def get_bg(bg_url):
"""将背景图保存到本地"""
response = requests.get(url=bg_url)
with open('bg.jpg', 'wb') as f:
f.write(response.content)
def get_track_raw(x_end):
"""生成原始的,未经加工的滑块轨迹"""
x = 0
y = 0
t = random.randint(70, 100)
count = 0
track = []
while x <= x_end:
track_item = [x, y, t]
track.append(track_item)
x += 1
t += random.randint(5, 10)
if count % 6 == 0:
y -= 1
count += 1
return track
def get_track_enc(token, track):
"""获得经过加密处理的滑块轨迹"""
return execjs.compile(open('main.js', encoding='utf-8').read()).call('get_track_encrypt', token, track)
def get_data(track, track_encrypt, token, x_end):
"""护额的请求参数data"""
return execjs.compile(open('main.js', encoding='utf-8').read()).call('get_data', track, track_encrypt, token, x_end)['data']
def get_verify(token, data):
"""完成验证请求"""
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Referer": "https://dun.163.com/",
"Sec-Fetch-Dest": "script",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"sec-ch-ua": "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\""
}
url = "https://c.dun.163.com/api/v3/check"
cb = execjs.compile(open('main.js', encoding='utf-8').read()).call('get_cb')
params = {
"referer": "https://dun.163.com/trial/sense",
"zoneId": "CN31",
"dt": "zQijXPUjJgBERgVVBQaSNzvUtqMaOUd2",
"id": "5a0e2d04ffa44caba3f740e6a8b0fa84",
"token": token,
"data": data,
"width": "286",
"type": "2",
"version": "2.28.0",
"cb": cb,
"user": "",
"extraData": "",
"bf": "0",
"runEnv": "10",
"sdkVersion": "",
"loadVersion": "2.5.3",
"iv": "4",
"callback": "__JSONP_wootm5k_91"
}
response = requests.get(url, headers=headers, params=params)
response = "{" + response.text.split('({')[1].split('})')[0] + '}'
response = json.loads(response)
result = response['data']['result']
if result:
print('validate:::', response['data']['validate'])
return response['data']['validate']
else:
return 0
def main():
"""总操作"""
token = get_token()
bg_url = get_bg_url(token=token)
get_bg(bg_url=bg_url)
x_end = get_slide_x()
track = get_track_raw(x_end=x_end)
track_enc = get_track_enc(token=token, track=track)
x_end -= 7
data = get_data(track=track, track_encrypt=track_enc, token=token, x_end=x_end)
res = get_verify(token=token, data=data)
count = 0
while res == 0:
print(f"第{count}次验证失败,正在重新验证!!!")
token = get_token()
bg_url = get_bg_url(token=token)
get_bg(bg_url=bg_url)
x_end = get_slide_x()
track = get_track_raw(x_end=x_end)
track_enc = get_track_enc(token=token, track=track)
x_end -= 7
data = get_data(track=track, track_encrypt=track_enc, token=token, x_end=x_end)
res = get_verify(token=token, data=data)
count += 1
print("程序结束!!!")
if __name__ == "__main__":
main()
JS代码:
main.js
require("./env")
require('./loader')
x = window.loader(71)
function get_cb(){
return x()
}
J_f8 = window.loader(10)['xorEncode']
function get_track_encrypt(token, arr){
var track_encrypt = []
for(i of arr){
track_encrypt.push( J_f8(token, i + ''))
}
return track_encrypt
}
get_data_ = window.loader(36)["_options"]['methods']['onMouseUp']
function get_data(track, track_enc, token, x_end){
return get_data_(track, track_enc, token, x_end)
}
env.js
window = self = top = global
delete global; // 记得用 _global = global 备份一下
delete Buffer;
delete __filename;
delete __dirname;
screen = {}
location = {
"ancestorOrigins": {},
"href": "https://dun.163.com/trial/sense",
"origin": "https://dun.163.com",
"protocol": "https:",
"host": "dun.163.com",
"hostname": "dun.163.com",
"port": "",
"pathname": "/trial/sense",
"search": "",
"hash": ""
}
navigator = {
appCodeName: "Mozilla",
appName: "Netscape",
appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
userAgent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
}
history = {}
div = {
addEventListener: function (){},
getAttribute: function (a){
// console.log("div getAttribute:::",a)
}
}
body = {}
document = {
createElement :function (ele){
// console.log("document createElement:::", ele)
if(ele==="div"){
return div
}
},
body : body,
getElementById: function (ele){
// console.log('document getElementById:::', ele)
}
}
ActiveXObject = function () {}
XMLHttpRequest = function () {}
addEventListener = function () {}
attachEvent = function () {}
setTimeout = function (){}
setInterval = function (){}
loader.js注意按照我上面的代码魔改就行(文件太大了,就不放这了)
最后再运行一下,看看效果:
拿到validate,成功搞定
END
这一篇博客和上一篇做法几乎相同,希望对读者有帮助,有问题评论区留言
最后感谢观看,原创不易,点赞关注支持一下!