前言
本文主要介绍了有道翻译的接口调用情况,对两个接口进行了初步的接口逆向,只供学习交流使用。
找到翻译接口 webtranslate
网页地址:https://fanyi.youdao.com/index.html#/
首先找到翻译接口
可以看出,翻译接口是 webtranslate,再看返回内容
返回的是一大串字符串,所以后面还需要解密这串字符
解密webstranslate 中的 Form Data 参数
i: 你好,程序
from: auto
to:
domain: 0
dictResult: true
keyid: webfanyi
sign: c04636a841a4b2c374dc4d7dcf675514
client: fanyideskweb
product: webfanyi
appVersion: 1.0.0
vendor: web
pointParam: client,mysticTime,product
mysticTime: 1711293172275
keyfrom: fanyi.web
mid: 1
screen: 1
model: 1
network: wifi
abtest: 0
yduuid: abcdefg
可以看到sign参数加密,加密长度为32位
md5加密特征:
**长度固定。**无论输入的数据长度是多少字节,输出总32位字符。
**不可逆。**即无法通过MD5值反推出原始数据。
**抗修改性。**对原始数据进行的任何改动,甚至是一个字节的修改,都会导致MD5值显著不同。
**抗碰撞性。**要找到两个具有相同MD5值的不同数据非常困难。
由此我们可以判断,它属于md5加密
寻找加密 sign
全局搜索 sign 的值,发现只有这一行的 debug 进去了
点进去之后找到了sign 这个参数,sign 是由于 k(o,e) 这个方法生成的,再网上找方法,最后发现确实是 md5 加密而来的
我们可以试一下,e的结果是
client=fanyideskweb&mysticTime=1711293674225&product=webfanyi&key=fsdsogkndfokasodnaso
页面上 md5 加密后
使用网上的工具加密
发现正好是 32位小写的 md5 加密,结果和预想的正确
通过检查发现 这个key= fsdsogkndfokasodnaso
探索 key 接口
发现这个key 竟然来自于另一个接口
所以自动化翻译需要两个接口来实现,第一步先获取加密的key,之后再请求翻译接口,除了sign,其他的都是写死的
现在看看第一个返回 key的接口是否需要逆向,发现这个sign 也是需要加密的,但是key=asdjnjfenknafdfsdfsd
第一个请求能够复现了
import requests
import datetime
import json
import hashlib
from urllib.parse import urlencodedef get_timestamp():return int(datetime.datetime.now().timestamp() * 1000)def get_key():# 主要是获取key等参数url = "https://dict.youdao.com/webtranslate/key"mysticTime = get_timestamp()tmp_sign = f"client=fanyideskweb&mysticTime={mysticTime}&product=webfanyi&key=asdjnjfenknafdfsdfsd"sign = hashlib.md5(tmp_sign.encode("utf8")).hexdigest()# 参数params = {'keyid': 'webfanyi-key-getter','sign': sign,'client': 'fanyideskweb','product': 'webfanyi','appVersion': '1.0.0','vendor': 'web','pointParam': 'client,mysticTime,product','mysticTime': mysticTime,'keyfrom': 'fanyi.web','mid': 1,'screen': 1,'model': 1,'network': 'wifi','abtest': '0','yduuid': 'abcdefg',}url_with_params = f"{url}?{urlencode(params)}"headers = {'Accept': 'application/json, text/plain, */*','Accept-Language': 'zh-CN,zh;q=0.9','Cache-Control': 'no-cache','Connection': 'keep-alive','Origin': 'https://fanyi.youdao.com','Pragma': 'no-cache','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/122.0.0.0 Safari/537.36','Sec-Ch-Ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"','Sec-Ch-Ua-Mobile': '?0','Sec-Ch-Ua-Platform': '"Windows"'}# 请求response = requests.get(url_with_params, headers=headers, data={})json_data = json.loads(response.text)# 获取 keykey = json_data['data']['secretKey']return keydef send(chinese):url = "https://dict.youdao.com/webtranslate"mysticTime = get_timestamp()# sign 是 client=fanyideskweb&mysticTime=1711295634744&product=webfanyi&key=fsdsogkndfokasodnasotmp_sign = f"client=fanyideskweb&mysticTime={mysticTime}&product=webfanyi&key={get_key()}"sign = hashlib.md5(tmp_sign.encode("utf8")).hexdigest()payload = {'i': chinese,'from': 'auto','to': '','domain': '0','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','mid': 1,'screen': 1,'model': 1,'network': 'wifi','abtest': '0','yduuid': 'abcdefg',}headers = {'Accept': 'application/json, text/plain, */*','Accept-Language': 'zh-CN,zh;q=0.9','Cache-Control': 'no-cache','Connection': 'keep-alive','Content-Type': 'application/x-www-form-urlencoded','Cookie': 'OUTFOX_SEARCH_USER_ID=-1545431425@10.55.164.248; OUTFOX_SEARCH_USER_ID_NCOO=1617400304.3454392','Origin': 'https://fanyi.youdao.com','Pragma': 'no-cache','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/122.0.0.0 Safari/537.36','sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"'}response = requests.post(url, headers=headers, data=payload)encode_data = response.text# 开始解密数据print(encode_data)
解密返回的加密数据
但是通过测试发现,send() 这个方法返回的是加密数据,接下来探索如何解密,通过debug 发现解密在这行代码里面
观察这个方法,定义了一个变量a,通过调用一个解密方法,把乱码的字符给解密了
关键代码
const a = Po["a"].decodeData(o, Wo["a"].state.text.decodeKey, Wo["a"].state.text.decodeIv), n = a ? JSON.parse(a) : {};
看下 Po[“a”].decodeData 会跳转到哪个方法里面
进入这个解密方法,框选的部分好像就是解码过程了
Debug 看下是怎么解密的
使用模型分析下这个代码
原来 y() 这个函数是 md5 加密的方法
经过采纳考别人的经验,复现出了 python 代码
import requests
import datetime
import json
import hashlib
from urllib.parse import urlencode
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64def get_timestamp():return int(datetime.datetime.now().timestamp() * 1000)def init_data():# 主要是获取key等参数url = "https://dict.youdao.com/webtranslate/key"mysticTime = get_timestamp()tmp_sign = f"client=fanyideskweb&mysticTime={mysticTime}&product=webfanyi&key=asdjnjfenknafdfsdfsd"sign = hashlib.md5(tmp_sign.encode("utf8")).hexdigest()# 参数params = {'keyid': 'webfanyi-key-getter','sign': sign,'client': 'fanyideskweb','product': 'webfanyi','appVersion': '1.0.0','vendor': 'web','pointParam': 'client,mysticTime,product','mysticTime': mysticTime,'keyfrom': 'fanyi.web','mid': 1,'screen': 1,'model': 1,'network': 'wifi','abtest': '0','yduuid': 'abcdefg',}url_with_params = f"{url}?{urlencode(params)}"headers = {'Accept': 'application/json, text/plain, */*','Accept-Language': 'zh-CN,zh;q=0.9','Cache-Control': 'no-cache','Connection': 'keep-alive','Origin': 'https://fanyi.youdao.com','Pragma': 'no-cache','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/122.0.0.0 Safari/537.36','Sec-Ch-Ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"','Sec-Ch-Ua-Mobile': '?0','Sec-Ch-Ua-Platform': '"Windows"'}# 请求response = requests.get(url_with_params, headers=headers, data={})json_data = json.loads(response.text)# 获取 keyreturn json_data['data']def send(chinese):url = "https://dict.youdao.com/webtranslate"mysticTime = get_timestamp()data = init_data()aesIv = data['aesIv']aesKey = data['aesKey']secretKey = data['secretKey']# sign 是 client=fanyideskweb&mysticTime=1711295634744&product=webfanyi&key=fsdsogkndfokasodnasotmp_sign = f"client=fanyideskweb&mysticTime={mysticTime}&product=webfanyi&key={secretKey}"sign = hashlib.md5(tmp_sign.encode("utf8")).hexdigest()payload = {'i': chinese,'from': 'auto','to': '','domain': '0','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','mid': 1,'screen': 1,'model': 1,'network': 'wifi','abtest': '0','yduuid': 'abcdefg',}headers = {'Accept': 'application/json, text/plain, */*','Accept-Language': 'zh-CN,zh;q=0.9','Cache-Control': 'no-cache','Connection': 'keep-alive','Content-Type': 'application/x-www-form-urlencoded','Cookie': 'OUTFOX_SEARCH_USER_ID=-1545431425@10.55.164.248; OUTFOX_SEARCH_USER_ID_NCOO=1617400304.3454392','Origin': 'https://fanyi.youdao.com','Pragma': 'no-cache','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/122.0.0.0 Safari/537.36','sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"'}response = requests.post(url, headers=headers, data=payload)encode_data = response.text# 开始解密数据print("encode_data:", encode_data)print("aesIv:", aesIv)print("aesKey:", aesKey)decode(encode_data, aesIv, aesKey)# 执行解密过程
def decode(encode_data, aesIv, aesKey):# 将秘密字符串转换为字节,并使用base64解码key = hashlib.md5(aesKey.encode('utf-8')).digest()iv = hashlib.md5(aesIv.encode('utf-8')).digest()print(len(encode_data))# 解密数据# 创建AES解密器aes = AES.new(key, AES.MODE_CBC, iv)decrypted_padded = aes.decrypt(base64.urlsafe_b64decode(encode_data))decrypted = unpad(decrypted_padded, AES.block_size).decode('utf8')# 返回解密后的数据json_data = json.loads(decrypted)print(json_data)#passif __name__ == '__main__':chinese = "你好,大家好才是真的好"send(chinese)
这边花时间最多的就是寻找 webtranslate 返回内容的解密方法,需要熟练运用 chrome 提供的工具,查询每一个方法的作用,但是只要细心点一定会成功的。