百度翻译JS逆向
百度翻译 sign 加密参数逆向过程分析
需求分析
实现一个我们自己的百度翻译,暂定 英译中
抓包
首先在翻译栏中输入内容点翻译按钮,F12抓包一下
发现每一次点击翻译都会请求两个地址
langdetect
请求,从请求参数,以及返回值 很明显的能发现是一个检查当前输入内容是什么语种的请求。
v2transapi
这个请求就是真正的翻译请求,翻译一下名字也许是v2 trans api
, v2
版本的翻译
api
?
将抓到的参数用python
requests
库请求一下看看
试了很多次发现请求头里面需要携带cookie
,这个cookie
是我在无痕模式下copy
过来的,应该是没有携带上个人信息的。
这个时候我们发现,把参数原封不动的复制过来是可以直接请求成功的。既然这样,我们换点翻译内容多试几次看看请求参数有什么不同。
- 测试用例:
hello
- 测试用例:
word
- 测试用例:
baidu
经过多个样例可以发现请求参数的大致一些规律
form
,to
这个很好理解是从什么语种翻译到什么语种
query
是我们的查询参数
transtype
,simple_means_flag
,token
,domain
是固定参数,没有变化。
唯一有变化的是 sign
参数,看样子是需要解决这个参数的加密方式了
并且发现,在query参数一样的时候,请求的sign参数也是一样的。先合理猜测一下。sign
是根据query
参数加密得来的。
逆向
全局搜索
全局搜索 v2transapi
发现在 index.js
文件里面定义了两处,点进index.js文件中,先是格式化一下,然后在这个文件内 ctrl + f
搜索一下v2transapi
直接在两处打上断点,然后点翻译按钮看一下js的执行过程。
我就这里就直接的断在了url
的地方,点翻译按钮js执行到了这里了。然后我们点一下下一步。发现进入到了这个done里面了。
这个时候如果会一点js就很快能懂这里的执行逻辑了,是用的 jQuery
的$
发起一个ajax
请求。那我们重点关注一下这个ajax
请求的data
参数是怎么来的。
往上找一点就很容易发现,这个请求是在 langIsDeteced
这个方法里面定义的,在请求的上面就定义了一些参数。
y参数构造
我们分析一下这个 y
参数的构造,在结合上面抓包的分析,我们重点是要看一下 sign
这个参数是怎么来的,
这个时候我们吧所有的断点取消掉,只在这个sign
这个地方加一个断点,方便调试。
大致看一下 sign
是通过调用 I
函数传了一个 e
过去,再看一下query
参数对应的值很容易猜出来这个e
就是我们的查询参数。
I
函数分析
这个时候断底一下进入到这个 I
函数 看一下内部是怎么实现的。点这个按钮就能进入这个函数内部了。
不过进入之后我们发现我们进入到了一个 e
函数内部,刚刚明明调用的是 I
函数怎么进入到了e
函数内部呢。不过仔细的看一下参数r
也确实是我们的查询参数。了解一点js
的能好理解一点,这个文件导出了一个函数,在另外一个文件内引入了这个函数并且重命名了。(题外话,这个重命名估计是代码打包过程做的js混淆)
现在进入到了这个e 函数内部也好棘手啊,右边作用域内一大堆的参数头都要晕掉。
这个时候不要慌,我们发现这个函数一共才30几行。我们直接复制到一个 index.html文件中。执行一下这段脚本看看什么情况。
执行一下看一下控制台
什么?报错了,23行 i is not defined
。
不要慌,30行的代码,缺什么我们补什么,要有信心,看一下源代码里面这个i
是个什么东东
这里是给这个u
赋值,看i
是不是null
,是null
就取 window[l]
不是就取 i
这里又出来了一个 window[l]
,不要怕,我们的代码里面肯定是看不出来这个是啥的,我们去刚刚断点调试的环境里面看一下这个是什么东东。
看我圈起来的地方定义了l
,好像是一个固定值,gtk
那现在就是取 window['gtk']
这个时候就比较迷惑了,这个值哪来的。刚刚分析 u
的值就是
window['gtk']
,然后在右边的参数栏里面就能发现 u = window['gtk'] = 320305.131321201
我们现在全局的搜索一下320305.131321201
点进去看一下
然后我们就发现了,在这个script
里面就找到了定义的地方,注意我圈起来的地方定义了token
。我们请求的参数里面也有一个token
。
这个时候,你应该问我,你怎么知道这个token
就是我们请求时候的token
。
其实是我之前在分析的时候先分析的token
参数,全局搜索搜索到这里的。而且回过头看一下y参数构造里面的token
,直接就是取的 window.common.token
。
补环境
现在我们知道了window['gtk']
是一个固定值,那我们就在我们的代码里面给他补上
这个时候我们在到浏览器里面看一下这个函数的调用有没有符合我们的预期结果。
TNND,你怎么还不出结果,你给我出啊!
吐槽一番,又报错了,36行的n is not defined
我们再去源代码中找一下n函数的调用,断点调试进去看看。
如图,这个时候断点要打在n
函数上,在行号上打断点的时候会出现两个向右的点头,n
函数前面的箭头要断点上
发现n
函数就在 e
函数前面定义着,而且也就8
行,直接复制到我们的环境中。
这个时候在看一下控制台的执行结果。
我擦,感动,结果已经出来了。这个时候在对比一下我们copy
来的代码输出结果和请求时候携带的sign
是否一致。发现是一致的,那么至此我们的sign
参数就逆向完成了。
Spider启动
现在这个sign
的加密过程我们就拿到了,不过是js
代码,我们的spider
是用Python
写的。
现在就有两个思路
- 我们模仿js的逻辑写一个Python的函数出来。因为代码一共也就40来行。
- 我们用Python去调用这个js代码
我们这里采用 PyExecJS
这个Python
库采用第二种思路来实现 sign
的加密函数。
signFun.js
内容,改变了第一行,先吧window
定义成了一个对象,因为PyExecJS
运行环境默认是node
环境,在node
环境中没有window
全局对象
运行成功
总结
至此基本的百度翻译逆向工作就做完了,要做成一个爬虫的话后面还有一下工作要处理,比如cookie
,token
,window['gtk']
,还有就是to
,form
,query
参数的处理,逻辑也比较简单就不写啦。
写这个文章用了一天左右的时间,主要是下班了写了一半没写完,第二题天继续开干,不过也证实了一点,cookie
,token
这些参数是死的,过了一天继续用都没问题。