当前位置: 首页 > news >正文

有道翻译逆向

今天突然使用了有道翻译,发现有道在线版本更新了,添加了很多功能,看了看规则,有些改变。所以我又更新了响应的文档,仅供学习交流,如有不妥之处,联系我删之。

目录

一、整体目标

二、流程步骤

1. 分析请求

2. 明确逆向目标

三、定位与调试手段

四、第一层:逆向sign签名

流程描述:

五、第二层:返回值的AES解密

流程描述:

六、整合与转换

七、总结


一、整体目标

  • 目标对象:有道翻译(fanyi.youdao.com)

  • 逆向目的:获取接口加密算法的核心逻辑,即

    1. 获取用于加密请求参数的 sign 值;
    2. 利用返回结果中的 aesKeyaesIv 对返回的加密数据进行解密。
  • 工具链

    • Chrome浏览器(F12调试工具)
    • PyCharm
    • Node.js(用于运行和验证JS代码)
    • ChatGPT(用于补全缺失的第三方库及转换代码到Python)

二、流程步骤

1. 分析请求

  • 观察网络请求
    使用Chrome的F12开发者工具,监控有道翻译发起的XHR请求,发现主要有三个请求:
    1. 第一个请求(拿到密钥:https://dict.youdao.com/webtranslate/key
      • 入参中包含固定参数和动态变化的参数,如 signmysticTime(时间戳)。
      • 接口返回数据包含 secretKeyaesKeyaesIv,这些参数用于后续解密。
    2. 第二个请求(拿到翻译单词的加密数据: https://dict.youdao.com/webtranslate):
      • 此接口是真正用于翻译的核心接口,入参同样包含动态变化的 sign 与时间戳。
      • 返回数据是一段加密后的字符串,必须用前一个请求返回的 aesKeyaesIv 解密才能获得翻译结果。

    3. 第三(翻译加密数据):

2. 明确逆向目标

  • 第一层:破解 sign 生成算法
    • 目的:理解并提取JS代码中负责生成请求签名 sign 的函数。
  • 第二层:解析返回数据
    • 目的:找到JS中用于解密返回数据的核心函数,并提取其加密算法(AES解密)的关键步骤。

三、定位与调试手段

文章总结了几种常用的定位和调试手段,帮助定位核心代码的位置和调用链:

  1. 事件断点:监控页面中的点击事件,确定用户触发翻译时发送的请求。
  2. 全局搜索:利用Chrome开发者工具的全局搜索功能,搜索关键词 sign,快速定位相关代码。
  3. XHR断点:在网络面板中对XHR请求设定断点,捕获请求发出时的调用栈信息。
  4. 栈分析:利用调试工具查看调用栈,追踪加密/解密函数的调用链,确定核心函数所在位置。

四、第一层:逆向sign签名

流程描述:

  1. 利用XHR断点捕获请求
    设定断点,触发翻译请求,观察JS中与 sign 相关的调用链。

  2. 全局搜索 sign
    直接搜索 sign: 关键字,快速定位到包含签名计算逻辑的函数。

  3. 分析调用栈

    • 通过栈断点查看调用链,找到类似函数名 S 或其他混淆名称函数,其中包含了对 md5 加密和时间戳参数的处理。
  4. 识别第三方加密库调用

    • 注意到部分代码调用了常见的加密函数(例如 md5base64AES),这些通常是调用外部库的函数。
    • 遇到第三方库时,通过经验判断其功能,避免深入无用代码。
  5. 提取并整合代码

    • 将核心的签名生成代码(如 function S(e, t) { return md5("client=" + 固定值 + "&mysticTime=" + e + "&product=" + 固定值 + "&key=" + t) })扣出来。
    • 补全缺失的变量和常量,必要时利用ChatGPT帮助添加第三方库引用(例如Node.js的 crypto 模块)。
  6. 调试验证

    • 运行提取后的代码(例如在Node.js中测试 console.log(S(...))),确认签名计算正确。

五、第二层:返回值的AES解密

流程描述:

  1. 定位解密函数
    • 利用XHR断点或栈断点,观察返回的加密数据和解密函数的调用,找到类似 decodeData 的函数(在混淆代码中可能名字也被混淆)。
  2. 避免直接单步调试进入混淆代码
    • 由于混淆严重,直接单步调试可能会进入大量无用或复杂的代码,建议通过查看对象属性和弹窗提示,定位到真正负责解密的对象或函数。
  3. 定位关键对象
    • 选中某个对象(如 _a)后,弹出其内容,找出其中负责AES解密的函数 decodeData 或相关函数。
  4. 提取AES解密核心代码
    • 扣出核心代码部分,主要涉及以下步骤:
      • 使用 Buffer.alloc(16, T(key)) 创建密钥和偏移量,函数 T 通常是对输入字符串进行 md5 处理。
      • 调用 crypto.createDecipheriv("aes-128-cbc", keyBuffer, ivBuffer) 初始化解密器。
      • 调用 decipher.update(encryptedData, "base64", "utf-8") 解密数据,并接续 decipher.final("utf-8") 完成解密。
  5. 补全第三方库依赖
    • 对于诸如 cryptomd5 等函数,使用 Node.js 的 crypto 模块或相应的Python第三方库来替换。
  6. 调试验证
    • 扣出完整的解密代码,并在提取的独立文件中测试,确保能够正确解密返回的加密数据字符串。

六、整合与转换

  1. 变量补全与清理
    • 补全扣出的代码中缺失的变量定义,去掉冗余代码,确保独立代码可以运行。
  2. 替换第三方库引用
    • 例如,将JS中的 crypto 模块调用替换成 Node.js 的方式,或者利用ChatGPT将JS代码转为Python代码,调用Python的 hashlibpycryptodome 等库来完成同样的功能。
  3. 统一测试
    • 将第一层生成签名和第二层解密的代码整合起来,在模拟真实请求时能正确获取到解密后的数据。
  4. 转换为Python(可选):
    • 使用ChatGPT快速将整合后的JS代码转为Python代码,达到同样的加解密效果,便于后续调用API。

       python实现流程:

       拿到KEY

tm = str(int(time.time() * 1000))

def md5_hash(text: str) -> str:
    """对输入字符串计算 MD5 并返回32位小写十六进制字符串"""
    return hashlib.md5(text.encode('utf-8')).hexdigest()

def _my_md5():
    fixed_client = "fanyideskweb"
    fixed_product = "webfanyi"
    mystic_time = tm
    key_value = "这个值需要你去跟栈拿到"
    string_to_sign = "client=" + fixed_client + "&mysticTime=" + mystic_time + "&product=" + fixed_product + "&key=" + key_value
    return md5_hash(string_to_sign)

def get_key():
    '''获取密钥'''
    params = { 
        "sign": _my_md5(),",
        "mysticTime":tm,
        # 其他参数在请求中复制
    }
    headers = {
        # 请求头中复制
    }
    url = "https://dict.youdao.com/webtranslate/key"
    response = requests.get(url=url, headers=headers, params=params)
    key_json = json.loads(response.text)
    aes_iv = key_json['data']['aesIv']
    aes_key = key_json['data']['aesKey']
    return aes_key,aes_iv

       拿到加密后的单词

def my_md5():
    fixed_client = "fanyideskweb"
    fixed_product = "webfanyi"
    mystic_time = str(int(time.time() * 1000))
    # key_value = "fsdsogkndfokasodnaso"
    key_value = "需要你去跟栈拿到参数"
    string_to_sign = "client=" + fixed_client + "&mysticTime=" + mystic_time + "&product=" + fixed_product + "&key=" + key_value
    return md5_hash(string_to_sign)

def get_jm_word(word_list):
    '''获取单词加密后的密文'''
    for item in word_list:
        data = {
            "i": item,
            "sign": my_md5(),
            "mysticTime":str(int(time.time() * 1000)),

        }
        headers = {
            # 请求头复制
        }
        url = "https://dict.youdao.com/webtranslate"
        response = requests.post(url=url, headers=headers, data=data)
        print(response)
        print(response.text)
        return response.text

      解密单词

def T(e: str) -> bytes:
    """
    对输入字符串 e 进行 MD5 运算,返回 16 字节的二进制摘要
    """
    return hashlib.md5(e.encode('utf-8')).digest()

def get_words(encrypted_data: str, t: str, o: str) -> str:
    '''解密单词'''
    if not encrypted_data:
        return None

    # 清理字符串,去除所有空白字符
    encrypted_data_clean = re.sub(r'\s+', '', encrypted_data)
    # 自动补齐 base64 填充(确保长度为4的倍数)
    missing_padding = len(encrypted_data_clean) % 4
    if missing_padding:
        encrypted_data_clean += '=' * (4 - missing_padding)
    key = T(t)  # 16字节密钥
    iv = T(o)   # 16字节IV
    # 使用 URL-safe base64 解码
    try:
        encrypted_bytes = base64.urlsafe_b64decode(encrypted_data_clean)
    except Exception as decode_error:
        raise ValueError("Base64 解码失败: " + str(decode_error))
    # 检查解码后的数据长度必须为 16 字节的倍数
    if len(encrypted_bytes) % 16 != 0:
        raise ValueError("解码后的数据长度不是16字节的倍数!当前长度:{}".format(len(encrypted_bytes)))
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted = cipher.decrypt(encrypted_bytes)
    pad_len = decrypted[-1]
    # 移除 PKCS#7 填充:最后一个字节表示填充字节数
    print(decrypted[:-pad_len].decode('utf-8'))
    word_c = json.loads(decrypted[:-pad_len].decode('utf-8'))
    word_t = word_c['translateResult'][0][0]['tgt']
    return word_t

# 参数解释
# encrypted_data : 单词加密的密文
# t: key
# o: iv

 最终效果

 

七、总结

整个逆向流程主要分为:

  • 步骤1:观察和捕获网络请求
    了解有道翻译发起的各个请求,确认需要逆向的接口参数和返回加密数据。
  • 步骤2:定位签名生成(sign)函数
    利用XHR断点、全局搜索、栈断点等手段,定位生成 sign 的核心代码,并扣出关键部分,补全缺失内容。
  • 步骤3:定位AES解密函数
    通过调试定位返回数据的解密逻辑,扣出核心代码,整理出利用AES-128-CBC方式解密返回数据的流程。
  • 步骤4:整合与优化代码
    将扣出的代码进行变量补全、删除冗余,替换第三方库引用,确保代码能够独立运行。
  • 步骤5:转换和验证
    利用Node.js或Python对整合后的代码进行测试验证,确保生成的 sign 值和解密结果正确无误。


相关文章:

  • 机器学习实战(9):神经网络基础——从感知机到多层感知机
  • GCC之编译(8)AR打包命令
  • kafka-集群扩容
  • docker 改了镜像源为阿里云,还是下载失败
  • RocketMQ保证消息有序性
  • 标量化rknn的输入输出向量转换处理
  • (deepseek)按键滤波硬件
  • Ubuntu22.04.6如何固定ip地址
  • 【UCB CS 61B SP24】Lecture 5 - Lists 3: DLLists and Arrays学习笔记
  • Spring AI + Ollama 实现调用DeepSeek-R1模型API
  • 修改阿里云服务器内网ip
  • 力扣-回溯-40 组合总和Ⅱ
  • 进制转换及C语言中进制转换方法
  • Git中revert和reset区别?
  • 如何将MySQL数据库迁移至阿里云
  • Pipeline 获取 Jenkins参数
  • 二叉树(数据结构)
  • 第5章:在LangChain中如何使用AI Services
  • 45.日常算法
  • 【算法通关村 Day6】二叉树层次遍历
  • 广州建设网站是什么/厦门网站快速排名优化
  • 深圳做响应式网站制作/深圳百度首页优化
  • html做网站头部/今天合肥刚刚发生的重大新闻
  • 网站 not found/最新nba排名
  • java免费入门网站/企业推广网
  • baidu网站建设/如何拥有自己的网站