YD翻译接口由于其耐造,无混淆的特点,是一个很好的入门js逆向的练手目标.
js逆向相较于c/c++等编译为机器码的语言,可以较为容易的获得源码级的信息,所以逆向的重点就在于在调用堆栈中寻找到关键的加密,解密代码.将代码copy到本地后,进行补环境,就可运行.
逆向步骤
首先在翻译栏中进行输入 在网络下的xhr中可以看到发送的所有数据包 查看数据包内容 确定webtranslate为关键数据包
再次在翻译栏中输入内容 对比两次的webtranslate 发现只有sign值进行变化 确定此次逆向的字段为sign
对数据包请求网址进行搜索
定位到关键代码
-
请求参数:动态合并的对象
-
请求头:application/x-www-form-urlencoded
点进k找到加密逻辑
将JS代码拿到本地 使用相同数据在浏览器和本地进行测试 得到相同的加密结果
编写python和js代码 成功获得返回后的数据 但是还需要解密
在调用栈中找到解密相关代码
进入decodeData 可知Key, Iv为固定值
成果展示
视频:
视频托管在国外视频床 需要科学上网查看~
源码展示
reverse_youdao.py
import time
import requests
import execjs
import json
def get_youdao_translate(input_text):
r = 'fsdsogkndfokasodnaso'
t = str(int(time.time() * 1000))
with open('get_sign.js', encoding='utf-8') as f:
js_code = f.read()
ctx = execjs.compile(js_code)
sign = ctx.call('k', t, r)
# Cookies和Headers
cookies = {
'OUTFOX_SEARCH_USER_ID': '-2092448371@223.72.91.242',
'OUTFOX_SEARCH_USER_ID_NCOO': '1775499115.7853804',
'UM_distinctid': '1931f3f545862d-0988056a045645-26011951-190140-1931f3f54592b79',
'_ga': 'GA1.2.1829450822.1731688769',
'_ga_ZSH399DT89': 'GS1.2.1731688769.1.0.1731688769.60.0.0',
'_uetsid': 'ad128800b4c611ef9c0425bbcf0ddd4c',
'_uetvid': 'a881d850a0c611ef8faca1ec8a80ae8c',
'DICT_DOCTRANS_SESSION_ID': 'Y2M5YWQxYTQtOGM2Ny00OGZjLWFiNmItN2JjOTRhMzhiNWI5',
}
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'https://fanyi.youdao.com',
'Referer': 'https://fanyi.youdao.com/',
'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/130.0.0.0 Safari/537.36',
'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
data = {
'i': input_text,
'from': 'auto',
'to': '',
'useTerm': 'false',
'domain': '0',
'dictResult': 'true',
'keyid': 'webfanyi',
'sign': sign,
'client': 'fanyideskweb',
'product': 'webfanyi',
'appVersion': '1.0.0',
'vendor': 'web',
'pointParam': 'client,mysticTime,product',
'mysticTime': t,
'keyfrom': 'fanyi.web',
'mid': '1',
'screen': '1',
'model': '1',
'network': 'wifi',
'abtest': '0',
'yduuid': 'abcdefg',
}
response = requests.post('https://dict.youdao.com/webtranslate', cookies=cookies, headers=headers, data=data)
t = 'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl'
a = 'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4'
with open('decry.js', encoding='utf-8') as f:
js_code = f.read()
ctx = execjs.compile(js_code)
back = ctx.call('de', response.text, t, a)
result = json.loads(back)
if 'dictResult' in result and 'ec' in result['dictResult'] and 'word' in result['dictResult']['ec']:
translations = result['dictResult']['ec']['word']['trs']
translations_text = [tran['tran'] for tran in translations]
return translations_text
if 'translateResult' in result:
return [item[0]['tgt'] for item in result['translateResult']]
return ["未找到翻译"]
def main():
while True:
input_text = input("请输入要翻译的单词/句子(输入'q'退出):")
if input_text.lower() == 'q':
break
try:
translations = get_youdao_translate(input_text)
print("\n翻译结果:")
for idx, trans in enumerate(translations, 1):
print(f"{idx}. {trans}")
print()
except Exception as e:
print(f"翻译出错:{e}")
if __name__ == "__main__":
main()
get_sign.js
const cryptoJs = require("crypto");
const d = 'fanyideskweb';
const u = 'webfanyi';
function y(e) {
return cryptoJs.createHash("md5").update(e.toString()).digest("hex");
}
function S(e, t) {
return y(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`);
}
function k(t, r) {
return S(t, r);
}
decry.js
const cryptoJs = require("crypto");
const Buffer = require('buffer').Buffer;
function T(e) {
return cryptoJs.createHash("md5").update(e).digest()
}
de = (e, t, a) => {
if (!e) return null;
const o = Buffer.alloc(16, T(t)), n = Buffer.alloc(16, T(a)), r = cryptoJs.createDecipheriv("aes-128-cbc", o, n);
let s = r.update(e, "base64", "utf-8");
s += r.final("utf-8");
return s;
}
没有回复内容