【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考

文章日期:2024.12.24

使用工具:Python,Node.js

逆向类型:webpack类型

本章知识:sign模拟生成,密文的解密(webpack),全程扣代码,仅供学习参考

文章难度:低等(没难度)

文章全程已做去敏处理!!! 【需要做的可联系我】

AES解密处理(直接解密即可)(crypto-js.js 标准算法):​​​​​​在线AES加解密工具

仅供学习!!仅供测试!!仅供参考!!

声明:本文逆向的所有内容文件仅供学习,从网站扣的代码不会公开,但博主自己写的代码内容将会公开在文章最底部,望谅解!!!

先看成品图,效果就是这个样子,可以直接翻译,支持多种不同语言(没有试过高并发)

给大家提前讲一下,比较简单的内容我将会快速讲解,不会很细,小白可能会不懂,不懂只能去看我之前的逆向文章,或者留言都可以,有志向的人,一起学习发展。

1、打开某某网站(使用文章开头的AES在线工具解密):

OI1Y7IeHDHWVFvw4Yo05CnrGO+zxG4b5OFpdZvnuG2WEWj/TsHjdLb2bzhvR/PvI

2、【sign】我们打开页面后随便输入中文进行翻译,然后找到刻意的接口,我们打开看一下,没想到一上来就有个sign,而且返回的数据还是密文

3、【sign】别着急,我们把这个接口的请求先转移到python文件内,然后经过我不断的测试,发现他的cookie必须要加【OUTFOX_SEARCH_USER_ID】参数,而且这个参数是通过服务器返回的,而且有效期为20年,我决定直接生成,我从来没见过一个cookie有效期为20年的,纯扯淡,他一定不是在服务器存储着,所以我们直接随机生成即可

其次是载荷data里【from】【to】这两个参数我就不多介绍了,看注释即可。

剩下的就是sign和一个时间戳,直接全局搜索

4、【sign】全局搜索接口的链接路径,发现搜索到了两个,直接点进去都打上断点,然后再次触发翻译

5、【sign】断住了,看图说话,直接跟进去。

6、【sign】跟进来后发现了sign,而且加密方法就在头上。这就不用多说了,口算就能知道是md5加密。

7、【sign】直接用 python 模拟出来,标准的MD5,就不用讲太多了

8、【密钥】接下来看一下那个密钥,这个也不用多说,他是另一个请求返回的,而且这个请求里也有一个sign,用的也是md5加密,只是加密的内容有点不一样,直接看结果吧,没什么难度

9、【代码整理】我将代码进一步整合,方便下一步的调试

10、【解密密文】接下来重点来了,解密返回的密文,本次全程扣代码,文章结尾我将会附上模拟解密的代码,并非扣下来的代码,供大家参考学习

注意:文章结尾的代码并非官网扣下来的,放心测试学习!!!

11、遇到这种密文解密其实也没多难,我接下来用两种方法帮大家找到这个加密位置。

12、【找加密位置1】我们找到返回密文的接口,复制一下链接路径,然后ctrl+shift+f全局搜索,搜索到后,对第二个打上断点,第一个不用打断点,因为第一个很明显,他的链接路径和我要找到不一样,直接排除

13、【找加密位置1】我们打上断点后,随便输入内容使其触发翻译,然后会自动断住,这时候我们就一步一步跟进

14、【找加密位置1】大约跟进了5次左右,看到了一个加密方法,虽然目前没有看到密文在此处解密,但我的职业素养告诉我,"有疑问?直接打上断点好了",记住,做逆向,只要看到疑似加解密的位置,直接打断点就好,这是找解密位置或加密位置的一种方法。我们直接打上断点,然会放开运行

14、【找加密位置2】使用HOOK大法(配合油猴插件会更方便好用),将HOOK代码直接在控制台执行,然会再次输入内容触发翻译,你会发现断住了(HOOK代码在文章结尾)

HOOK原理:首先我们要知道他解密密文要做什么事情,像这种一般都是字典传输,那么他在解密内容后,一定会执行【JSON.parse】,将字符串的字典转为格式化后的字典,如果他不执行转换,那他就读取不了内容,所以我们用这个HOOK代码进行拦截,相当于这个HOOK代码就是中间商,你所做的事情要经过这个中间商后,这个中间商在觉定是拦截还是放行,这个中间商把别人替代了,所以你不得不经过他,这就是大概的HOOK的原理(有些场景的HOOK需要搭配油猴)

15、【找加密位置2】我们一边放行一遍观察他的内容参数,才走了几步就看到了解密后的密文数据,我们在堆栈里看他上一步的操作,看看都干了什么,点击上一个堆栈,嘿,成功找到加密位置。

16、【解密密文】接下来我们开始解密,我们将他的解密函数在控制台运行,他会自动暴露源函数,这时我们点进去

17、【解密密文】点击进来后,可以看到他解密的方法,我们直接转移到本地js文件内,然会尝试运行看缺少什么,发现缺少【l】函数,我们直接去源代码里找

