作者 | sergiojune
责编 | 郭芮
声明:以下内容仅交流学习,请勿用于非法用途。
如果你现在想模拟登陆知乎,会发现 fromdata 是一串加密的字符串:
看了之后是不是很痛苦?你是不是就想使用 selenium 来模拟登陆?不过好像知乎对 selenium 也进行了相应的反爬处理,哈哈。但是我不也想用 selenium,效率太慢了,直接破解 js 才是我最喜欢挑战的。
好,我现在教你如何用 js 硬撸破解。废话不多说,直接进入正题。
找出signature加密
加密位置这个相对来说是比较难找的,需要自己对那些混淆过的 js 进行一遍又一遍的搜寻,有时候你看到头皮发麻也不一定找得到,再加上这个加密参数是一堆字符串,连个键都没有,搜索的条件都没有。这怎么办呢?我们可以去百度或者谷歌看看前人是怎么搞的。
你直接搜索知乎模拟登陆的话,会发现以前的知乎的 fromdata 是键值形式的,有以下键值对:
顺便也可以看看之前的破解思路,看到了上面的 signature 是通过加密来的,猜测知乎应该也是对之前的键值加密的 fromdata 进行加密的,在控制台搜搜 signature 看看有没有线索:
提示:按下 ctrl + shift + f 可出现搜索框
一搜,果然有,看了看,signature 的加密过程和以前的还是一样的,证明我的猜想正确了,说明是通过之前的 fromdata 的键值对进行加密成的一堆字符串,现在就看看 signature 是如何加密的。
如果你对加密有点了解的话,还容易通过上面代码知道这是通过 hmac 加密,哈希算法是 sha1,密钥为 d1b964811afb40118a12068ff74a12f4,加密数据有四个,为 clientId、grantType、timestamp 和 source,这些值都可以在上面通过调试出来的,就不多说了。如下:
找出fromdata的完整键值对
在知道 signature 是如何加密的之后,我们还需要找出完整的 fromdata 先,不过在上面的调试中,你会发现也有几个 fromdata 值,但是不全。
这时候如果我们得继续搜索 signature 的话,找了一整天你都会发现不到什么线索,这时候我们可以通过登陆的 url 进行突破,看看 url 是哪个路径,然后一顿搜。
其登陆url是 https://www.zhihu.com/api/v3/oauth/sign_in。那我们可以直接搜 sign_in 试试。搜了发现和上面的 signature 是在同一个 js 文件上的,感觉应该有戏。
这个和登陆地址完全匹配,应该就是这个了,可以进行调试一波:
这不出来了,经过多次调试,发现大多数值都是固定的,只有 signature 和 timestamp 不是,其他的就是账号密码之类的,还有个验证码 captcha 以及它的类型 lang,signature 上面的已经找出来了,timestamp 很明显就是时间戳,其他的就不多说了。
现在的 fromdata 已经全部找出来了,我们离加密字符串又近了一步,如果你直接用这个表单进行模拟登陆,会给你返回下面错误
Missing argument grant_type
可见我们还得找出这个 fromdata 的加密方法。
找出 fromdata 加密位置
如果你是第一次找这个,估计你得不断地翻 js,也不一定能找得到,或者你可以根据下面这个调用函数过程来找:
会发现很多,不过你懂套路的话都知道加密一般都用到 encrypt 名字之类的,可以直接根据这个名字搜:
一搜果然有这个,通过查看你很容易就找到这个:
这个一通过调试,你可以看到,我们的加密字符串出来了,是不是很激动,我当时找到了这个的时候激动不得了。
这个是加密的字符串
这个就是我们需要找的
历尽千辛万苦,终于找出了庐山真面目,激动不?先不要激动先,这只是加密的位置,后面的才是最难的!
找出 fromdata 加密的所有方法
知道位置后,我们可以直接把这个加密的 js 方法都扣出来,放在一个 html 文件内执行就好。
在上面找出位置之后,很容易就可以看到这个完整的一个的加密方法:
按这个半括号向上找,你就可以找到一个完整的加密方法,这个就是整个 fromdata 的加密方法。挺容易找的,如果觉得不方便找的,可以先将这个 js 文件里面的代码复制下来,然后到 Sublime Text 软件上找,这个可以折叠,也比较容易找,找出来是这样子:
格式化之后有 400 多行,而且全是混淆,难看得一批。
为了看看这个正确不正确,我们可以把函数里面的内容直接拿出来,就是去掉最外层的函数,然后调用下面的函数 Q,把我们的 fromdata 传进去:
最后将上面的 JavaScript 给弄成一个 html 文件,放在 script 标签内即可:
格式就和上面一样,然后直接用浏览器上打开这个 html 文件,你会看到这个:
这个就是我们一直努力在找的 fromdata 加密字符串。
弄完这个之后,我们继续使用 Python 来操作了,因为这个加密的方法格式化之后有 400 多行,实在太多,也全都是混淆,如果想用 Python 来实现的话也不是不可能,就是成本太大了,需要的时间太多了,我们还不如直接使用 Python 的 execjs 来执行 JavaScript 代码直接获得就可以了,这个简单方便。
我们除了使用 execjs 来执行,还可以使用 selenium 运行这个html 文件也是可以的,但是我并不想用 selenium 这个工具,还是喜欢折腾,所以忽略了,想用的可以试试。
但是这里又会有一个问题,我们用浏览器打开的是为它提供了一个浏览器的运行环境,我们在 Python 使用的 execjs 提供的是 node 环境,两个环境的不一样,就会产生不同的效果,下面我们可以选择使用 webstorm 编辑器来提供 nodejs 环境来进行尝试以下。
在 node 环境调试加密代码
你可以拿上面的 JavaScript 代码在 webstorm 运行,你就会看到:
TypeError: __g._encrypt is not a function
所以我们需要调试,需要把那些在浏览器上只有的对象,比如 window、navigator 之类的对象给弄掉,从而在 node 上用不用的代码代替相同的效果即可。
要调试我们先要找到代码运行的开端,可以很容易找到:
可以看到,这里它会先去判断有没有 window 这个对象来判断是不是在浏览器上面运行的,所以我们可以直接把它修改成 true 或者其他表示成 true 的值都可以。
再次运行,可以看到这个错误:
ReferenceError: atob is not defined
这个 atob 是将 base64 加密的字符串给解密,在 node 环境下是没有这个方法的,我们需要使用 Buffer.toString()替代即可。
运行之后,还是报这个错误:
TypeError: __g._encrypt is not a function
注意:这个是大坑,估计一般人每个一两天还搞不定,这个是因为上面的解密的,但是上面的并不一样,你可以在上面的两个函数加断点,分别在浏览器和 node 环境下运行,可以看到解密的数据是不一样的,是因为在浏览器上的 base64 加密的是 binary 编码,解密之后也就同样需要使用 binary 编码。这个是我在知乎的资源文件上搜索 atob 这个方法,然后慢慢查找看到的,当时也差不多心态崩了,还好坚持下来了。而我在 node 环境下解密之后使用了默认的编码,所以解密的数据出错了。当我们加上 binary 编码之后,再运行:
这时,错误不再是上面那个了,变成了另一个,证明解密正确了,再来看看下面这个错误:
execption at 11: ReferenceError: window is not defined
原来是 window 对象惹的祸,这个时候就需要我们伪造 window 对象了,至于怎样伪造呢,我们可以调试出错的地方,看看它使用了什么方法,就直接使用适合 node 运行的相同效果的代码代替就可以了,经过多次调试,需要我们伪造 window 和 navigator 这两个对象,下面就是伪造之后的代码:
这个时候再运行看看:
可以看到成功了,上面的红色字是一个提示,关于 Buffer 的,这个我们忽略就行,接下来就可以使用 Python 环境进行测试了。
果然,都出来了,哈哈,注意需要先安装 PyExecJS 库,自行安装,然后在测试的时候记得导入 execjs 即可。现在这个 fromdata 算是大功告成了,接下来就是登陆验证下我们搞得这个加密对不对了。
模拟登陆知乎
这个知乎的登陆也是坑满满,我也给踩了几个,这个就直接说坑吧,其他的就不多说了。
我只是用手机号来登陆的,也可以用邮箱登陆,过程都差不多的。
1. 网址的请求头
请求登陆的网址的请求头需要带上这几个:
不带 content-type 的话,会给你返回这个错误:
Missing argument grant_type
不带 x-zse-83 的话,会给你返回这个错误:
请求参数异常,请升级客户端后重试
至于 agent-user 那就更不需要说了。
2. 请求顺序
知乎这个登陆是首先请求验证码地址,看需要不需要填写验证码,如果需要填就再请求一次,而且还需要再再请求一次查看是否输入验证码正确,不正确就重复上面步骤,当不需要填写验证码的时候就可以直接请求登陆网址了。
还有上面的三次请求的验证码地址的请求方法都是不一样的,哪种方法自行调试即可。
3. 验证验证码
在验证验证码的时候请求头的 content-type 不要填写值为 multipart/form-data,如果填了请求验证码的时候会给你返回这个错误:
{"error":{"message":"Missing argument input_text","code":400}}
它的意思是说没有带上验证码验证,当我们去掉的这个字段的时候,就可以验证了。
验证验证码的时候请求头只需要有一个 user-agent 就可以了。
4. 请求的所有阶段带上 cookie
知乎这个有个 cookie 值是验证码票据,是从第一次请求验证码地址来的,就是下面这个:
如果不带 cookie请求或者请求顺序不一样都有可能给你返回这个错误:
{"error":{"message":"缺少验证码票据","code":120002,"name":"ERR_CAPSION_TICKET_NOT_FOUND"}}
5.XXX
你还想要?没有了,坑暂时只有这么多,最后给你们看下登陆成功的结果:
写在最后
这个登陆折腾了差不多一周了,实际来说可能是三周,因为从刚开始看不懂 js 代码,就跑去学了两周 js,现在总得来说 js 也可以说上手了,以后或许也会使用 nodejs 搞点爬虫,挺好玩的。
如果你上面看不懂不要紧,可以先去学习下 js,推荐大家去廖雪峰的官网看 JavaScript 教程,写得蛮不错的。
至于代码,暂时不公布了,如果你一步一步按照我方法来弄的话估计也可以,前提的有 js 基础最好。
折腾这个,掉了不少头发,但听说转发是生发之道,所以你们懂的!
作者:sergiojune,一个热爱折腾Python的学者。本文为作者投稿,首发于作者个人公众号日常学python,版权归对方所有。
热 文 推 荐
☞ 华为 5G 硬实力
☞ Java 开发者希望未来使用 Python 和 Go
☞“边缘计算将吞掉云计算!”
☞ 18 岁少年盗取价值 90 万元的萌乃币, 交易所被迫关停!
李笑来登顶 GitHub TOP 榜!币圈大佬要教程序员如何自学编程
☞ 马云:蚂蚁金服这样做区块链!
女生适合做程序员吗?
Google首页玩起小游戏,AI作曲让你变身巴赫
曝光!月薪 5 万的程序员面试题:73% 人都做错,你敢试吗?
System.out.println("点个在看吧!");
console.log("点个在看吧!");
print("点个在看吧!");
printf("点个在看吧!\n");
cout << "点个在看吧!" << endl;
Console.WriteLine("点个在看吧!");
Response.Write("点个在看吧!");
alert("点个在看吧!")
echo "点个在看吧!"