逆向案例8——基于某山翻译的接口,遇到新的函数用法

1.python代码展现

import requests
import execjs
while True:
    question = input('请输入你要翻译的中文:')
    sign = execjs.compile(open('demo11.js','r',encoding='utf-8').read()).call('getSign',question)

    headers = {
        'authority': 'ifanyi.iciba.com',
        'accept': 'application/json, text/plain, */*',
        'accept-language': 'zh-CN,zh;q=0.9',
        'content-type': 'application/x-www-form-urlencoded',
        'origin': 'https://www.iciba.com',
        'referer': 'https://www.iciba.com/',
        'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
        '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/120.0.0.0 Safari/537.36',
    }

    params = {
        'c': 'trans',
        'm': 'fy',
        'client': '6',
        'auth_user': 'key_web_new_fanyi',
        'sign': sign,
    }

    data = {
        'from': 'zh',
        'to': 'en',
        'q': question,
    }

    response = requests.post('https://ifanyi.iciba.com/index.php', params=params, headers=headers, data=data).json()
    decrypt_data = execjs.compile(open('demo11.js','r',encoding='utf-8').read()).call('f',response['content'])
    print(decrypt_data['out'])

2.js代码展现

const CryptoJS = require('crypto-js');

s = function (e) {
    e = decodeURIComponent(e);
    for (var t = String.fromCharCode(e.charCodeAt(0) - e.length), r = 1; r < e.length; r++)
        t += String.fromCharCode(e.charCodeAt(r) - t.charCodeAt(r - 1));
    return t
};


function l(e) {
    var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "%5C%C2%80%C2%9A%C2%A8%C2%B6%C2%B8y%C2%9B%C2%B2%C2%8F%7C%7F%C2%97%C3%88%C2%A9d"
        , r = CryptoJS.enc.Utf8.parse(s(t))
        , o = CryptoJS.AES.encrypt(e, r, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    });
    return o.toString()
}

data = '50163343871b0bb6'

/*data是经过加密来的
u()("6key_web_new_fanyi".concat(s.LI).concat(t.q.replace(/(^\s*)|(\s*$)/g, ""))).toString().substring(0, 16),合理推测是MD5加密
 */
function getSign(search_word) {
    concatSearxhParams = '6key_web_new_fanyi' + '6dVjYLFyzfkFkk' + search_word;
    md5results = CryptoJS.MD5(concatSearxhParams).toString().substring(0, 16);
    sign = l(md5results)
    return encodeURIComponent(sign)

}



// 解密函数
function f(e) {
    var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "aahc3TfyfCEmER33"
        , r = CryptoJS.enc.Utf8.parse(t)
        , o = CryptoJS.AES.decrypt(e, r, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    });
    return JSON.parse(o.toString(CryptoJS.enc.Utf8))
}

进入翻译界面,打开抓包工具,刷新,确定包

 

 

发现有sign参数加密,返回的数据也进行了加密。

 

 

按关键字进行搜索,发现没有结果,因此从启动器出发

 

在send处打上断点,再发送一次翻译

 

点击右侧堆栈 

发现这个堆栈有加密数据

 

在这里打上断点,进行断点在这里

发现这一行代码

v("/index.phpc=trans&m=fy&client=6&auth_user=key_web_new_fanyi&sign=".concat(encodeURIComponent(r)

encodeURIComponent(r) 是一个 JavaScript 函数,用于将字符串转换为 URI 编码格式,以便在 URL 中传输特殊字符或非 ASCII 字符时使用。该函数将字符串中的每个字符都转换为其 UTF-8 编码的十六进制表示,并将其中的特殊字符(如空格、符号等)用相应的编码表示,以确保在 URL 中的正确传输。

例如,如果 r 是一个字符串:"Hello, 世界!",则调用 encodeURIComponent(r) 后会返回编码后的字符串:"Hello%2C%20%E4%B8%96%E7%95%8C%EF%BC%81"。在这个编码后的字符串中,空格被转换为 %20,逗号被转换为 %2C,中文字符被转换为 UTF-8 编码的十六进制表示。

这样做的好处是,编码后的字符串可以安全地包含在 URL 中,而不会造成 URL 结构的破坏,保证了 URL 的正确性和可靠性。

在上方有返回R

r = (0,_.$Q)(r)

调用某个函数,并将结果赋值给变量 r。在这段代码中,(0, _.$Q) 是一个函数调用,参数为 r。它使用了一种特殊的调用方式,可能是为了处理函数作用域或者确保函数调用的上下文环境。

因此进入这个函数

 

在return处打上断点,复制代码在js中 ,运行,发现缺了s(t)函数,其实就在上方,在上方复制。

现在回到最开始的函数。发现r还是经过u()的加密

  1. u() 是一个函数调用,可能是某个函数库或对象中的方法。
  2. "6key_web_new_fanyi".concat(s.LI) 将字符串 "6key_web_new_fanyi" 和变量 s.LI 进行连接。
  3. .concat(t.q.replace(/(^\s*)|(\s*$)/g, "")) 将变量 t.q 进行替换操作,去除首尾空格,并与前面的字符串连接。
  4. .toString() 将生成的字符串转换为字符串类型。
  5. .substring(0, 16) 取字符串的子串,从索引 0 开始,长度为 16。

综上,这行代码的作用是构建一个字符串,然后对其进行处理,最终得到一个长度为 16 的子字符串。

 

对u()的加密进行分析

 

 

可以发现是32位,推测是MD5加密,在在线加密网站验证,发现果然如此。而且从下面的图片,可以知道加密的内容是两个固定字符串加搜索内容。 

 

由此构建函数代码

function getSign(search_word) {
    concatSearxhParams = '6key_web_new_fanyi' + '6dVjYLFyzfkFkk' + search_word;
    md5results = CryptoJS.MD5(concatSearxhParams).toString().substring(0, 16);
    sign = l(md5results)
    return encodeURIComponent(sign)

}

首先接受搜索参数,进行字符串组合,再进行md5加密,然后将经过加密的参数,在传入另一个加密函数,再次加密得到sign。

解密过程类似,而且解密函数就在下方。

 

 

 content: JSON.parse((0,_.B6)(e.content)) 进入B6函数,接受e,content的加密数据

发现他是一个AES解密

 

 

 

 

 

 

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学习各种软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值