18、【解密密文】找到了,直接看图,直接复制到文件内,然后再次运行,发现缺少a函数,我们直接去找

19、【解密密文】我们对当时找到的参数位置的a打上断点,然后刷新页面,会发现断住了,然后我们控制台输出一下,然后点击进去,会发现他是一个webpack加载器文件,直接把当前js全部复制到js内

20、【解密密文】复制到了本地js文件内,运行后发现缺少两个环境,我直接补上了,就不细讲了。然后我看出来这个加载器的主函数是a,所以直接把他的主运行函数导出全局,让外部可以访问,既然a已经被导出为jzq了,那么下面的两个函数也有修改一下,都改为jzq即可,然后再次运行看看缺少什么

21、【解密密文】运行发现报错了,找到报错的行,然后打印一下他运行什么导致的报错,打印结果发现他缺少方法,加载器没有找到这个方法,无法调用,那我们直接去找

22、【解密密文】我们回到浏览器控制台,运行一下图片里的函数,然后随便打开任意字段,点击链接进到加载器的函数里

23、【解密密文】进来后,我们直接搜索缺少的方法,找到后,直接放入到本地js里,可以让加载器读取,但要注意了,看下面第二张图,你的加载器需要修改一下代码,否则他不会读取你导入的方法

24、【解密密文】这次运行成功了,他已经能正常读取到91565了,但他缺少另一个方法,我们继续去搜索,一种重复这种方法。由于太多,我就不一个一个尝试了,我直接把全部都复制过来

25、【解密密文】我把所有的都复制过来,运行后发现没有问题了,成功解密了。一般情况不要无脑全部复制,这会导致卡顿,具体怎么复制还是根据大家的个人习惯

26、【全部代码整理】直接看成品

注意:没有用扣下来的代码做解密是因为文件量太大,而且扣代码是教程并不是一定要用扣下来的代码,要懂得变通

【AES-128-CBC.js】 js文件,用于解密,扣下来的代码不能用于公开,只能给大家展示模拟出来的源代码

javascript 复制代码
// 导入 crypto 模块
const crypto = require('crypto');
// 安装 express 服务包
// npm install express --save
const express = require('express');

const app = express();
app.use(express.json());


// AES-128-CBC 解密函数
function aesDecrypt(ciphertext, key, iv) {
  try {
    // 创建解密器
    const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
    // 执行解密操作
    let decrypted = decipher.update(ciphertext, "base64", "utf-8");
    decrypted += decipher.final('utf8');
    return decrypted;
  } catch (error) {
    console.error('解密失败:', error);
  }
}

function generateMD5Hash(input) {
  // 创建 MD5 哈希
  const hash = crypto.createHash('md5');
  // 更新哈希对象内容
  hash.update(input);
  // 输出结果为 Buffer 对象
  return hash.digest(); // 默认是二进制 Buffer 格式
}


app.post('/', (req, res) => {
    const {text, key, iv} = req.body;
    decrypt_data = aesDecrypt(text,generateMD5Hash(key),generateMD5Hash(iv))
    console.log('【解密结果】 - ',decrypt_data)
    res.status(200).json({code: 1, data: JSON.parse(decrypt_data)});
});


// 检测服务是否已打开
app.get('/run', (req, res) => {
    res.status(200).json({code: 1});
});


app.listen(4000, () => {
    console.log('Node.js 服务监听端口 127.0.0.1:4000');
});

翻译请求.py】运行此代码前,请先运行js文件,js文件运行后会启动接口,python要调用接口实现解密

python 复制代码
import json
import random
import requests
import time
import hashlib
import base64


# base64 解密
def base64_decode(str):
    # print(base64.b64decode('MTIz'.encode()))
    return base64.b64decode(str).decode('utf-8')
# 这个是链接被base64加密了,此步骤只用于去敏,无其他作用
URL = base64_decode('aHR0cHM6Ly9kaWN0LnlvdWRhby5jb20=')

