前言
本次针对某个翻译平台的js逆向,同时并不存在恶意,只是本着学习研究为主,同时,在分析期间并未高频次测试导致该平台服务器不可用
观察
首先直接体验下:
抓包查看请求的接口:
然后请求参数有这些:
一看,i应该就是我传的参数了,常规思维走起来,直接复制这些参数,然后在python里运行:
我把i换成main翻译试试:
不行了,有点意思哈,为啥复制的english就可以翻译呢?
原因是这样的:
像这种翻译平台,肯定很多人调用,然后同时段用的人肯定很多,加上,有些人有一种习惯,就是在翻译中途,数据还没出来的话,就想再点一次,而此时服务器其实翻译好了正准备返回或者已经返回,这样,服务器缓存一下,可以节省实际的翻译部分代码产生的资源占用,而直接就把翻译结果返回了,同时可以把这个资源给其他需要翻译的人用,所以就会有为什么同一个待翻译字段和同一组请求参数可以用,换一个待翻译字段用同一组请求参数就不能用了。
有朋友会说了,同一个待翻译字段,它同一套加密算法,出来肯定是一样的值啊,所以怎么确定不是同样的翻译一次而是你说的缓存翻译结果直接返回呢,注意哈,请求参数里有个时间戳字段,这个可是每次都会变的啊,所以大概率是我说的那种情况。当然实际情况是这样的啊,我也不是该平台的实际研发人员,所以具体情况我也不清楚哈
接着我又翻译几次之后,发现,就有4个值是一直会变得,其他不会变,就 lts,bv,salt,sign,就这四个,然后i就是我们输入的待翻译的字段
好,不废话,直接看这几个值在哪生成的,直接全局搜sign:
进入这个js文件,再搜,看到有11处,好,那一个一个看下
第一个:
这个的请求参数不太像,再看下面的,
这个也不太像,我们刚才的请求参数没有cache这个值,接着再看:
这个有点像,
再接着看:
这个感觉有点像
分析
对上面看到的两处有点像的,打上断点看看:
动态调试
输入get立即就被断上了,那说明我们打断点打对地方了
那么我们开始找那4个参数怎么来的了
第一个,ts
不用说了,这个就是个时间戳,然后被转成了字符串的,先看下它是多少位的时间戳:
用在线的时间戳转换工具直接看
看来是毫秒级的时间戳了,而我们python默认的time模块并不是毫秒级的:
这个后面乘以个1000然后取整就可以了,ts搞定了
第二个,bv
看代码,bv其实就是t,而t 就是navigator.appversion参数再md5之后的字段,先看下navigator.appversion是啥东西,把鼠标放上去,它自己就显示了
不觉得这个很像个啥吗,搞爬虫,应该一眼就能认出来这是啥了,就是ua啊,
然后用第一个【/】作为分隔符把它分割了:
用python做个验证,这个值果然可以对上
也就是说,只要我们的请求不变,那一直都可以用这个值。
第三个,salt
这个,看代码就看出来,就是用刚才的时间戳参数,然后乘以了一个随机的0到1之间的小数,然后再乘以10取整数跟之前的时间戳以字符串拼接起来的,那不就是直接加了一个0-9的随机整数嘛
第四个,sign
这个就是最重要的了,
而,主要就是看下e和i是啥了,仔细看,
e就是我们待翻译参数
i就是刚才的salt参数了
这个sign参数每次都是由时间戳+待翻译参数生成出来的,这也是为什么,刚才我用同一组固定的请求参数翻译【english】时可以翻译,但是翻译【main】不行了
ok,后面就是代码实现了
python实现
import requests
import time
from hashlib import md5
import random
def get_md5(something):
if isinstance(something, str):
m = md5()
m.update(something.encode(\'utf-8\'))
return m.hexdigest()
def format_ts():
now = time.time()
now = int(now * 1000)
return now
def format_bv(some_headers):
ua = some_headers.get(\'User-Agent\')
ua = ua.split(\'/\', 1)[1]
# print(1231232, ua)
bv = get_md5(ua)
return bv
def format_salt(ts):
# ts + parseInt(10 * Math.random(), 10)
number = random.randint(0, 9)
if not isinstance(ts, str):
ts = str(ts)
salt = ts + str(number)
return salt
def format_sign(keywords, salt):
# n.md5(\"fanyideskweb\" + keywords + salt + \"Tbh5E8=q6U3EXe+&L[4c@\")
k = \'fanyideskweb\' + keywords + salt + \'Tbh5E8=q6U3EXe+&L[4c@\'
sign = get_md5(k)
return sign
def main(keyword):
headers = {
\'Accept\': \'application/json, text/javascript, */*; q=0.01\',
\'Accept-Language\': \'zh-CN,zh;q=0.9\',
\'Connection\': \'keep-alive\',
\'Content-Type\': \'application/x-www-form-urlencoded; charset=UTF-8\',
\'X-Requested-With\': \'XMLHttpRequest\',
\'sec-ch-ua\': \'\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"90\", \"Google Chrome\";v=\"90\"\',
\'sec-ch-ua-mobile\': \'?0\',
\'Sec-Fetch-Dest\': \'empty\',
\'Sec-Fetch-Mode\': \'cors\',
\'Sec-Fetch-Site\': \'same-origin\',
\'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36\',
\'Origin\': \'https://xxxx.com\',
\'Referer\': \'https://xxxx.com\'
}
# 获取结果
url = \'xxxx \' # 保密
data = {
\'i\': keyword,
\'from\': \'AUTO\',
\'to\': \'AUTO\',
\'smartresult\': \'dict\',
\'client\': \'fanyideskweb\',
\'salt\': \'\',
\'sign\': \'\',
\'lts\': \'\',
\'bv\': \'\',
\'doctype\': \'json\',
\'version\': \'2.1\',
\'keyfrom\': \'fanyi.web\',
\'action\': \'FY_BY_REALTlME\'
}
ts = format_ts()
___rl__test__cookies = ts + 3
salt = format_salt(ts)
bv = format_bv(headers)
sign = format_sign(keyword, salt)
if ts and salt and bv and sign:
data[\'lts\'] = ts
data[\'sign\'] = sign
data[\'salt\'] = salt
data[\'bv\'] = bv
req = requests.post(url, headers=headers, data=data)
res = req.content.decode(\'utf-8\')
print(2313123, res)
return res
main(\'get\')
来源:https://www.cnblogs.com/Eeyhan/p/14971034.html
图文来源于网络,如有侵权请联系删除。