有道翻译js逆向

有道翻译

数据分析

生成sign

通过抓取数据,有道翻译是请求这个接口 https://dict.youdao.com/webtranslate,请求参数如下所示:

1694047786722

通过观察参数,mysticTime为时间搓,sign为加密算法生成的数据,其他参数不变,所以只需找到sign的生成函数就可以了。

通过全局搜索,在每个出现sign位置打上断点,找到生成sign的函数

1694049701269

sign是有w函数生成,w函数接收两个值,一个是当前的时间搓,另一个经过重复获取,发现是固定值。

w函数内部通过字符串格式化后把值传给A函数,观察A函数,只是把传过来的值进行md5加密。

使用python实现生成sign

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

设置cookie

生成sign后,可以构造请求发送请求获取数据。

可以观察,请求需要携带cookie,通过清除游览器的cookie刷新界面,再次发送请求,发现cookie是通过请求这个接口 https://dict.youdao.com/login/acc/query/accountinfo,在请求这个接口的请求结果头部设置了cookie

1694050707438

使用python实现设置cookie。

url = "https://dict.youdao.com/login/acc/query/accountinfo"
self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
    "Referer": "https://fanyi.youdao.com/index.html"
}
response = requests.get(url=url, headers=headers)
headers["Cookie"] = response.headers["Set-Cookie"]

解密请求结果

设置cookie,生成sign后请求数据,获取请求结果,发现请求结果是密文,需要通过解密获取具体的结果信息。通过添加断点以及堆栈信息,一步步查找。

1694051545902

可以观察到,密文o传入到tt["a"].decodeData函数后传回给a的是明文数据。点击tt["a"].decodeData跳转到decodeData的具体函数。

1694051868883

通过观察,on 参数为固定值,并都是作为参数传值给 j 函数,点击j函数跳转到具体位置,发现j函数是只是进行md5加密后的数据返回。然后把密文和数据传给createDecipheriv函数进行解密得到明文。

如果使用python对数据进行处理的会得到各种报错,因此使用execjs库直接把数据交个js处理。

const crypto=require('crypto')

function getDatas(t){
    const o = 'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl';
const n = 'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4';
    const a = Buffer.alloc(16, crypto.createHash('md5').update(o).digest())
    const r = Buffer.alloc(16, crypto.createHash('md5').update(n).digest())
    const i = crypto.createDecipheriv("aes-128-cbc", a, r);
    let s = i.update(t, "base64", "utf-8");
    return s += i.final("utf-8"), s
}

这里也提供使用python解码的方法,由js可以看出使用的编码是AES,t先经过base64解码后在进行AES解码。

import hashlib
import base64
from Crypto.Cipher import AES

def getDatas(t):
    o = 'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl'
    n = 'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4'
    a = hashlib.md5(o.encode()).digest()
    r = hashlib.md5(n.encode()).digest()
    t = t.replace("_", "/").replace("-", "+")
    missing_padding = 4 - len(t) % 4
    if missing_padding:
        t += '=' * missing_padding
    cipher = AES.new(a, AES.MODE_CBC, r)
    decrypted = cipher.decrypt(base64.b64decode(t)).decode('utf-8')
    index = decrypted.rfind("}")
    decrypted = decrypted[:index+1]
    return decrypted

代码封装

根据上述,我们通过对js逆向对有道翻译进行处理得到明文数据,对上述步骤进行封装整理成类。

import time
import json
import hashlib
import requests

import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
import execjs

class YouDaoTranslate():
    def __init__(self):
        path = os.path.dirname(os.path.abspath(__file__))
        self.js_path = os.path.join(path,"有道.js")
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
            "Referer": "https://fanyi.youdao.com/index.html"
        }
        self.__set_cookie()
        self.__langType()
        self.langMap = {
            'zh-CHS':"中文",
            "en":"英语",
            "ja":"日语",
            "ko":"韩语",
            "fr":"法语",
            "de":"德语",
            "ru":"俄语",
            "es":"西班牙语",
            "pt":"葡萄牙语",
            "vi":"越南语",
            "id":"印度尼西亚语",
            "it":"意大利语",
            "nl":"荷兰语",
            "th":"泰语",
            "ar":'阿拉伯语'
        }

    def translate(self,query,from_="auto",to=""):
        url = "https://dict.youdao.com/webtranslate"
        localtime = str(int(time.time() * 1000))
        data = "client=fanyideskweb&mysticTime={}&product=webfanyi&key=fsdsogkndfokasodnaso".format(localtime)
        sign = hashlib.md5(data.encode(encoding='utf-8')).hexdigest()
        data = {
            "i": query,
            "from": from_,
            "to": to,
            "dictResult": "true",
            "keyid": "webfanyi",
            "sign": sign,
            "client": "fanyideskweb",
            "product": "webfanyi",
            "appVersion": "1.0.0",
            "vendor": "web",
            "pointParam": "client,mysticTime,product",
            "mysticTime": localtime,
            "keyfrom": "fanyi.web"
        }
        response = requests.post(url=url, headers=self.headers, cookies=self.cookies, data=data).text

        # 使用execjs解码
        # with open(self.js_path, 'r', encoding='utf-8') as po:
        #     signs = execjs.compile(po.read()).call('datas', response)
        #     text = json.loads(signs)

        # 使用python解码
        signs = self.__decode(response)
        text = json.loads(signs)

        result_list = []
        if text.get("translateResult"):
            for i in text['translateResult']:
                result_list.append(i[0]["tgt"].strip())
        else:
            result_list.append("对不起,无法翻译该语种")

        res_dict = {
            "result":"\n".join(result_list),
            "CEDict":[]
        }
        if text.get('dictResult') and text['dictResult'].get("ce"):
            for tr in text['dictResult']['ce']['word']['trs']:
                if not tr.get("#tran"):
                    continue
                res_dict["CEDict"].append({"text":tr['#text'],"tran":tr['#tran']})
        return res_dict


    def __langType(self):
        """语言检测"""
        url = "https://api-overmind.youdao.com/openapi/get/luna/dict/luna-front/prod/langType"
        response = requests.get(url=url, headers=self.headers)
        text = response.json()
        self.langDict = {}
        common = text["data"]["value"]["textTranslate"]["common"]
        specify = text["data"]["value"]["textTranslate"]["specify"]
        for data in common + specify:
            self.langDict[data["code"]] = data["label"]


    def __decode(self,t):
        o = 'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl'
        n = 'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4'
        a = hashlib.md5(o.encode()).digest()
        r = hashlib.md5(n.encode()).digest()
        t = t.replace("_", "/").replace("-", "+")
        missing_padding = 4 - len(t) % 4
        if missing_padding:
            t += '=' * missing_padding
        cipher = AES.new(a, AES.MODE_CBC, r)
        decrypted = cipher.decrypt(base64.b64decode(t)).decode('utf-8')
        index = decrypted.rfind("}")
        decrypted = decrypted[:index+1]
        return decrypted

    def __set_cookie(self):
        url = "https://dict.youdao.com/login/acc/query/accountinfo"
        response = requests.get(url=url, headers=self.headers)
        self.headers["Cookie"] = response.headers["Set-Cookie"]


if __name__ == '__main__':
    obj = YouDaoTranslate()
    res = obj.translate('你好,世界')
    print(res)

该类下还增加了获取语言类型方法,通过translate方法的from_to参数控制翻译的语言。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值