逆向过程中遭遇加密算法该怎么办
摘自夜幕爬虫团队东哥杰作
如果你身处一线爬虫岗,那你大概率每天都会在逆向过程中遭遇加密算法的阻截。从加密的角度来看,网络请求可以分为四种:
•请求和响应都没有加密;
•请求中有加密,响应没有;
•请求中没加密,响应信息中有加密;
•屮!请求信息和响应信息中都 TM 有加密;
如果你连加密都没见过,那你可得好好看一下。
假设你现在要爬取一个网站上的帖子信息,但是如果没有登录的话你是无法看到 VIP 板块内容的。
作为爬虫工程师,你清楚地知道:现在你需要用代码的方式发送 POST 请求,完成登录操作。
一个网站的请求信息概览如下:
这里以夜幕爬虫安全论坛 bbs.nightteam.com 的登录请求为例,email、password 和 vcode 对应的是注册邮箱、密码和验证码。
password 字段的值为 25f9e794323b453885f5181f1b624d0b,这一看就不是正常人会用的密码。正常人用的密码大概长这样:
•普通人用的密码 acsPO982er
•工具生成的或高端人士的密码 Ikj48-zo23-5jz9
•测试账户或者笨蛋管理员的密码 Admin1688
密码长度通常在 12 位以内,较长的最多到 16 位。几乎没有网站会要求你将密码长度保持在 32 位,遂可以猜测这是编码或加密过的结果值。说到这里,你应该具备一些编码或加密的常识,例如:
•MD5 提取结果通常是 32 位,不受明文长度影响;
•Base64 编码结果末尾通常会出现一或二个等于符号,受明文长度影响;
•一长串无规律数字与字母组合的字符大概率是 AES、DES、SHA 相关加密;
•SHA1 加密结果值为 40 位,不受明文长度影响;
•SHA256 加密结果值为 64 位,不受明文长度影响;
•另外,AES、RSA 等对称和非对称加密都喜欢将结果值用 Base64 进行编码,这样易于传递;
•如果你看到一长串字符里出现 + 号、\ 号和末尾的 = 号,那大概率就是上一行描述的加密算法加密后又进行了 Base64 编码的结果;
•但要注意的是 32 位的字符串不一定是 MD5 摘要结果,64 位的也不一定是 SHA 加密结果。
强烈建议你去看看这些编码或加密算法的文档,了解它们的计算过程。如果你不知道在哪里看,也不知道如何阅读晦涩难懂的 RFC,可以翻开《Python3 反爬虫原理与绕过实战》这本书的第十章。
下面有四段长度不一的字符串,你猜猜它们对应使用了哪些编码或加密手段?
当你遇到可疑的字符串时,你需要找到证据确定你的猜想。你可以通过全局搜索来找关键词,如果搜不到就看调用栈并逐个查看和分析。下图为全局搜索图示:
当你用各种方法找到加密算法的时候,你还需要仔细调试和分析它究竟用了什么方法将原本秀色可餐的字符串换成了面目全非的结果值。
经过一番操作,你找到了类似加密的算法,具体如下图所示:
看起来应该是这段代码对 password 进行了操作,具体代码在 384 行:
这一看就是用信息摘要算法,也就是 MD5 对 password 进行了加密。为了确定你的猜想,验证这段代码确实是你想找的代码,你需要在 385 行打上断点并再次点击页面的登录按钮。登录按钮点击后程序就会运行到那里,并停在断点处:
代码运行状态中,password 的值为一长串的字符。此时还不能完全确定它就是 MD5,虽然文字上写的是 $.md5,但有可能是故意蒙骗你的呢?
这时候你可以在网上搜索在线的 MD5 计算,将相同的输入填写进去,看看得出的结果和代码的是否相同。
如果结果相同,说明 MD5 就是这里用的“加密”算法,反之则要进一步跟进代码。
找到加密的算法后,你有 2 种选择:
•用你熟悉的 JAVA/Python 语言实现相同的输入输出,也就是调用 MD5 算法对密码进行摘要提取;
•在 Node 或 JavaScript 中调用相同的方法实现对密码摘要的提取;
看到这里你肯定会说,那么问题来了:
•什么时候选择第一种?
•什么时候选择第二种?
如果你明确的知道目标的代码逻辑,例如使用 MD5 提取信息摘要或使用 AES 算法对密码或请求正文中的 sign 字段进行构造/加密等,你可以用第一种方法。但如果你不清楚目标的代码逻辑,目标除了使用常见的编码或加密外还实现了自己的一套数据处理规则,且函数之间多层次的相互调用,那么就选择第二种方法。
你可能看到下方图示中贴出的代码逻辑:
文件里有近万行的代码,不仅层层调用,还对变量名、常量名、方法名和参数名进行了混淆,这时候你要想从一个函数入口逐步捋清整个加密算法的逻辑是非常困难的。面对这种情况你可以选择第二种方法,从入口函数开始,将层层调用的方法或变量粘贴到一个 JS 文件中,并根据运行时提示的 xxx not define 将缺少的对象补足(通俗地讲,就是缺啥补啥),直到 JS 文件中的程序能够正常运行并输出你想要的结果。
上面我们以请求信息中出现加密为例,讲解了不同场景的应对流程和可选方法。其实响应信息中出现加密也是相同的思路,假设服务端在响应信息中返回了这么一串字符:
处理方法和上面介绍的相同:
•尝试通过对象名关键字搜索密文处理函数;
•尝试通过对 JSON.stringify 或 Base64.decode 又或者 Base.decrypt 再或者 AES.decrypt 等方法关键词搜索或方法的 hook 找到处理函数;
思考:为什么搜索这些关键词?
找到之后根据情况选择捋清逻辑或者依葫芦画瓢地拼凑 JavaScript 代码,直到能够得到相同的输入输出。
对于常见的编码或加密算法,你应该有一个全面的认识。你需要了解:
•编码或加密算法的产生背景;
•编码或加密算法的协议规范;
•编码或加密算法的正向计算规则;
•编码或加密算法的反向计算规则;
•编码或加密算法在你熟悉的语言中成熟的加密库的使用;
•编码或加密算法在 JavaScript 中的使用和关键词特征;
举个例子,AES 算法有几种模式,例如 CBC、ECB;同时它又有不同的填充方式,例如 PK7、PK5;还需要设定不同长度规格的密钥和向量;如果你不了解这些,那么就算你知道目标使用了 AES 算法,你也没有办法得到相同的输入和输出,也就是无法完成爬虫任务。下方图示为 AES 的 Node 语言示例:
从这里也可以看出,爬虫工程师应该收集、整理这些加密库的使用,这样在工作中遇到的时候可以直接拿来使用,节省一大部分时间。另外,不要做伸手党,因为没有自己写的东西通常是最容易忘记的东西。
下方还有几道思考题在等你,不要错过哦!
指引
•Node 执行 JavaScript 代码的内容在《JavaScript 逆向系列课》第三课——调用 JavaScript 执行代码;
•AES 加密算法详解的内容在《JavaScript 逆向系列课》第十三课——解密!AES并不是每次都奏效;•BASE64 编码和对应的变幻内容在《JavaScript 逆向系列课》第十五课——Base64竟有如此威力;•定位加密参数对应代码位置的方法在《JavaScript 逆向系列课》第七课;
•如果你不熟悉调试和调用栈的查看,请观看《JavaScript 逆向系列课》第四课;
思考题
在你看完推荐和指引的内容后,你肯定对编码或加密算法有新的认识。这里有几道题,你可以思考一下并在课程评论区留下你的答案:
1.如果请求正文中用的“加密”方法是 MD5,那么服务端会怎么处理?客户端除了“加密”字符串之外还需要提交什么?2.如何修改 Base64 算法,达到既保持编码特征(例如 = 号、+ 号)又能够蒙骗(能正常解码,但解码结果和原文肯定对不上号)解码者呢?3.AES 加密有几种填充方式?4.AES 加密的密钥有什么要求?5.服务端只返回了一长串的密文,你应该如何获得明文?6.如果目标使用了可逆的算法,你能联想到的是?7.如果目标使用了不可逆的算法,你能联想到的是?8.行为验证码提交验证的时候,请求正文通常会有哪些字段,字段值是长还是短?
答疑解惑
看课程的同学基础不一,遂出现很多不同程度的问题。这里针对与本主题相关的评论进行解答,与其它主题相关的问题解答会在对应的补充图文里出现,大家继续踊跃发言,大胆批评!
第一个问题
解答:RSA、AES、DES 等加密算法的工作模式、填充方式可以在搜索引擎中找到答案,具体的代码实现也有库和文档。例如 JavaScript 领域的 crypto-js 库的文档 https://cryptojs.gitbook.io/docs/ 中就有各种算法的正向使用和反向使用示例,建议亲手去实现和操作。代码实现部分东哥正在收集整理,后面会导出放到论坛给大家作为参考,但强烈建议自己动手。
第二个问题
解答:crypto-js 是一个 JavaScript 语言实现的加密算法库,包含了常见的编码和加密算法的实现。AES 是对称加密的一种,crypto-js 中也有 AES 算法的实现。Python 领域也有对应的加密算法库,例如 PyCrypto。有了成熟的库,你就可以在确定明文和其它参数的情况下计算出密文,而不用自己从 0 开始编写。
第三个问题
解答:说零基础也能学会大概是从韦世东那里得来的:“没有 JavaScript 基础也没关系,能看懂逻辑就行了,主要还是思路|没有 JavaScript 基础也没关系,我们课程里会讲需要用到的”。这句话是对是错请自行评价,解释只会越描越黑。
练习平台的事情在群公告已有解释,受到疫情影响,此处不重复。
“会的不用看,不会的看了也没用”这句不好解释,说多了也是越描越黑,但我们可以从现象上来看结果:
有同学会一部分 JavaScript 逆向,但在看的过程中收获了有用的知识,他认为对自己有帮助,参考逆向一群的学霸墨华;
有同学不会 JavaScript 逆向,但一边看视频配合着文章,能够用 hook 和查看调用栈的方式快速地找到加密方法的入口、能够用 Node 的方式扣代码并运行出结果,能够用全局搜索的方式定位加密入口,能绕过不同生成方式的 debugger 阻碍,参考韦世东自己;
第四个问题
这个大概率是个人原因了,第六课无限 debugger 从产生背影、影响范围、作用效果等方面进行了描述,同时举了多个例子阐述不同手法生成的无限 debugger 对应的解决方法。我不是课程主讲人,我也是第一次学这个,但我在写这篇文章的时候我都能背出来几个:
1.函数置空法:重写带有 debugger 的函数,将函数体置空,达到消除无限 debugger 的效果;2.设置断点条件:将断点条件设置为 false,这样永远都不会断点在那里;3.替换法:将原 JS 文件下载到本地,修改代码后用 reres 插件替换浏览器自己请求的同名文件,实现消除效果;
学习态度端正问题
我是韦世东,我来跟大家说一下我的学习态度。我自己明确地知道几点:
1.买了并不等于会,不动手学习白买(这跟买书一个道理,很多人买了书又不看);2.学习永远是主动的,不指望看几遍视频能学会,从而成为大佬;3.这不是小学教育,没有人会给我布置作业和检查作业,也没有人催促着我学习,进不进步只跟我一人有关;4.强者之所以强是因为有欲望且刻苦坚持,天才不是你也不是我;5.找不到练手的地方?咸鱼的公众号、蔡老板的公众号、学习使你劝退的公众号、花儿谢了的公众号,哪个号上面不是一大堆实战案例和剖析。考虑到法律纠纷问题,别人不能在课程里直接告诉我去哪里找练手,也不可以告诉我用哪个网站练手,但机灵的我一定能找到;6.大部分人都不会拒绝友好的提问和表现出来的求知欲,遂我可以向很多人请教;7.比我优秀还比我努力的人非常多,我不能落下;8. 假设我哔哔的时间和别人求学的时间相等,那么我收获的就是一顿气愤,而别人收获的却是知识;9.学习完知识点记得整理笔记,我可以用 Typora 或者 SnippetsLab 等软件记录心得和代码。重申一次,我向来不做伸手党,就算问别人要的代码也会自己动手调试,并且进行优化和注释整理;以下是我的 SnipptesLab 笔记截图:
我如此勤奋的原因是我知道自己菜,我想进步,想要成为别人口中的“大佬”。遂我刻苦努力学习,不懂就问。找不到答案就请教,缺哪方面知识就补!大家看看平时我是如何主动问别人的: