JS逆向系列之谷歌翻译

一、逆向谷歌翻译

分析过程

  • 首次分析

首先找到谷歌翻译的Ajax请求接口

以get方式发送的Ajax请求,多次尝试,发现请求参数中,tkq,是不断变化的,其中q是待翻译的内容,tk是根据q进行js加密算法生成的,所以我们要分析tk的产生。

  • 通过断点逆向tk的加密算法

加密算法应该在translate_m_zh-CN.js文件里

打个断点,请求翻译接口的URL时停止,发现在send(a)中停止了,根据Call Stack的调用关系,一步步分析,最上面的就是send函数

依次往下点击每一个调用的函数,当到了QE函数的时候,发现了tk参数来自b,b又明显来自c

于是我们顺藤摸瓜,发现c是由PE(d)产生的,PE又是来自pu(a)函数,我们跳转到pu函数的位置,似乎已经有了答案

  • 分析生成tk的JS代码

如下是我们刚获取的JS代码

var mu = function(a) {
    return function() {
        return a
    }
}
  , nu = function(a, b) {
    for (var c = 0; c < b.length - 2; c += 3) {
        var d = b.charAt(c + 2);
        d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d);
        d = "+" == b.charAt(c + 1) ? a >>> d : a << d;
        a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d
    }
    return a
}
  , ou = null
  , pu = function(a) {
    if (null !== ou)
        var b = ou;
    else {
        b = mu(String.fromCharCode(84));
        var c = mu(String.fromCharCode(75));
        b = [b(), b()];
        b[1] = c();
        b = (ou = window[b.join(c())] || "") || ""
    }
    var d = mu(String.fromCharCode(116));
    c = mu(String.fromCharCode(107));
    d = [d(), d()];
    d[1] = c();
    c = "&" + d.join("") + "=";
    d = b.split(".");
    b = Number(d[0]) || 0;
    for (var e = [], f = 0, g = 0; g < a.length; g++) {
        var h = a.charCodeAt(g);
        128 > h ? e[f++] = h : (2048 > h ? e[f++] = h >> 6 | 192 : (55296 == (h & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (h = 65536 + ((h & 1023) << 10) + (a.charCodeAt(++g) & 1023),
        e[f++] = h >> 18 | 240,
        e[f++] = h >> 12 & 63 | 128) : e[f++] = h >> 12 | 224,
        e[f++] = h >> 6 & 63 | 128),
        e[f++] = h & 63 | 128)
    }
    a = b;
    for (f = 0; f < e.length; f++)
        a += e[f],
        a = nu(a, "+-a^+6");
    a = nu(a, "+-3^+b+-f");
    a ^= Number(d[1]) || 0;
    0 > a && (a = (a & 2147483647) + 2147483648);
    a %= 1E6;
    return c + (a.toString() + "." + (a ^ b))
};

发现做了很多不必要的操作、甚至多此一举,目的是用于混淆
如:

var mu = function(a) {
    return function() {
        return a
    }
}

还用了.fromCharCode()函数,把数字转换为字母,明显多此一举的操作。但是,通过分析,发现tk生成需要另一样东西,就是tkk这个参数,来源于网页源代码上,寻找一番,还是发现了,多次尝试,发现这个值是固定的

具体实现

说明:js文件做了精炼,把不必要的值去掉了,实行的效果是英译中,不同语言的翻译修改请求的参数即可

  • Python脚本
import requests
import json
import execjs

def get_tk(q):
    with open('google_tk.js', 'r', encoding='utf-8') as f:
        js = f.read()
    j = execjs.compile(js)
    tk = j.call('pu', q)
    return tk

def translate_google():
    q = input("请输入你要翻译的内容:")
    headers = {
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
        "referer": "https://translate.google.cn/"
    }
    url = "https://translate.google.cn/translate_a/single?client=webapp&sl=en&tl=zh-CN&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=sos&dt=ss&dt=t&otf=2&ssel=0&tsel=0&kc=3&tk=" + get_tk(
        q) + "&q=" + q
    res = requests.get(url, headers=headers)
    data = res.content.decode()
    json_data = json.loads(data)
    print(json_data[0][0][0])

if __name__ == "__main__":
    translate_google()

  • tk.js
//tkk = '441865.2154418832';
var nu = function (a, b) {
    for (var c = 0; c < b.length - 2; c += 3) {
        var d = b.charAt(c + 2);
        d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d);
        d = "+" == b.charAt(c + 1) ? a >>> d : a << d;
        a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d
    }
    return a
},

pu = function (a) {
    tkk='441865.2154418832';
    var d = tkk.split(".");
    var b = Number(d[0]);
    for (var e = [], f = 0, g = 0; g < a.length; g++) {
        var h = a.charCodeAt(g);
        128 > h ? e[f++] = h :
            (2048 > h ? e[f++] = h >> 6 | 192 :
                    (55296 == (h & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (h = 65536 + ((h & 1023) << 10) + (a.charCodeAt(++g) & 1023), e[f++] = h >> 18 | 240, e[f++] = h >> 12 & 63 | 128) :
                        e[f++] = h >> 12 | 224, e[f++] = h >> 6 & 63 | 128), e[f++] = h & 63 | 128)
    }
    a = b;
    for (f = 0; f < e.length; f++)
        a += e[f],
        a = nu(a, "+-a^+6");
    a = nu(a, "+-3^+b+-f");
    a ^= Number(d[1]) || 0;
    0 > a && (a = (a & 2147483647) + 2147483648);
    a %= 1E6;
    return (a.toString() + "." + (a ^ b))
};

运行效果

二、总结常见算法的加密原理

  • 散列(hash)函数算法:MD5、SHA
  • 对称加密算法:DES、3DES、AES
  • 非对称性加密算法:RSA、DSA、ECC

散列(hash)函数算法

  • 简介

Hash也称散列、哈希,对应的英文都是Hash。基本原理就是把任意长度的输入,通过Hash算法变成固定长度的输出。这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值。活动开发中经常使用的MD5和SHA都是历史悠久的Hash算法。

echo md5("这是一个测试文案");// 输出结果:2124968af757ed51e71e6abeac04f98d
  • 特点

一、不可逆,与原视数据没有对应关系
二、输入数据的微小变化会得到完全不同的hash值,相同的数据会得到相同的值
三、hash算法冲突概率比较小,但是根据抽屉原理,一定会存在不同的输入被映射成相同输出的情况

对称加密算法

  • 简介

秘钥:加密解密使用同一个密钥、数据的机密性双向保证、加密效率高、适合加密于大数据大文件、加密强度不高(相对于非对称加密)

非对称性加密算法

  • 简介

秘钥:加密解密使用的不同秘钥、有两个密钥、需要使用密钥生成算法生成两个秘钥、数据的机密性只能单向加密、如果想解决这个问题、双向都需要各自有一对秘钥、加密效率低、加密强度高

posted @ 2021-08-15 19:34  AtSunset  阅读(941)  评论(0)    收藏  举报