js逆向--百度翻译
分析并找出请求接口
百度翻译的结果在页面上是局部刷新的,我们可以直接打开浏览器抓包工具,分析ajax请求,找出请求的数据包:
我们发现数据是通过https://fanyi.baidu.com/v2transapi?from=zh&to=en这个请求发送的,Post请求
分析该请求的参数:
simple_means_flag参数
我们可以全局搜索(ctrl+shift+f)下这些参数,比如simple_means_flag,这种命名的参数应该好定位
发现就一个index_a1bebf2.js文件,并且显示的两处位置上的参数和表单参数一致,点击第1971行直接进入s文件,然后点击左下角的{}进行格式化展示,然后局部搜索(ctrl+f)参数simple_means_flag,找到位置后,选择一行打上断点(在代码前方行号处点击一下),然后刷新页面,会停在此处
我们可以看出,simple_means_flag参数是一个常量3
sign参数:
可以看到sign参数是通过调用L函数获取的,我们将鼠标放在L上,发现调用的是内部 e 函数,直接点击进入这个函数,并将该函数作为入口函数,然后看该函数里调用了哪些别的函数,后面都按照同样的方法,缺什么就找什么的思想,复制出来js代码
我们先将e函数的内部实现代码复制出来
function e(r) { var o = r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g); if (null === o) { var t = r.length; t > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(t / 2) - 5, 10) + r.substr(-10, 10)) } else { for (var e = r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), C = 0, h = e.length, f = []; h > C; C++) "" !== e[C] && f.push.apply(f, a(e[C].split(""))), C !== h - 1 && f.push(o[C]); var g = f.length; g > 30 && (r = f.slice(0, 10).join("") + f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) + 5).join("") + f.slice(-10).join("")) } var u = void 0 , l = "" + String.fromCharCode(103) + String.fromCharCode(116) + String.fromCharCode(107); u = null !== i ? i : (i = window[l] || "") || ""; for (var d = u.split("."), m = Number(d[0]) || 0, s = Number(d[1]) || 0, S = [], c = 0, v = 0; v < r.length; v++) { var A = r.charCodeAt(v); 128 > A ? S[c++] = A : (2048 > A ? S[c++] = A >> 6 | 192 : (55296 === (64512 & A) && v + 1 < r.length && 56320 === (64512 & r.charCodeAt(v + 1)) ? (A = 65536 + ((1023 & A) << 10) + (1023 & r.charCodeAt(++v)), S[c++] = A >> 18 | 240, S[c++] = A >> 12 & 63 | 128) : S[c++] = A >> 12 | 224, S[c++] = A >> 6 & 63 | 128), S[c++] = 63 & A | 128) } for (var p = m, F = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), D = "" + 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)), b = 0; b < S.length; b++) p += S[b], p = n(p, F); return p = n(p, D), p ^= s, 0 > p && (p = (2147483647 & p) + 2147483648), p %= 1e6, p.toString() + "." + (p ^ m) }
在执行上述js的过程中会发现i未定义,我们可以将断点打在sign参数这一行,然后刷新页面,然后按步调试,进入 e 函数内部,当调试到 i 这个位置后面一步时,我们直接在调试工具中输入console.log(i),结果发现i是一个定值320305.131321201
我们可以直接在e函数内部开头定义: var i = 320305.131321201;
继续调试,发现内部还调用了n函数,我们继续将n函数的代码复制到js到文件中
function n(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 }
token参数:
我们可以全局搜索一下这个字符串:‘941a269718a9898ff17cdafd59383800’
发现token参数存在于https://fanyi.baidu.com/?aldtype=16047这个html页面中
我们可以直接向这个页面发送请求,然后提取token即可
domain参数:
我们可以接着前面的调试,按照一步一步调试的方式,进入domain参数后面的实现函数内部
至此,我们可以通过console调试来输出一下M参数的值
我们发现domain参数也是一个常量,值为common
其它参数:
from参数:需要翻译的语言类型
to参数:翻译成何种语言
query参数:需要翻译的内容
transtype:翻译的类型,比如realtime,enter等,经验证,此参数可以不用携带
至此,所有的请求参数以及数据包我们都已经厘清了,包括js代码也已经抽取出来了
接下来我们就开始编写代码
编写python脚本:
需要注意的一点,百度翻译请求头中需要验证cookie中的BAIDUID这个参数,所以我们直接利用会话(session)来发送请求
# coding:utf-8 import re import string import execjs import requests class BaiduTranslator: '''百度翻译''' def __init__(self): self.get_token_url = 'https://fanyi.baidu.com/?aldtype=16047' self.url = 'https://fanyi.baidu.com/v2transapi' self.sess = requests.session() self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', } self.ctx = execjs.compile(open('./baidufanyi.js',encoding='utf-8').read()) def run(self): '''入口''' print('(温馨提示,目前仅支持中英文互译)') self.sess.get(url=self.get_token_url,headers = self.headers) #先获取一下cookie while True: content = input('请输入需要翻译的内容(q退出):') if content == 'q': print('欢迎下次使用!') self.sess.close() #关闭此次会话 break print('翻译结果:{}'.format(self.translate(content))) def translate(self,content): '''翻译''' #请求参数,语言翻译类型(支持中英文互译) params = { 'from': 'en' if content[0] in string.ascii_letters else 'zh', 'to': 'zh' if content[0] in string.ascii_letters else 'en', } formdata = { 'from':params['from'], 'to':params['to'], 'query':content, # 'transtype':'realtime', 该参数可以不带上 'simple_means_flag':'3', 'sign':self.get_sign(content), #执行js代码获取 'token':self.get_token(), 'domain':'common' } result = self.sess.post(self.url,params=params,data=formdata,headers = self.headers).json() try: return result['trans_result']['data'][0]['dst'] except: return '翻译出错' def get_token(self): '''获取token参数''' text = self.sess.get(self.get_token_url, headers=self.headers).text token = re.search(r"token: '(.*?)',", text).group(1) #从页面中提取 return token def get_sign(self,content): '''获取sign参数''' sign = self.ctx.call('e',content) return sign if __name__ == "__main__": BaiduTranslator().run()
js文件
function n(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 e(r) { var i = '320305.131321201'; var o = r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g); if (null === o) { var t = r.length; t > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(t / 2) - 5, 10) + r.substr(-10, 10)) } else { for (var e = r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), C = 0, h = e.length, f = []; h > C; C++) "" !== e[C] && f.push.apply(f, a(e[C].split(""))), C !== h - 1 && f.push(o[C]); var g = f.length; g > 30 && (r = f.slice(0, 10).join("") + f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) + 5).join("") + f.slice(-10).join("")) } var u = void 0 , l = "" + String.fromCharCode(103) + String.fromCharCode(116) + String.fromCharCode(107); u = null !== i ? i : (i = window[l] || "") || ""; for (var d = u.split("."), m = Number(d[0]) || 0, s = Number(d[1]) || 0, S = [], c = 0, v = 0; v < r.length; v++) { var A = r.charCodeAt(v); 128 > A ? S[c++] = A : (2048 > A ? S[c++] = A >> 6 | 192 : (55296 === (64512 & A) && v + 1 < r.length && 56320 === (64512 & r.charCodeAt(v + 1)) ? (A = 65536 + ((1023 & A) << 10) + (1023 & r.charCodeAt(++v)), S[c++] = A >> 18 | 240, S[c++] = A >> 12 & 63 | 128) : S[c++] = A >> 12 | 224, S[c++] = A >> 6 & 63 | 128), S[c++] = 63 & A | 128) } for (var p = m, F = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), D = "" + 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)), b = 0; b < S.length; b++) p += S[b], p = n(p, F); return p = n(p, D), p ^= s, 0 > p && (p = (2147483647 & p) + 2147483648), p %= 1e6, p.toString() + "." + (p ^ m) }
结果演示: