2024新版有道web接口逆向

本文最后更新于:2024年8月24日 下午

开始之前

在本期教程开始之前,虽然网络上有很多有道翻译的解密,但是在一年前,有道换了新的ui,加入了ai翻译

那关于新版本如何解密呢,本期教程就给大家揭晓


抓包&数据分析

首先我们打开有道翻译,点我同时右键f12打开浏览器的抓包,过滤一下,可以看到有这两个请求

从数据的返回上看,这两个api是连在一起的,一个是生成解密密钥,一个是用密钥解密密文

所以我们先把目光放到生成密钥这个api


解密生成key的sign

从上面请求的数据来看,经过多次访问,只有mysticTimesign发生了变化,第二次参数不难搞,就是当前的时间戳

那么我们看一下js看看他把sign写在那里了

全局搜索sign这个参数

从这个getsign函数来看,我们可以找到该函数的位置,同时查看t函数,t函数是加密的内容

从报错信息得知,加密藏于app.js,从这个文件一路打断点,找到sign函数的入口了,同时o函数是请求的时间戳,e函数是固定值内容为asdjnjfenknafdfsdfsd

function s直接断点得到如下信息

d函数是fanyideskwebe函数是时间戳u函数是webfanyit函数是asdjnjfenknafdfsdfsd,同时调用的_函数也在上面了,是个md5加密,综上所述,加密公式为

这里的时间戳为13位的时间戳

1
client=fanyideskweb&mysticTime=0000000000000&product=webfanyi&key=asdjnjfenknafdfsdfsd

和官方加密的一致


成品1

Typescript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import CryptoJS from 'crypto-js';


export async function get_sign(){
var headers : any = {
"Referer": "https://fanyi.youdao.com/",
"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
}
var mysticTime : number = Math.floor(Date.now())
var sign: string = CryptoJS.MD5(`client=fanyideskweb&mysticTime=${mysticTime}&product=webfanyi&key=asdjnjfenknafdfsdfsd`).toString()
var url : string = "https://dict.youdao.com/webtranslate/key?" +
"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"

var requset = await fetch(url, headers)
var json_data = await requset.json()
console.log(json_data["data"])
}

get_sign()

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import hashlib
import httpx
import time


def get_sign():
mysticTime = int(time.time() * 1000)
sign = hashlib.md5(f"client=fanyideskweb&mysticTime={mysticTime}&product=webfanyi&key=asdjnjfenknafdfsdfsd".encode(encoding='UTF-8')).hexdigest()
a = httpx.get(
url="https://dict.youdao.com/webtranslate/key?keyid=webfanyi-key-getter"\
f"&sign={sign}"\
"&client=fanyideskweb"\
"&product=webfanyi"\
"&appVersion=1.0.0"\
"&vendor=web"\
"&pointParam=client,mysticTime,product"\
f"&mysticTime={mysticTime}"\
"&keyfrom=fanyi.web"\
"&mid=1"\
"&screen=1"\
"&model=1"\
"&network=wifi"\
"&abtest=0"\
"&yduuid=abcdefg",
headers= {
"Referer": "https://fanyi.youdao.com/",
"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
}
)
return a.json()["data"]

print(get_sign())

解密密文

那我们搞定了key这个api后,现在开始分析webtranslate这个api,首先我们先看发送的内容

这里的i函数就是我们翻译的内容,sign参数又来了,那我们在原来的地方打个断点,拿签名

然后你会发现只是t函数变了,其他没变,套用第一次分析的公式

1
client=fanyideskweb&mysticTime=0000000000000&product=webfanyi&key=fsdsogkndfokasodnaso

这样第一层就搞定了,我们现在要找密文解密的地方,全网就复杂在这里

按照原理来说,js会请求api,再解密,所以我们要找到请求的地方

从这里开始发起请求,回到app.js,搜索一下发送请求的url,然后我们就找到了这些

这里是个aes的加密,l也是解密的密文,我们就要开始分析了

首先e是密文,tkey_apiaeskeyokey_apiaesiv忘记说了,t函数是md5加密

1
2
3
4
5
const a = s.alloc(16, T(t))
, n = s.alloc(16, T(o))
, r = i.createDecipheriv("aes-128-cbc", a, n);
let l = r.update(e, "base64", "utf-8");
return l += r.final("utf-8"),

我们把这一段代码拷贝到ide,根据自己学的ts,改了一下,这里网易使用了aes-128-cbc加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { createHash,createDecipheriv } from 'crypto';


var t = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
var o = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
var e = "Z21kD9ZK1ke6ugku2ccWu4n6eLnvoDT0YgGi0y3g-v0B9sYqg8L9D6UERNozYOHq0tIY5zg2_G3Njo5Qsw98fEghPM5cT9RqLsGBQW5jSVzv635HhMVrRZ6XpfYk0jtdgCf21MHrt_Gv94m_MLKUmBr8wSktKe6dvgvAHa5JFYxZjBINiTLwCUM5uFfL230YCw4OOj_f5cgK75GSOgS76xYGS0AcczPyGKkqErEunfjU5J0ztwhHESgwX6aE1qleQphl7R8HfR51CDtqzeL9XhzMjPgetz6gBaXdjYYC00T_ELg1AFx4dqu1N9BZ2wxHxKZ4StKqRDTZcVX5XU13ffSHHnhGva7SKIUpOZgtbz8950OQ92Gm9VkHLeKyWXCtNhms9O8gbr88hXT1oHrLqSduzlKgqHq_LnjsvTOraZRSVuiugeaWVgh2iF-CPnFPsgp2CjeQ9gEgBXwbkn1iDg0uFGRmT_h6mO3mzoaamxNMKWjNlHqXeRNQtsiKwpUCN-VE0IFe5zSKeYN_6y5MVH5zehnMjHbBd-K0f9ESoM-9a5-PDPMUqXxS5dB5gCAh"
function T(o : string){
return createHash('md5').update(o).digest();
}
console.log(T(o).toJSON().data)
const a = Buffer.from(T(t).toJSON().data)
const n = Buffer.from(T(o).toJSON().data)
const r = createDecipheriv('aes-128-cbc', a, n);
let l = r.update(e, 'base64', 'utf-8');
l += r.final('utf-8');
console.log(a,n)
console.log(l)

这样就输出正确的结果了,所以我们可以得知代码正确的内容了

同理,我们把上面获取密钥结合在一起,就行了


成品2

Typescript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import { createHash,createDecipheriv } from 'crypto';
import { access } from 'fs';

export async function get_sign(){
var headers : any = {
"Referer": "https://fanyi.youdao.com/",
"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
}
var mysticTime : number = Math.floor(Date.now())
var sign: string =createHash('md5').update(`client=fanyideskweb&mysticTime=${mysticTime}&product=webfanyi&key=asdjnjfenknafdfsdfsd`).digest("hex")
var url : string = "https://dict.youdao.com/webtranslate/key?" +
"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"

var requset = await fetch(url, headers)
var json_data = await requset.json()
return json_data["data"]
}

export function T(o : string){
return createHash('md5').update(o).digest();
}

export async function get_translation_result(target: string) {
const v : any = await get_sign()
var aeskey : string = v["aesKey"]
var aesiv : string = v["aesIv"]
var url : string = "https://dict.youdao.com/webtranslate"
var mysticTime : number = Math.floor(Date.now())
var sign: string =createHash('md5').update(`client=fanyideskweb&mysticTime=${mysticTime}&product=webfanyi&key=fsdsogkndfokasodnaso`).digest("hex")
var m : string = `i=${target}&from=auto&to=&useTerm=false&dictResult=true&keyid=webfanyi&sign=${sign}&client=fanyideskweb&product=webfanyi&appVersion=1.0.0&vendor=web&pointParam=client%2CmysticTime%2Cproduct&mysticTime=${mysticTime}&keyfrom=fanyi.web&mid=1&screen=1&model=1&network=wifi&abtest=0&yduuid=abcdefg`
var header : any = {
method : "POST",
headers : {
"Content-type": "application/x-www-form-urlencoded",
"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
"Referer": "https://fanyi.youdao.com/",
"Cookie": "OUTFOX_SEARCH_USER_ID=11560287@1.1.1.1; OUTFOX_SEARCH_USER_ID_NCOO=1; "
},
body:
m
}
const resp = await fetch(url, header)
const resp_text = await resp.text()
const a = Buffer.from(T(aeskey).toJSON().data)
const n = Buffer.from(T(aesiv).toJSON().data)
const r = createDecipheriv('aes-128-cbc', a, n);
let l = r.update(resp_text, 'base64', 'utf-8');
l += r.final('utf-8');
var t = JSON.parse(l)
var ans : string = ""
for (let index = 0; index < t["translateResult"].length; index++) {
ans+=t["translateResult"][index][0]["tgt"]
}
console.log(ans)
}

get_translation_result("你好")

2024新版有道web接口逆向
http://blog.bingyue.top/2024/08/24/youdao_fanyi/
作者
bingyue
发布于
2024年8月24日
许可协议