本文只做技术交流,若有冒犯请告知我撤除
百度翻译不知道啥时候开始转向收费的形式,开发者可以免费享受200w字符/月的福利
废话少说,入主题
参数分析
参数名 | 参数解释 |
---|---|
from | 要翻译的原文语种 |
to | 翻译结果的目标语种 |
query | 要翻译的内容 |
transtype | 翻译类型(该参数目前发现有 realtime 和 translang 两种,我们使用 realtime 即可) |
simple_means_flag | 未知 |
接口更新后上述链接会返回如下内容
出现错误码 998,这里暂时不对错误码进行解释
我们重新看看新的接口
通过 Chrome Dev Tools 可以看到,新的请求如下
除了原有的5个参数之外,新增了两个参数
参数名 | 参数解释 |
---|---|
sign | 签名(如:719145.924184) |
token | 请求令牌(如:3a1ae6d96bdd2a8e0f2eb367cb23cb83) |
对谷歌翻译接口有研究的大神应该不难发现,sign 的加密后的值与 Google 的翻译加密后的值非常类似,赶紧看看呗
回到 Chrome Dev Tools,观察新的请求发现翻译页面界面总共加载了 8 个 JavaScript 文件,其中最值得我们注意的就是 index_a6525c5.js 该文件了,不要问我为什么,男人的直觉
根据翻译接口 v2transapi 可以查到一段迷之代码
langIsDeteced: function(t, a, n, r) {
if (null !== t) {
var s = $(".select-from-language .language-selected").attr("data-lang"),
o = $(".select-to-language .language-selected").attr("data-lang"),
l = null;
r && !c.get("fromLangIsAuto") && s !== t ? l = i.processOcrLang(t, s, o) : (e.show(t, s), l = i.getLang(t, s, o)),
u.show();
var g = this,
a = this.processQuery(a),
d = {
from: l.fromLang,
to: l.toLang,
query: a,
transtype: n,
simple_means_flag: 3,
sign: h(a),
token: window.common.token
};
this.translateXHR && 4 !== this.translateXHR.readyState && this.translateXHR.abort(),
this.translateXHR = $.ajax({
type: "POST",
url: "/v2transapi",
cache: !1,
data: d
}).done(function(t) {
c.set("isInRtTransState", !0),
g.translateSuccess(t, l.fromLang, l.toLang, a)
})
}
}
很明显可以看出就是翻译的核心代码,其中 token 的值直接通过 window.common.token
获取即可,稍微要费点心思的就是 sign 的值,通过 h(a)
获取的
其中 a 是传入的待翻译的内容,那么很容易猜到 h()
这个函数会返回 719145.924184
类似这样的结果,并且多次尝试发现,a 的值固定的情况下,h()
的返回值也是固定的,这样也方便大家测试
因为代码加密过,需要男人的直觉给我们更多的指引..
看这段代码
define("translation:widget/translate/input/pGrab",
function(r, o, t) {
"use strict";
function a(r, o) {
for (var t = 0; t < o.length - 2; t += 3) {
var a = o.charAt(t + 2);
a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a),
a = "+" === o.charAt(t + 1) ? r >>> a: r << a,
r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a
}
return r
}
function n(r) {
var o = r.length;
o > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(o / 2) - 5, 10) + r.substr( - 10, 10));
var t = void 0,
n = "" + String.fromCharCode(103) + String.fromCharCode(116) + String.fromCharCode(107);
t = null !== C ? C: (C = window[n] || "") || "";
for (var e = t.split("."), h = Number(e[0]) || 0, i = Number(e[1]) || 0, d = [], f = 0, g = 0; g < r.length; g++) {
var m = r.charCodeAt(g);
128 > m ? d[f++] = m: (2048 > m ? d[f++] = m >> 6 | 192 : (55296 === (64512 & m) && g + 1 < r.length && 56320 === (64512 & r.charCodeAt(g + 1)) ? (m = 65536 + ((1023 & m) << 10) + (1023 & r.charCodeAt(++g)), d[f++] = m >> 18 | 240, d[f++] = m >> 12 & 63 | 128) : d[f++] = m >> 12 | 224, d[f++] = m >> 6 & 63 | 128), d[f++] = 63 & m | 128)
}
for (var S = h,
u = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), l = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(51) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(98)) + ("" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(102)), s = 0; s < d.length; s++) S += d[s],
S = a(S, u);
return S = a(S, l),
S ^= i,
0 > S && (S = (2147483647 & S) + 2147483648),
S %= 1e6,
S.toString() + "." + (S ^ h)
}
var C = null;
t.exports = n
});;
返回的结果中有一段 S.toString() + "." + (S ^ h)
,与我们期望的加密值结构非常类似
嗯,其实这段代码就是加密的核心代码,有兴趣的朋友可以详细理解,这里就不多说啦
我已经将加密的代码单独整理出来,大家直接调用即可
这里再啰嗦一下,加密过程中需要用到一个名为 gtk
的参数,该值直接通过 window.gtk 即可获取,可以获取网页源码后通过 regex 匹配该值,这个值是可以重复使用的,不需要每次调用,所以再初始化的时候获取到即可
好啦,不啰嗦了,加密源码奉上
https://www.devtool.top/upload/2018/01/u5kr8hdmtgikjqpi8ta7q00sqr.zip
后续会有更多有趣的内容和大家分享讨论,如果觉得对你有帮助的话点下关注~~