class youdao:
    # MD5 - SHA3_512 加密
    def md5_encrypt(self, string):
        '''
        'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
        'blake2b', 'blake2s',
        'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
        'shake_128', 'shake_256'
        '''
        md5 = hashlib.md5()
        md5.update(string.encode('utf-8'))
        return md5.hexdigest()

    # 获取 secretKey / aesKey / aesIv
    def obtain_key(self):
        '''获取 secretKey / aesKey / aesIv'''
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
        }
        url = URL + "/webtranslate/key"
        # 13位时间戳
        mysticTime = time.time()
        # 立马的key是官网固定的
        sign = self.md5_encrypt(
            f'client=fanyideskweb&mysticTime={mysticTime}&product=webfanyi&key=asdjnjfenknafdfsdfsd')
        params = {
            "keyid": "webfanyi-key-getter",
            "sign": sign,
            "client": "fanyideskweb",
            "product": "webfanyi",
            "pointParam": "client,mysticTime,product",
            "mysticTime": f"{mysticTime}",
        }
        response = requests.get(url, params=params, headers=headers).json()['data']
        # sign密钥
        self.secretKey = response['secretKey']
        # key密钥
        self.aesKey = response['aesKey']
        # iv向量
        self.aesIv = response['aesIv']

    # 翻译请求 返回密文
    def translate(self, text, froms, tos):
        '''执行翻译请求,返回密文'''
        headers = {
            "Accept": "application/json, text/plain, */*",
            "Content-Type": "application/x-www-form-urlencoded",
            "Referer": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
        }
        # 13位时间戳
        mysticTime = time.time()
        sign = self.md5_encrypt(
            f'client=fanyideskweb&mysticTime={mysticTime}&product=webfanyi&key={self.secretKey}')
        # 随机,伪装一下
        cookies = {
            "OUTFOX_SEARCH_USER_ID": f"-{random.randint(10000000, 99999999)}@{random.randint(1, 255)}.{random.randint(1, 255)}.{random.randint(1, 255)}.{random.randint(1, 255)}"
            # 防封,刷新一次可以使用20年有效期
        }
        url = URL + "/webtranslate"
        data = {
            "i": text,
            # "from": "en",  # 输入日文  en英文 ja日文  fr法语 zh-CHS中文 de德语 es西班牙语
            # "to": "zh-CHS",  # 翻译为中文
            "dictResult": "true",
            "keyid": "webfanyi",
            "sign": sign,
            "client": "fanyideskweb",
            "product": "webfanyi",
            "appVersion": "1.0.0",
            "vendor": "web",
            "pointParam": "client,mysticTime,product",
            "mysticTime": f"{mysticTime}",
            "keyfrom": "fanyi.web",
        }
        if froms:
            data['from'] = froms
        if tos:
            data['to'] = tos
        response = requests.post(url, headers=headers, cookies=cookies, data=data).text
        self.ciphertext = response.strip()

    # AES-128-CBC 解密
    def decrypt_AES_128_CBC(self):
        '''AES-128-CBC 解密'''
        import requests

        headers = {'Content-Type': 'application/json'}
        data = {"text": self.ciphertext, "key": self.aesKey, "iv": self.aesIv}

        url = "http://127.0.0.1:4000/"
        response = requests.post(url, headers=headers, data=json.dumps(data)).json()
        text = ''
        if response['code'] == 1:
            for data_list in response['data']['translateResult']:
                text += data_list[0]['tgt']
        print('----- 翻译结果 -----\n' + text + '\n')


main = youdao()
# 获取 secretKey / aesKey / aesIv 只需要运行一次即可,经过我的分析,他每天返回的key都不一样
main.obtain_key()
# 发送翻译请求  # 输入日文  en英文 ja日文  fr法语 zh-CHS中文 de德语 es西班牙语
# froms:当前输入的是什么语言  输入None则自动识别
# tos:要翻译为什么语言    输入None则自动识别
main.translate('你是谁,你叫什么名字?\n怎么称呼大哥\n我的名字是哈哈,请叫我哈哈哥', froms=None, tos=None)   # 自动识别 翻译为 英文(默认)
# 解密请求内容
main.decrypt_AES_128_CBC()

【HOOK代码】

javascript 复制代码
// 【json.parse 解密对象专用  JSON字符串转换为JS对象】
(function () {
    var parse_ = JSON.parse;
    JSON.parse = function (arg) {
        console.log("[J] - 正在执行[*json.parse] -> ", arg);
        debugger;
        return parse_(arg);  // 不改变原来的执行逻辑
    }
})();
相关推荐
API快乐传递者5 分钟前
Python爬虫获取淘宝详情接口详细解析
开发语言·爬虫·python
公众号Codewar原创作者7 分钟前
R数据分析:工具变量回归的做法和解释,实例解析
开发语言·人工智能·python
FL162386312912 分钟前
python版本的Selenium的下载及chrome环境搭建和简单使用
chrome·python·selenium
巫师不要去魔法部乱说15 分钟前
PyCharm专项训练5 最短路径算法
python·算法·pycharm
Chloe.Zz22 分钟前
Python基础知识回顾
python
骑个小蜗牛26 分钟前
Python 标准库:random——随机数
python
Trouvaille ~36 分钟前
【机器学习】从流动到恒常,无穷中归一:积分的数学诗意
人工智能·python·机器学习·ai·数据分析·matplotlib·微积分
汤姆和佩琦1 小时前
2024-12-25-sklearn学习(20)无监督学习-双聚类 料峭春风吹酒醒,微冷,山头斜照却相迎。
学习·聚类·sklearn
是十一月末1 小时前
Opencv实现图像的腐蚀、膨胀及开、闭运算
人工智能·python·opencv·计算机视觉
IT女孩儿1 小时前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript