文章目录

  • 👉1、目标网址
  • 👉2、寻找翻译结果接口并分析
  • 👉3、Python 实现有道翻译接口调用
  • 👉4、最理想的接口分析
  • 👉5、最终实现的密文解密



学习记录:2023–有道翻译接口

sign 等参数的加密

返回的密文数据解密实现

👉1、目标网址

有道翻译的官网:有道翻译

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_python

👉2、寻找翻译结果接口并分析

输入中文:快乐 要求翻译成:英文 全局搜索:快乐 或 happy,结果没有发现什么

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_python_02


切换 Fetch/XHR 进行搜索筛选后,找到了下面的一个接口,点击进行查看:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_MD5_03


初看:post请求,携带的参数有我们输入的中文:快乐,再看返回数据:多试试结果翻译结果发现它是变化的,对于这样我们只有去多找找了。

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_04


但我在调试时 ==> 对有道网页一顿乱点后找到了新接口:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_MD5_05


在这个接口中,它有我们想要的返回数据,且是能直接看懂的数据!!!那就抛弃之前的接口,就对这个接口分析:

参数:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_06


初看 sign 发现它是动态变化的,ts 与 salt 可能是时间戳,bv 不知道是啥,但请求几次后发现它不变。

好,全局搜素 sign 进行查看:在如下 js 找到:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_07


进入 搜索 sign :发现

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_python_08


找到这里就断点调试看看:我所找到的:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_09


所以:

  • ts:1680404913064, ==> 13位 当前时间戳
  • salt:16804049130648, ==> 13位 当前时间戳 + [0,9] 的一个随机整数
  • sign:e9c74c82dbb3ca3f1d25f0a743647a8d, ==> md5 La(Na + e + n + Va)
    Na => “fanyideskweb” e => 用户输入的字符 n => salt Va => “Ygy_4c=r#e#4EX^NUGUc5”

对应 Python 代码:

ts = str(int(time.time() * 1000))
salt = ts + str(random.randrange(0, 10))
# md5 -> 编码 -> 转16进制 数据字符串
sign = hashlib.md5(("fanyideskweb" + translation_words + salt + "Ygy_4c=r#e#4EX^NUGUc5").encode('utf-8')).hexdigest()

好! 关键参数解决了,下面进行Python代码实现

👉3、Python 实现有道翻译接口调用

""""
CSDN:抄代码抄错的小牛马
"""
import hashlib
import time
import random
import requests

url = 'https://fanyi.youdao.com/bbk/translate_m.do'
headers = {
    'Accept': 'application/json, text/plain, */*',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Host': 'fanyi.youdao.com',
    'Origin': 'https://fanyi.youdao.com',
    'Referer': 'https://fanyi.youdao.com/index.html',
    'Cookie': '复制你那里的cookie',
    'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="8"',
    'sec-ch-ua-mobile': '?0',
    'Sec-Fetch-Dest': 'empty',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Site': 'same-origin',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.0.2242 SLBChan/25',
}
translation_words = '周末愉快'

# 'ts':   '1680404913064',  ==> 13位 当前时间戳
# 'salt': '16804049130648', ==> 13位 当前时间戳 + [0,9] 的一个随机整数
# 'sign': 'e9c74c82dbb3ca3f1d25f0a743647a8d', ==> md5  La(Na + e + n + Va);

#  Na -> "fanyideskweb"
#  e -> translation_words
#  n -> salt
#  Va -> "Ygy_4c=r#e#4EX^NUGUc5"

ts = str(int(time.time() * 1000))
salt = ts + str(random.randrange(0, 10))
# md5 -> 编码 -> 转16进制 数据字符串
sign = hashlib.md5(("fanyideskweb" + translation_words + salt + "Ygy_4c=r#e#4EX^NUGUc5").encode('utf-8')).hexdigest()

print(sign)

form_data = {
    'i': translation_words,

    'from': 'zh-CHS',
    'to': 'en',
    'client': 'fanyideskweb',
    'bv': '655f7a3056df1c2a1fccf338154a4eaf',
    'doctype': 'json',
    'version': '3.0',
    'cache': 'true',
    'ts': ts,
    'salt': salt,
    'sign': sign,

}

res = requests.post(url=url, headers=headers, data=form_data).json()

resp = [i['tgt'] for i in res['translateResult']]
print('-------------------------有道翻译-------------------------')
print('翻译前:', translation_words)
print('翻译后:', resp)

运行:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_MD5_10


tgt 参数: 可省略

from: en == >翻译前的语言

to: zh-CHS == >翻译成什么语言这里就不太行,设置 auto 报错,虽然可以把所有的语言参数弄进去,让用户选择

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_11

👉4、最理想的接口分析

但这样有个缺点:上面那个接口不能自动识别翻译前的语言。又去找了一会儿,发现最符合我们理想的接口,就是最初找的那个接口,虽然上面我们解决的参数,但是这个返回的数据是加密的。只有去找js看了。

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_12


捣鼓一会儿:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_python_13


在控制台它打印出来了数据:多试几次后发现它就是接口返回的加密数据解密后的状态。因为它翻译后返回的数据是加密过后的,那他是怎么加密返回给我们的呢?它又是在哪里解密返回的呢?

测试发现可以搜素解密后的接口数据translateResult找到加密解密JS:命中目标 !!!

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_14

断点调试

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_python_15

在1690 这一行,我们仔细看一下,发现它是一个解密过程,传进已加密的数据 + key + iv ,经验足够的兄弟可以看出可能是一个aes加密解密。到此,我们可以点进 decodeData() 中去看看

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_JS爬虫_16


进入后如下:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_python_17


网页js:

T = (t,o,n)=>{
                if (!t)
                    return null;
                const a = e.alloc(16, f(o))
                  , i = e.alloc(16, f(n))
                  , r = c.a.createDecipheriv("aes-128-cbc", a, i);
                let s = r.update(t, "base64", "utf-8");
                return s += r.final("utf-8"),
                s
             }

断点一步一步:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_JS爬虫_18


试了几次发现:key 和 iv 没有变

# o --> key = 'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl'
# n --> iv  = 'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4'

再跟着调试下去:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_JS爬虫_19


如下:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_JS逆向_20


好! 找到规律了,因为发现 key 和 iv 是没变的,所以先将他们 MD5处理了:

""""
CSDN:抄代码抄错的小牛马
"""
import hashlib

# o --> key = 'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl'
# n --> iv  = 'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4'


key_md5 = hashlib.md5(('ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl').encode('utf-8')).digest()
iv_md5 = hashlib.md5(('ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4').encode('utf-8')).digest()

print(len(key_md5))   
print(key_md5)

print(len(iv_md5))
print(iv_md5)

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_MD5_21


aes解密:

JS:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_22


PY:

from Crypto.Cipher import AES
import hashlib
import base64
from Crypto.Util.Padding import unpad
import time
import requests
import json


def decrypt(decrypt_str):
    key = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
    iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"

    key_md5 = hashlib.md5((key).encode('utf-8')).digest()
    iv_md5 = hashlib.md5((iv).encode('utf-8')).digest()
    print('key_md5:', key_md5)
    print('iv_md5:', iv_md5)
    print()
    aes = AES.new(key=key_md5, mode=AES.MODE_CBC, iv=iv_md5)

    code = aes.decrypt(base64.urlsafe_b64decode(decrypt_str))
    return unpad(code, AES.block_size).decode('utf8')

好!完工~~

👉5、最终实现的密文解密

直接贴代码:

from Crypto.Cipher import AES
import hashlib
import base64
from Crypto.Util.Padding import unpad
import time
import requests
import json


def decrypt(decrypt_str):
    key = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
    iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"

    key_md5 = hashlib.md5((key).encode('utf-8')).digest()
    iv_md5 = hashlib.md5((iv).encode('utf-8')).digest()
    print('key_md5:', key_md5)
    print('iv_md5:', iv_md5)
    print()
    aes = AES.new(key=key_md5, mode=AES.MODE_CBC, iv=iv_md5)

    code = aes.decrypt(base64.urlsafe_b64decode(decrypt_str))
    return unpad(code, AES.block_size).decode('utf8')


def get_data(translation_words):
    url = 'https://dict.youdao.com/webtranslate'
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Cookie': 'OUTFOX_SEARCH_USER_ID_NCOO=379056539.64209586; OUTFOX_SEARCH_USER_ID=-380628258@222.182.116.19',
        'Host': 'dict.youdao.com',
        'Origin': 'https://fanyi.youdao.com',
        'Referer': 'https://fanyi.youdao.com/',
        'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
        '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/111.0.0.0 Safari/537.36',
    }

    # 'ts':   '1680404913064',  ==> 13位 当前时间戳
    # fsdsogkndfokasodnaso 固定值

    ts = str(int(time.time() * 1000))
    str_sign = f"client=fanyideskweb&mysticTime={ts}&product=webfanyi&key=fsdsogkndfokasodnaso"
    sign = hashlib.md5((str_sign).encode('utf-8')).hexdigest()

    print('------------------------sign------------------------')
    print('sign为:', sign, end='\n\n')
    form_data = {
        'i': translation_words,
        'from': 'auto',
        'to': '',
        'sign': sign,
        'dictResult': 'true',
        'keyid': 'webfanyi',
        'client': 'fanyideskweb',
        'product': 'webfanyi',
        'appVersion': '1.0.0',
        'vendor': 'web',
        'pointParam': 'client,mysticTime,product',
        'mysticTime': ts,
        'keyfrom': 'fanyi.web',
    }

    res = requests.post(url=url, headers=headers, data=form_data).text

    print('------------------------返回的数据密文------------------------')
    print(res, end='\n\n')
    return res


if __name__ == "__main__":
    translation_words = '周末愉快'
    # translation_words = input("请输入要翻译的:")
    decrypt_str = get_data(translation_words)
    end_code = decrypt(decrypt_str)
    print('------------------------解密后的数据密文------------------------')
    print(end_code, end='\n\n')
    json_data = json.loads(end_code)

    print('-------------------------有道翻译-------------------------')
    print('翻译前:', translation_words)
    print('翻译后:', json_data['translateResult'])

运行结果:

java 有道翻译接口 一次翻译多个单词 python有道翻译接口_AES解密_23


拜~~~

我们下次再见 ^ _ ^