网易有道翻译逆向案例
- 本次案例逆向的是网易有道云翻译
- 用到的知识包括
- requests 模块及方法
- md5加密
- js代码环境的补全
【一】分析网站
(1)网站页面如图
(2)抓包
(3)分析抓到的包
- 逐个查看每个包的标头和载荷
- 在
webtranslate
- 这个包的请求头中发现其为post请求
- 这个包的载荷中发现了其携带有很多参数
(4)分析载荷
i: run
from: auto
to:
dictResult: true
keyid: webfanyi
sign: a9856197613117e6524edc4b5076bd55
client: fanyideskweb
product: webfanyi
appVersion: 1.0.0
vendor: web
pointParam: client,mysticTime,product
mysticTime: 1683545856511
keyfrom: fanyi.web
-
在载荷中发现了很多参数
-
对网站二次请求抓到的该包携带的参数进行对比
i: rain
from: auto
to:
domain: 0
dictResult: true
keyid: webfanyi
sign: 60d4d3ab8995ecacee1767824036d8a2
client: fanyideskweb
product: webfanyi
appVersion: 1.0.0
vendor: web
pointParam: client,mysticTime,product
mysticTime: 1683546118762
keyfrom: fanyi.web
- 通过对比可以发现,其中有两个参数发生了变化
- sign : 猜测这是一个加密后的数据
- mysticTime : 这是一个时间戳生成的
【二】分析sign并逆向补充
(1)分析sign 的由来
- 全局搜索sign值:
-
通过搜索后的结果猜测其可能存在过的文件
-
其最可能是js文件通过代码生成,所以排除css、html等文件
-
-
进入第一个文件进行尝试分析
-
可以看到当前有一个sign函数,有传参进去,t为时间戳,e为参数。
-
将此处打断点,重新请求,看其请求是否会被卡主
-
-
可以很明显的发现其被卡主
(2)参数补充 - t ,逆向生成sign值
-
将鼠标放到参数t上查看
-
发现其生成就在上一行代码
const t = (new Date).getTime();
-
继续向上查看代码
-
发现有两行代码很可疑,将其拷贝并补充代码
function g(e) { return r.a.createHash("md5").update(e).digest() } function v(e) { return r.a.createHash("md5").update(e.toString()).digest("hex") } function h(e, t) { return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`) }
- 将第一个传e为参的函数调用删除,因为第二个传e为参数的函数也进行同样类似的更新操作
function v(e) { return r.a.createHash("md5").update(e.toString()).digest("hex") } function h(e, t) { return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`) }
- 查看第二和 h 函数,补全参数 。
-
已经有的参数 e 、 t 。需要补全 d 、 u
-
向上翻看代码
const d = "fanyideskweb" , u = "webfanyi" , m = "client,mysticTime,product" , p = "1.0.0" , b = "web" , f = "fanyi.web";
-
其中含有固定参数 d 、 u
-
补全后的代码
// t 参数的声明 const t = (new Date).getTime(); function v(e) { return r.a.createHash("md5").update(e.toString()).digest("hex") } var d = "fanyideskweb" var u = "webfanyi" function h(e, t) { return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`) } h() //ReferenceError: r is not defined
-
这里运行后会报错,错误显示 r 没有被定义
-
这里采用的办法是 利用 crypto-js 补全环境
-
-
先声明 调用 该模块
var Cry = require('crypto-js')
-
再修改该部分代码
//修改前 function v(e) { return r.a.createHash("md5").update(e.toString()).digest("hex") } //前面代码的大概意思是运用MD5加密算法将 e 加密混淆后转换为字符串 //修改后 function v(e) { return Cry.MD5(e).toString() } //修改后用 crypto-js(在js代码中的crypto算法) 这个 模块将 e 加密混淆后转换为字符串
-
补全后的代码
var Cry = require('crypto-js') // t 参数的声明 const t = (new Date).getTime(); // function v(e) { // return r.a.createHash("md5").update(e.toString()).digest("hex") // } function v(e) { return Cry.MD5(e).toString() } var d = "fanyideskweb" var u = "webfanyi" function h(e, t) { return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`) } h() // 无报错
(3)参数补充 - e ,逆向生成sign值
-
观察发现 e 为固定值
e = "fsdsogkndfokasodnaso"
-
补全代码
var Cry = require('crypto-js') // t 参数的声明 const t = (new Date).getTime(); // function v(e) { // return r.a.createHash("md5").update(e.toString()).digest("hex") // } function v(e) { return Cry.MD5(e).toString() } var d = "fanyideskweb" var u = "webfanyi" function h(e, t) { return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`) } function sign(){ var e = "fsdsogkndfokasodnaso" return h(e,t) } console.log(sign()) //调用sigh函数,查看其生成代码 //b73eba683a8cafb121cbf292d122e628
(4)如何检验代码是否改写成功?
-
将参数 t 写死 ,观察生成值是否相同
var Cry = require('crypto-js') // t 参数的声明 // const t = (new Date).getTime(); var t = '1683546118762' // function v(e) { // return r.a.createHash("md5").update(e.toString()).digest("hex") // } function v(e) { return Cry.MD5(e).toString() } var d = "fanyideskweb" var u = "webfanyi" function h(e, t) { return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`) } function sign(){ var e = "fsdsogkndfokasodnaso" return h(e,t) } console.log(sign()) // 二者 相同
【三】Python 部分代码
(1)定义函数 ---- 获取sign值部分
-
模块的导入
# requests 请求模块 import requests # 随机UA模块 from fake_useragent import UserAgent # 这里是执行js代码的相关模块 import subprocess from functools import partial # 必须先定义这个变量 再引入 execis模块 否则会报错 subprocess.Popen = partial(subprocess.Popen, encoding='utf-8') import execjs
-
定义
get_sign()
部分代码def get_sign(): # 创建node对象 node = execjs.get() # 读取到 js 代码,并以 utf-8编码方式打开 with open('01.js', encoding='utf-8') as f: # JS 源文件编译 ctx = node.compile(f.read()) # 调用函数 sign = ctx.eval('run()') # sign[0]:列表中的sign值, sign[1]:列表中的t值 return sign[0], sign[1]
-
这里还需要重写
01.js
文件,定义run()
函数var Cry = require('crypto-js') // t 参数的声明 // const t = (new Date).getTime(); var t = '1683546118762' // function v(e) { // return r.a.createHash("md5").update(e.toString()).digest("hex") // } function v(e) { return Cry.MD5(e).toString() } var d = "fanyideskweb" var u = "webfanyi" function h(e, t) { return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`) } function sign(){ var e = "fsdsogkndfokasodnaso" // 返回 sign 值 return h(e,t) } function run(){ // 调用 sign() 函数,返回 sign 值和 t 值 return [sign(),t] }
(2)伪装请求头
-
在
webtranslate
文件中我们可以发现请求标头(如果不知道那个不需要就全都写上)Accept: application/json, text/plain, */* Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Cache-Control: no-cache Connection: keep-alive Content-Length: 247 Content-Type: application/x-www-form-urlencoded Cookie: OUTFOX_SEARCH_USER_ID=-2102182500@10.110.96.154; OUTFOX_SEARCH_USER_ID_NCOO=1723343714.3489342 Host: dict.youdao.com Origin: https://fanyi.youdao.com Pragma: no-cache Referer: https://fanyi.youdao.com/ sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-site User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
-
这里
headers
中的"User-Agent"
采用了fake随机获取 -
这里的cookie进行了单独提取
def spider(eng): # 请求头中的部分参数 headers = { 'Accept': 'application/json, text/plain, */*', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Content-Length': '239', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'dict.youdao.com', 'Origin': 'https://fanyi.youdao.com', 'Pragma': 'no-cache', 'Referer': 'https://fanyi.youdao.com/', 'sec-ch-ua': "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "Windows", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-site", "User-Agent": UserAgent().random, } # 请求头中的cookie cookies = { 'OUTFOX_SEARCH_USER_ID': '-2102182500@10.110.96.154', 'OUTFOX_SEARCH_USER_ID_NCOO': '1723343714.3489342', } # 需要请求的请求页 url page_url = 'https://dict.youdao.com/webtranslate' # 因为是携带参数的请求,所以是post请求 # post请求需要携带请求参数(分析已经得知 sign 和 t 为变量,已经做了逆向获取) # 从 get_sign() 函数中获取到返回值 sign,mysticTime sign, mysticTime = get_sign() # 将 sign,mysticTime 添加到请求携带参数中 data = { "i": str(eng), "from": "auto", "to": "", "dictResult": "true", "keyid": "webfanyi", "sign": sign, "client": "fanyideskweb", "product": "webfanyi", "appVersion": "1.0.0", "vendor": "web", "pointParam": "client,mysticTime,product", "mysticTime": mysticTime, "keyfrom": "fanyi.web", } # 对请求页发起请求 response = requests.post(url=page_url, headers=headers, data=data, cookies=cookies) # 返回请求响应内容 return response.text
-
(3)定义主函数入口
if __name__ == '__main__':
res = spider('rain')
print(res)
【四】逆向解密密文数据
(1)查找 sign
值的去向
-
从刚才的断点依次执行代码,找到加密后的密文数据,即
webtranslate
的响应内容- 一般特征是 json.parse()
-
在这行代码我们发现了这个方法
-
并且也发现了其上面获取到的数据
o
o = "Z21kD9ZK1ke6ugku2ccWu-MeDWh3z252xRTQv-wZ6jddVo3tJLe7gIXz4PyxGl73nSfLAADyElSjjvrYdCvEP4pfohVVEX1DxoI0yhm36ytQNvu-WLU94qULZQ72aml6x7-sEgf3E8xxPy8fNgUR0PtLyLVXDnp0W_hhc-8PxqHNoVtmgMHMW8tBEi7Xh66zR8dDM3Ga-WTkqp4bVDIJHqVq7sbwGnCYrLIE-UQQNHZPv0XjhUoKPsJcfHXQzoNiGzoWtcYwV_z2Pwc9VS_vUfwlpfboIjBuCpnQ7QaUUL3cvi2pHSzznsXHIwr3B9RQCkiGZ5hW1_etmLOma7LtBnWLzWm9vj9qHHoAXTW6ZSXKvSaKWpI4owfVpffytuYADNG7n7H0Vg5P93mfAMNOMRKN4dNm0HkeWIKHFPLNLgH0SbZWk3kVnuJHw0Xsa32GLb3kcmjui6srexVN1PI3KpyCIL29fSPyLM9p5IHw8pZ5rSOAe6Z-pHwrcnpdnLhf8d-xRcxy2k6Aq_OLh4pw-DpoS53nPPBW6xIlFXH4itoLTlDjXq3Q64mujS7UTjuy8XME5aegFhrkDL86D18K0TeOJQtlZYtQNJMvqRcyVakDBEjph-_8r6jBhfLVr2aSM5BEs_cS_rh4tLXp48vte1P2YkuDF2_GQlh7fkJpSuSko9cObi8xvxULk1heIKOxpkRlwK187vD-E1VNMrulR5YmZXtQcS9E1g40f8qLHByyUULfY41SCepWQgvrwI3n4KAd6Pui5INE3iK-n9_unJ4L9H0HUugLoEmJA9F5ylal-pvhafyHlunzfv3os2lzccP-e04GrL_MZdqnGa_c019lEFqZKist2PxIIdkM3QpGedOXsx-guqfAjXaycFpQJn7DH32VDTVEbRGdcNaMZvh0lS4-ExynCiPLvYSX8Xvxpl4lffiknNZ7gYf56S8uKMAsGP3gS040ZV7mLo90wtlXe3cZekKNEpTR5OzqPeL6_CUTvjaE75o7TWE6BjmtGvA10fcuTZuZep73PgnvkU-HP447QGc_SqxuD98ZoITYkf11HsX9WeEgIZYkN-CCAQ1DcNiyzcJNIB3rdrYj9_5hFAwrKIPU1kYPnBQVsqb3p2YVXARZK7LqNBQfDHIZ5k3_boqWP2ZbRfr_Sdy5Yw=="
-
同时看到了我们想要的一串数据
"{"code":0,"dictResult":{"ec":{"exam_type":["初中","高中","CET4","CET6","考研"],"word":{"usphone":"reɪn","ukphone":"reɪn","ukspeech":"rain&type=1","trs":[{"pos":"n.","tran":"雨,雨水;(热带地区的)雨季(the rains);(降雨般的)一阵,(大量的)降落物"},{"pos":"v.","tran":"下雨;(使)大量降落,雨点般落下;(使)如雨般地猛击"},{"tran":"【名】 (Rain)(法)兰,(英)雷恩,(罗、捷)赖恩(人名)"}],"wfs":[{"wf":{"name":"复数","value":"rains"}},{"wf":{"name":"第三人称单数","value":"rains"}},{"wf":{"name":"现在分词","value":"raining"}},{"wf":{"name":"过去式","value":"rained"}},{"wf":{"name":"过去分词","value":"rained"}}],"return-phrase":"rain","usspeech":"rain&type=2"}}},"translateResult":[[{"tgt":"雨","src":"rain","tgtPronounce":"yŭ"}]],"type":"en2zh-CHS"}"
(2)逆向补全代码
-
首先 将这部分代码 扣出来
const n = an["a"].decodeData(o, sn["a"].state.text.decodeKey, sn["a"].state.text.decodeIv)
-
由此我们可以看到,我们需要两个 固定值
decodeKey
(秘钥)和decodeIv
(偏移量) -
将鼠标悬停在其上面就可以看到两个相应的值
decodeKey
(解密秘钥)
"ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
decodeIv
(偏移量)
"ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
-
我们还需要知道解密的算法
-
将鼠标悬停在
an["a"].decodeData
上查看其解密js代码 -
发现一个箭头函数
S = (t, o, n) => { if (!t) return null; const a = e.alloc(16, g(o)) , c = e.alloc(16, g(n)) , i = r.a.createDecipheriv("aes-128-cbc", a, c); let s = i.update(t, "base64", "utf-8"); return s += i.final("utf-8"), s }
-
格式化
function decode(t, o, n) { if (!t) return null; const a = e.alloc(16, g(o)) , c = e.alloc(16, g(n)) , i = r.a.createDecipheriv("aes-128-cbc", a, c); let s = i.update(t, "base64", "utf-8"); return s += i.final("utf-8"), s }
-
由此,分析
- 我们需要知道
e.alloc
- 还需要知道
r.a.
- 我们需要知道
-
(3)解决办法
-
e.alloc
-
由Buffer替换即可
const a = Buffer.alloc(16, g(o)) , c = Buffer.alloc(16, g(n))
-
r.a.
-
生成加密算法对象进行加密
// 导入crypto模块 const crypto = require('crypto') // 创建算法对象 function g(e) { return crypto.createHash("md5").update(e).digest() } function decode(t, o, n) { if (!t) return null; const a = Buffer.alloc(16, g(o)) , c = Buffer.alloc(16, g(n)) , i = crypto.createDecipheriv("aes-128-cbc", a, c); let s = i.update(t, "base64", "utf-8"); return s += i.final("utf-8"), s }
(4)拼接数据
// 导入crypto模块
const crypto = require('crypto')
// 创建算法对象
function g(e) {
return crypto.createHash("md5").update(e).digest()
}
function decode(t, o, n) {
if (!t)
return null;
const a = Buffer.alloc(16, g(o))
, c = Buffer.alloc(16, g(n))
, i = crypto.createDecipheriv("aes-128-cbc", a, c);
let s = i.update(t, "base64", "utf-8");
return s += i.final("utf-8"),
s
}
// 声明 变量 解密秘钥和偏移量
var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
// 定义 加密后的密文数据
var n = o = "Z21kD9ZK1ke6ugku2ccWu-MeDWh3z252xRTQv-wZ6jddVo3tJLe7gIXz4PyxGl73nSfLAADyElSjjvrYdCvEP4pfohVVEX1DxoI0yhm36ytQNvu-WLU94qULZQ72aml6x7-sEgf3E8xxPy8fNgUR0PtLyLVXDnp0W_hhc-8PxqHNoVtmgMHMW8tBEi7Xh66zR8dDM3Ga-WTkqp4bVDIJHqVq7sbwGnCYrLIE-UQQNHZPv0XjhUoKPsJcfHXQzoNiGzoWtcYwV_z2Pwc9VS_vUfwlpfboIjBuCpnQ7QaUUL3cvi2pHSzznsXHIwr3B9RQCkiGZ5hW1_etmLOma7LtBnWLzWm9vj9qHHoAXTW6ZSXKvSaKWpI4owfVpffytuYADNG7n7H0Vg5P93mfAMNOMRKN4dNm0HkeWIKHFPLNLgH0SbZWk3kVnuJHw0Xsa32GLb3kcmjui6srexVN1PI3KpyCIL29fSPyLM9p5IHw8pZ5rSOAe6Z-pHwrcnpdnLhf8d-xRcxy2k6Aq_OLh4pw-DpoS53nPPBW6xIlFXH4itoLTlDjXq3Q64mujS7UTjuy8XME5aegFhrkDL86D18K0TeOJQtlZYtQNJMvqRcyVakDBEjph-_8r6jBhfLVr2aSM5BEs_cS_rh4tLXp48vte1P2YkuDF2_GQlh7fkJpSuSko9cObi8xvxULk1heIKOxpkRlwK187vD-E1VNMrulR5YmZXtQcS9E1g40f8qLHByyUULfY41SCepWQgvrwI3n4KAd6Pui5INE3iK-n9_unJ4L9H0HUugLoEmJA9F5ylal-pvhafyHlunzfv3os2lzccP-e04GrL_MZdqnGa_c019lEFqZKist2PxIIdkM3QpGedOXsx-guqfAjXaycFpQJn7DH32VDTVEbRGdcNaMZvh0lS4-ExynCiPLvYSX8Xvxpl4lffiknNZ7gYf56S8uKMAsGP3gS040ZV7mLo90wtlXe3cZekKNEpTR5OzqPeL6_CUTvjaE75o7TWE6BjmtGvA10fcuTZuZep73PgnvkU-HP447QGc_SqxuD98ZoITYkf11HsX9WeEgIZYkN-CCAQ1DcNiyzcJNIB3rdrYj9_5hFAwrKIPU1kYPnBQVsqb3p2YVXARZK7LqNBQfDHIZ5k3_boqWP2ZbRfr_Sdy5Yw=="
// 利用解密函数进行解密
console.log(decode(n, k, iv))
/*{"code":0,"dictResult":{"ec":{"exam_type":["初中","高中","CET4","CET6","考研"],"word":{"usphone":"reɪn","ukphone":"reɪn","ukspeech":"rain&type=1","trs":[{"pos":"n.","tran":"雨,雨水
;(热带地区的)雨季(the rains);(降雨般的)一阵,(大量的)降落物"},{"pos":"v.","tran":"下雨;(使)大量降落,雨点般落下;(使)如雨般地猛击"},{"tran":"【名】 (Rain)(法)兰,
(英)雷恩,(罗、捷)赖恩(人名)"}],"wfs":[{"wf":{"name":"复数","value":"rains"}},{"wf":{"name":"第三人称单数","value":"rains"}},{"wf":{"name":"现在分词","value":"raining"}},{"wf"
:{"name":"过去式","value":"rained"}},{"wf":{"name":"过去分词","value":"rained"}}],"return-phrase":"rain","usspeech":"rain&type=2"}}},"translateResult":[[{"tgt":"雨","src":"rain","tg
tPronounce":"yŭ"}]],"type":"en2zh-CHS"}
*/
- 通过打印结果 可以看到我们的解密函数正常运行
【五】Python部分代码
(1)定义 tran_data()
函数
- 其参数为,加密后的密文数据
def tran_data(data):
# 创建 node 对象
node = execjs.get()
# 读取到 js 代码,并以 utf-8编码方式打开
with open('02.js', encoding='utf-8') as f:
# JS 源文件编译
ctx = node.compile(f.read())
# 调用函数 , 并向其传参 ,获得响应数据
t = ctx.eval(f'run("{data}")')
return t
(2)在 js 文件中定义主函数 run()
// 导入crypto模块
const crypto = require('crypto')
// 创建算法对象
function g(e) {
return crypto.createHash("md5").update(e).digest()
}
function decode(t, o, n) {
if (!t)
return null;
const a = Buffer.alloc(16, g(o))
, c = Buffer.alloc(16, g(n))
, i = crypto.createDecipheriv("aes-128-cbc", a, c);
let s = i.update(t, "base64", "utf-8");
return s += i.final("utf-8"),
s
}
// 声明 变量 解密秘钥和偏移量
var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
// 参数为加密后的密文数据
function run(encode_data) {
return (decode(encode_data, k, iv))
}
【六】代码整合
(1)Python代码部分
# requests 请求模块
import requests
# 随机UA模块
from fake_useragent import UserAgent
# json模块
import json
# 这里是执行js代码的相关模块
import subprocess
from functools import partial
# 必须先定义这个变量 再引入 execis模块 否则会报错
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
import execjs
def get_sign():
node = execjs.get()
with open('sign.js', encoding='utf-8') as f:
ctx = node.compile(f.read())
sign = ctx.eval('run()')
return sign[0], sign[1]
def tran_data(data):
# 创建 node 对象
node = execjs.get()
# 读取到 js 代码,并以 utf-8编码方式打开
with open('decode.js', encoding='utf-8') as f:
# JS 源文件编译
ctx = node.compile(f.read())
# 调用函数 , 并向其传参 ,获得响应数据
t = ctx.eval(f'run("{data}")')
return t
def spider(eng):
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Length': '239',
'Content-Type': 'application/x-www-form-urlencoded',
'Host': 'dict.youdao.com',
'Origin': 'https://fanyi.youdao.com',
'Pragma': 'no-cache',
'Referer': 'https://fanyi.youdao.com/',
'sec-ch-ua': "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "Windows",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": UserAgent().random,
}
cookies = {
'OUTFOX_SEARCH_USER_ID': '-2102182500@10.110.96.154',
'OUTFOX_SEARCH_USER_ID_NCOO': '1723343714.3489342',
}
page_url = 'https://dict.youdao.com/webtranslate'
sign, my_time = get_sign()
data = {
"i": str(eng),
"from": "auto",
"to": "",
"dictResult": "true",
"keyid": "webfanyi",
"sign": sign,
"client": "fanyideskweb",
"product": "webfanyi",
"appVersion": "1.0.0",
"vendor": "web",
"pointParam": "client,mysticTime,product",
"mysticTime": my_time,
"keyfrom": "fanyi.web",
}
response = requests.post(url=page_url, headers=headers, data=data, cookies=cookies)
return response.text
# print(response.text)
# print(f'response:::{response}')
if __name__ == '__main__':
while True:
eng = input(f"请输入英文单词::")
encode_text = spider(eng)
# print(encode_text)
res = json.loads(tran_data(encode_text))["dictResult"]["ec"]["word"]["trs"]
for i in res:
print(i)
(2)sign代码部分
var Cry = require('crypto-js')
// t 参数的声明
// const t = (new Date).getTime();
var t = '1683546118762'
// function v(e) {
// return r.a.createHash("md5").update(e.toString()).digest("hex")
// }
function v(e) {
return Cry.MD5(e).toString()
}
var d = "fanyideskweb"
var u = "webfanyi"
function h(e, t) {
return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
}
function sign(){
var e = "fsdsogkndfokasodnaso"
return h(e,t)
}
function run(){
return [sign(),t]
}
(3)decode代码部分
// 导入crypto模块
const crypto = require('crypto')
// 创建算法对象
function g(e) {
return crypto.createHash("md5").update(e).digest()
}
function decode(t, o, n) {
if (!t)
return null;
const a = Buffer.alloc(16, g(o))
, c = Buffer.alloc(16, g(n))
, i = crypto.createDecipheriv("aes-128-cbc", a, c);
let s = i.update(t, "base64", "utf-8");
return s += i.final("utf-8"),
s
}
// 声明 变量 解密秘钥和偏移量
var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
// 参数为加密后的密文数据
function run(encode_data) {
return (decode(encode_data, k, iv))
}