温馨提示:
爬虫玩得好,监狱进得早。数据玩得溜,牢饭吃个够。
《刑法》第 285 条,非法获取计算机信息系统数据罪。
违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。
正文:
之前我们 破解了有道翻译 的案例,现在我们应该有这样一种意识,如果我们当时分析的JS代码很复杂怎么办,没关系,本文将介绍更高级的一种手段—— JS逆向,我们将通过 破解百度翻译 这一案例,来学习JS逆向工程
介绍
【1】应用场景
当JS加密的代码过于复杂,没有办法破解时,考虑使用JS逆向思想
【2】模块
2.1》模块名:execjs
2.2》安装: sudo pip3 install pyexecjs
2.3》使用流程
import execjs
with open('xxx.js', 'r') as f:
js_code = f.read()
js_obj = execjs.compile(js_code)
# 把一个字符串当成一个表达式来执行
js_obj.eval('函数名("参数")')
接下来我们还是像之前的有道翻译的那样,翻译一个单词,直接抓包:
我们看到抓包,文件中有一个v2transapi开头的文件,点开它就是我们抓包抓到的数据,在General中我们看到了,url请求地址,以及重要的post请求方式,然后我们看Form Data里面,我们其他的不用看我们看两个:token、sign,其中,token是不变的,而sign是变化的,翻译一个单词他就会变化一次,我们需要对他处理
接着,我们还是搜索在JS里面搜索sign:
我们看这个跟我们之前的不一样啊,好多包含sign的js文件啊,我们只能一个一个看,但是在这我就直接点了啊,就点第一个包含sign的js文件,因为我们看到sign:他后面还有一个token,大概就是了
点进去,跳转到Sources选项卡中,格式化一下,在这个文件里面再搜索sign:
打上断点后,我们接着,在翻译一个单词:
把鼠标放到y上,我们进入到上面显示的 f e® 函数里面去:
这个就是我们所说的生成sign加密的函数,然后把这段代码复制出来,我们在调试,复制从最上面复制,就是下面划横线的那个函数里面的代码,就是 红框框起来的所有代码
创建一个JS文件先给他存进去,这是三个函数,其中1、2不用管,我们看第3个函数,这个函数就是真正生成sign的函数,我们看这个函数的参数是 r,对于这个r是什么,我们再翻译一个单词就知道了
如果刚刚没有停止断点的话,我们点击这个,就可以往下走就会显示出了:
(如果停止上一次断点了,就接着在那个函数下面打断点,在翻译一个单词就看到了)
好了,我们看到这个r, 就是我翻译的单词,所以我们要调用e这个函数:
到这里我们分析的就这样了,但是我们还是没有分析完,因为一般来说不会这样顺利的,找出代码,一执行就完了,这是不可能的,所以接下来我们用Python代码测试一下:
# 调试js代码的库
import execjs
# 只读打开文件
with open('translate.js', 'r') as f:
js_code = f.read()
# 创建编译对象
js_obj = execjs.compile(js_code)
# 把一个字符串当成一个表达式来执行
sign = js_obj.eval('e("dragon")')
print(sign)
运行代码:
直接报错,我们看报错最后一行,t is not defined ,这个是我们刚才复制的js代码,最后一行,那我们就先把他注释上,再次运行:
还是报错,这次的错误是window is not defined,那我们在js代码里找window:
这里大概在32行左右,这里的window[l],中的l,对应着上一行的l,它又对应着 后面一大串代码,这个是,字符串的Ascii码,接着复制 l 后面段代码,接着Console一下:
这个打印出来的是gtk,应该是代码上面分别对应的:
左边103的是g
中间116的是t
右边107的是k
那我们明白了,window[l],中的l,就是gtk,那我们去源代码中搜索一下gtk是个什么鬼?
就一处,显示的是window.gtk,那我们这样,直接把他后面的字符串复制到js代码中的window[l]这里,试一下看能不能翻译,因为它获取的就是这段字符串,索性我们就直接给到他:
再运行我们写的Python代码:
这就行了?那我们去在翻译一下,在抓包找一下sign对一下不就知道了吗,我们走着:
没问题,这就粗略的搞定了,那万一有一天百度翻译把这个字符串改了,我们还得去找它,所以我们直接在JS代码里加一个参数,代替他,这样就不怕它会变了,等到我们在页面里面用正则提取出来,作为参数,这样传过去也是没问题的:
这样就OK了,再看token,这个东西是不变的,我们不必在分析它,这个在它的源代码里也是有的,我们就不用管它了:
我们再处理一下 headers和data,headers去抓首页的,data只能是先翻译一个单词再抓加上General 里的 url,处理成这样:
保存一下,这三个一会儿会用到,现在开始写代码:
导入模块
import requests
import execjs
import re
实例化变量
class BaiduTranslateSpider:
def __init__(self):
# 这个是获取到gtk和token后的url,写到逻辑函数时用到
self.url = 'https://fanyi.baidu.com/v2transapi?from=zh&to=en'
# 这个是我们要得获取gtk和token得用到的url
self.index_url = 'https://fanyi.baidu.com/'
# headers写自己浏览器的
self.headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9",
"cookie": "PSTM=1605584756; BAIDUID=621509D1EDB1556EC71CC68C1A5E304C:FG=1; BIDUPSID=1AEB19AD6C78D3C7A1EFF1AEF6482602; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; __yjs_duid=1_cab112ddffaa4af33775ae7d7dfa029c1608807397345; BAIDUID_BFESS=621509D1EDB1556EC71CC68C1A5E304C:FG=1; delPer=0; PSINO=1; H_PS_PSSID=1443_33223_33306_31660_32971_33350_33313_33312_33169_33311_33310_33339_33309_26350_33308_33307_33145_33389_33370; BCLID=8765896679250944447; BDSFRCVID=L9-OJexroG3SwVJrTfwCjurRFtc7jnQTDYLEqQKg3tugmU4VJeC6EG0Ptj35efA-EHtdogKK0gOTH6KF_2uxOjjg8UtVJeC6EG0Ptf8g0M5; H_BDCLCKID_SF=tR3h3RrX26rDHJTg5DTjhPrM2HrJWMT-MTryKKORQnrjqxbSqqo85JDwQRnfKx-fKHnRhlRNtqTjHtJ4bM4b3jkZyxomtfQxtNRJQKDE5p5hKq5S5-OobUPUDMJ9LUkqW2cdot5yBbc8eIna5hjkbfJBQttjQn3hfIkj2CKLtCvHjtOm5tOEhICV-frb-C62aKDs2IIEBhcqJ-ovQTb65pKpKROh-MJBb6rNKtbJJbc_VfbeWfvpKq_UbNbJ-4bLQRnpaJ5nJq5nhMJmM6-hbtKFqto7-P3y523ion3vQpP-OpQ3DRoWXPIqbN7P-p5Z5mAqKl0MLPbtbb0xXj_0-nDSHH-fqT_q3D; BCLID_BFESS=8765896679250944447; BDRCVFR[S4-dAuiWMmn]=I67x6TjHwwYf0; BA_HECTOR=a084852k2k0l00ah2s1fubldu0r; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1608855621,1608855625,1608858163,1608897985; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1608899822; ab_sr=1.0.0_ZDYxNDU1MTNjNDBjOTkwOGMwYzc4Zjk0MDc5NmRjOGU1MDU2YTBjNjkyYWU5ZTIwNTg5Yzc2YjMyZDRhNDJiZDA3ZTdlNGNmNjljODNlYjk5MmE2ZGM4OGJhMzk4ODM1; __yjsv5_shitong=1.0_7_36bddc638f8abe26b68d5c474b5aa7d1bf8b_300_1608899822168_43.254.90.134_bed0d9ad; yjs_js_security_passport=52a6024d37461c03b90ffc37c07f84492340e8cd_1608899823_js",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36", }
获取gtk和token
def get_gtk_token(self):
"""获取gtk和token"""
html = requests.get(url=self.index_url,
headers=self.headers).text
# 正则获取我们之前分析的gtk参数和token参数,因为token数不变的,所以我们不必分析
gtk = re.findall("window.gtk = '(.*?)'", html, re.S)[0]
token = re.findall("token: '(.*?)'", html, re.S)[0]
return gtk, token
接下来就是功能函数:生成sign
def get_sign(self, word):
# word 是要翻译的单词
"""功能函数:生成sign"""
# 先获取到gtk和token
gtk, token = self.get_gtk_token()
# 打开我们之前的JS代码
with open('translate.js', 'r') as f:
js_code = f.read()
js_obj = execjs.compile(js_code)
# eval把一个字符串当成一个表达式来执行
sign = js_obj.eval('e("{}","{}")'.format(word, gtk))
return sign
爬虫逻辑函数
def attack_bd(self, word):
"""爬虫逻辑函数"""
gtk, token = self.get_gtk_token()
sign = self.get_sign(word)
# 我们之前处理的data数据,这个也是用自己浏览器的
data = {
"from": "en",
"to": "zh",
"query": word,
"transtype": "realtime",
"simple_means_flag": "3",
"sign": sign,
"token": token,
"domain": "common",
}
# json():把json格式的字符串转为python数据类型
html = requests.post(url=self.url,
data=data,
headers=self.headers).json()
result = html['trans_result']['data'][0]['dst']
return result
最后奉上全部代码:
import requests
import execjs
import re
class BaiduTranslateSpider:
def __init__(self):
self.url = 'https://fanyi.baidu.com/v2transapi?from=zh&to=en'
self.index_url = 'https://fanyi.baidu.com/'
self.headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9",
"cookie": "PSTM=1605584756; BAIDUID=621509D1EDB1556EC71CC68C1A5E304C:FG=1; BIDUPSID=1AEB19AD6C78D3C7A1EFF1AEF6482602; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; __yjs_duid=1_cab112ddffaa4af33775ae7d7dfa029c1608807397345; BAIDUID_BFESS=621509D1EDB1556EC71CC68C1A5E304C:FG=1; delPer=0; PSINO=1; H_PS_PSSID=1443_33223_33306_31660_32971_33350_33313_33312_33169_33311_33310_33339_33309_26350_33308_33307_33145_33389_33370; BCLID=8765896679250944447; BDSFRCVID=L9-OJexroG3SwVJrTfwCjurRFtc7jnQTDYLEqQKg3tugmU4VJeC6EG0Ptj35efA-EHtdogKK0gOTH6KF_2uxOjjg8UtVJeC6EG0Ptf8g0M5; H_BDCLCKID_SF=tR3h3RrX26rDHJTg5DTjhPrM2HrJWMT-MTryKKORQnrjqxbSqqo85JDwQRnfKx-fKHnRhlRNtqTjHtJ4bM4b3jkZyxomtfQxtNRJQKDE5p5hKq5S5-OobUPUDMJ9LUkqW2cdot5yBbc8eIna5hjkbfJBQttjQn3hfIkj2CKLtCvHjtOm5tOEhICV-frb-C62aKDs2IIEBhcqJ-ovQTb65pKpKROh-MJBb6rNKtbJJbc_VfbeWfvpKq_UbNbJ-4bLQRnpaJ5nJq5nhMJmM6-hbtKFqto7-P3y523ion3vQpP-OpQ3DRoWXPIqbN7P-p5Z5mAqKl0MLPbtbb0xXj_0-nDSHH-fqT_q3D; BCLID_BFESS=8765896679250944447; BDRCVFR[S4-dAuiWMmn]=I67x6TjHwwYf0; BA_HECTOR=a084852k2k0l00ah2s1fubldu0r; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1608855621,1608855625,1608858163,1608897985; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1608899822; ab_sr=1.0.0_ZDYxNDU1MTNjNDBjOTkwOGMwYzc4Zjk0MDc5NmRjOGU1MDU2YTBjNjkyYWU5ZTIwNTg5Yzc2YjMyZDRhNDJiZDA3ZTdlNGNmNjljODNlYjk5MmE2ZGM4OGJhMzk4ODM1; __yjsv5_shitong=1.0_7_36bddc638f8abe26b68d5c474b5aa7d1bf8b_300_1608899822168_43.254.90.134_bed0d9ad; yjs_js_security_passport=52a6024d37461c03b90ffc37c07f84492340e8cd_1608899823_js",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36", }
def get_gtk_token(self):
"""获取gtk和token"""
html = requests.get(url=self.index_url,
headers=self.headers).text
gtk = re.findall("window.gtk = '(.*?)'", html, re.S)[0]
token = re.findall("token: '(.*?)'", html, re.S)[0]
return gtk, token
def get_sign(self, word):
"""功能函数:生成sign"""
# 先获取到gtk和token
gtk, token = self.get_gtk_token()
with open('translate.js', 'r') as f:
js_code = f.read()
js_obj = execjs.compile(js_code)
sign = js_obj.eval('e("{}","{}")'.format(word, gtk))
return sign
def attack_bd(self, word):
"""爬虫逻辑函数"""
gtk, token = self.get_gtk_token()
sign = self.get_sign(word)
data = {
"from": "en",
"to": "zh",
"query": word,
"transtype": "realtime",
"simple_means_flag": "3",
"sign": sign,
"token": token,
"domain": "common",
}
# json():把json格式的字符串转为python数据类型
html = requests.post(url=self.url,
data=data,
headers=self.headers).json()
result = html['trans_result']['data'][0]['dst']
return result
def run(self):
word = input('请输入要翻译的单词:')
print(self.attack_bd(word))
if __name__ == '__main__':
spider = BaiduTranslateSpider()
spider.run()