首页 > 百科知识 正文

维普期刊JS逆向详细流程(极客算法技巧口诀)

时间:2023-11-19 20:48:11 阅读:892 作者:懂的珍惜

文章来源:https://blog.csdn.net/qq_35491275/article/details/117307069作者:mkdir700本篇文章为作者授权转载

前言

我所用的方法基于浏览器环境的,非硬解(头秃ing),文章较长,建议收藏。

这是我第一次接触瑞数加密,比较难,不过学到的东西也是挺多的,也是因为我第一次解瑞数,所以文章写得比较详细甚至是啰嗦,这篇文章大致是以我逆向的思路去写的,应该适合像我这样从未接触过瑞数的朋友。

这次逆向总结,估计会写 3 到 4 篇文章。

接口签名的生成与获取Cookie 的生成与获取基于浏览器环境的爬虫如何部署?关于本次瑞数解密的总结本文中也会有一些调试技巧夹在其中,如有问题或更好的建议欢迎提出!

本文以维普期刊的高级检索接口作为示例,地址:http://qikan.cqvip.com/Qikan/Search/Advance?from=index

正文开始。

过 debugger

定时器 debugger

拿到网站后,一如既往的直接打开审查工具,在这一步直接被 debugger 卡住了。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第1张

这是一个定时器无限 debugger,如果两次时间差大于 100,那么就会一直让我们处于 debugger 状态。

当遇到这种反调试的手法时,可在进入 debugger 状态后,在 console 中输入以下代码,以此跳过。

for (var i = 1; i < 99999; i )window.clearInterval(i);参考:爬虫漫游指南:瑞数的反调试陷阱

死循环 debugger

过了这一步后,当我回到网页,又会直接进入 debugger 状态。这个 debugger 是在一个判断下,这个比较简单我们直接右键选着**“永不在此处暂停”**。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第2张

**注意:**以上步骤都是在谷歌浏览器中调试的,使用火狐情况会不一致,建议使用谷歌浏览器。

这些 if 判断都是在一个 while(1) 循环内,使用火狐会一直在循环内,谷歌只要设置了不在此处暂停就没事,具体为啥不知道。

分析搜索接口

输入关键词,点击检索。很容易找到请求路径为 SearchList?... 的链接就是数据接口。

查看这个请求,需要搜索参数 searchParamModel 和签名 G5tA5iQ4。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第3张

查看这个请求的调用栈

维普期刊JS逆向详细流程(极客算法技巧口诀)-第4张

这里可以先去看看 window.advSearch 这个方法。这里的 url 与 data 组合后,并不存在这个字段 G5tA5iQ4

所以这个字段的值不是在这里构建,这个字段对应的值就是签名,也是我们必须要解决的。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第5张

先这里提前解释下,为什么这里明明没有设置 G5tA5iQ4 的值,却在请求发送时,含有这个签名。

原因很简单,XMLHttpRequest 的 send 方法被修改了。下图是两者的对比。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第6张

签名在何处生成?

进入第一个函数

维普期刊JS逆向详细流程(极客算法技巧口诀)-第7张

在这里打上断点,再去点击检索按钮,进入调试。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第8张

注意看这个 arguments,这是请求的参数。

由于点击一次检索按钮会有好几个请求,所以这里会有多个请求经过这里。

我们需要调试的是 SearchList 这个接口,所以只要不是 SearchList 的接口参数,直接跳过即可。

结合上文的接口分析,searchParamsModel 就是搜索参数。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第9张

不过,不要忘了。我们的目的是找签名在何处生成。

**小提示:**上文说过 XMLHttpRequest 的 send 方法被修改过,实际上,上图的 _$hp 就是所谓的 send 方法。

此时查看 this

维普期刊JS逆向详细流程(极客算法技巧口诀)-第10张

继续往下找,查看 _$a5,(变量名每次请求都不同,知道是它就行),展开并查看它的第一个作用域。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第11张

可以看到,在此处的 _$q3 的值就是一个带有签名的链接。

_$q3 属于 _$M3,那么就可以按 Ctrl F 搜索关键字 _$M3

注意:_$M3 是一个函数,所以你应该找到是类似下面这样的函数

维普期刊JS逆向详细流程(极客算法技巧口诀)-第12张

我们得知 _$q3 作为参数传进了 _$M3,所以在这个函数内打上断点,重新调试

当我们进来的时候,查看 _$q3,发现 _$q3 只是请求链接,所以由此可知,签名就是在 _$M3 中生成并拼接到 _$q3 上的。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第13张

为了方便调试,我们可以将这里的断点换成条件断点,即只调试 _$q3==="/Search/SearchList" 这种情况。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第14张

将内部函数折叠,可以看到 _M$3 内部只是调用了一下 _$Vq 函数。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第15张

展开 _$Vq 函数,分析代码。在这个 _$Vq 函数里面,多折腾几下总能找到签名是何处生成的。

当然也可以用点小技巧,我们知道签名最终会拼接到 _$q3 上,所以必然存在类似这样的代码:

_$q3 = 或者_$q3

维普期刊JS逆向详细流程(极客算法技巧口诀)-第16张

断点调试到此处,可知签名是调用 _$5Q(_$hp, _$M_, _$No) 得来的。

查看这个函数的三个参数,可知它们分别是 0,大小为 16 的整数数组以及一个 undefined

维普期刊JS逆向详细流程(极客算法技巧口诀)-第17张

在这里,_$M_ 是整数数组,我们可以向上寻找,查看 _$M_ 是如何生成的。

可以找出整数数组是将 /Search/SearchList 作为参数传入某个函数而生成的。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第18张

中场休息

分析到这里,我们知道了签名生成的流程如下:

当用户点击搜索按钮,触发点击事件;构建请求对象(请求对象的参数没有签名关键字);由于 send 方法被修改,所以调用 send 方法时,签名就在这个过程中被生成;签名代码来源分析

其实你应该发现了,分析了这么久的 JS 代码,却不知道这大段 JS 存放在哪里?

像下图这样,JS 来源显示为 VM 数字 的形式,这就说明这些 JS 代码是后来加载进引擎的。

换句话说就是,这些 JS 代码并不是存在一个 JS 文件里的,实际上是通过 eval 函数将一大堆字符串加载进了内存。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第19张

此时就需要寻找以上 JS 代码是如何加载进内存。

这个也是瑞数加密的一大特色,这些加载 JS 代码的代码本身就是被混淆的,并且存在于 Html 页面中。

查看搜索页面源码:view-source:http://qikan.cqvip.com/Qikan/Search/Advance?from=index

**Tips:**如果是加载是空白,那么你需要先正常访问一次搜索页面再查看源代码。这个是 cookie 的原因,具体的生成机制及解决办法会在下一章讲解到。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第20张

这一行代码的后面,就是一堆 JS 代码,我们可以将整个网页代码拷贝至本地编辑器。

将 html 代码格式化后查看。

一开始就加载了一个 JS 文件,为了调试方便,可以将这个 JS 文件下载到本地

http://qikan.cqvip.com/NJDrTcXo8msX/leE4DkIasHMb.f22c526.js

维普期刊JS逆向详细流程(极客算法技巧口诀)-第21张

查看 leE4DkIasHMb.f22c526.js,一堆杂乱的字符,其主要的作用就是为 window.$_ts 赋值。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第22张

html 代码引入 leE4DkIasHMb.f22c526.js 后,紧接着就是一个自调用的被混淆的 JS 代码。

这段代码的核心作用就是将 leE4DkIasHMb.f22c526.js 中的杂乱字符串通过特定方式还原为代码并加载进内存。

此时的主要工作就是找到,杂乱字符串变成规则字符串代码的位置。

为什么要这么做,在这里举个简单的例子。

举例时间到!

现在这样一串字符串, Y29uc29sZS5sb2coJ2hpaGloaWhpLi4uJyk=

这很常见,这是通过 base64 编码后得到的字符串,那么我们可以通过 base64 解码得到本来的字符串,然后使用 eval 函数执行即可。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第23张

以上是原本的功能,打印输出了 hihihihi...

现在我们需要修改这个代码输出的内容,而这可以通过字符串替换方式实现,就像这样。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第24张

那么下面的流程图,我想应该可以理解了。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第25张

上方的例子是因为我们知道编码方式是 base64,所以可以轻松的将密文转为明文。

对于不常见的加密方式,我们就只有去调试找出明文生成的位置,再加上 JS 代码本事就是混淆的,所以难度就有所提升。

举例结束。

签名代码在何处加载到内存?

仔细想一想,一段字符串想以 js 代码的形式加载进内存,必定会使用 eval 方法。

所以,我们只需要找到哪里使用了调用 eval 即可。

搜索关键词 eval,运气很好可以直接搜索到,下面赋值的操作执行即,_$vo 就是 eval

维普期刊JS逆向详细流程(极客算法技巧口诀)-第26张

此时搜索关键词 _$vo,发现有 93 个匹配项

维普期刊JS逆向详细流程(极客算法技巧口诀)-第27张

_$vo 作为函数,如果被调用,那么调用的写法可能是这样 _$vo()

则搜索关键词 _$vo(,匹配项为 0 个。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第28张

函数的调用还有一种 函数名.call 的方式,所以不妨搜索 _$vo.call 试试看。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第29张

找到一个匹配结果,在这里打上断点,调试过来看看传入的参数是什么。

可见,_$kw 的值就是代码字符串了。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第30张

我们在 eval 执行前注入自己的代码即可达成目的。

注入代码

为了大家方便阅读,我将上一步得到的明文代码字符串称为签名代码

虽然这代码还有其他的功能,但对我们来说,只想通过这段代码获取签名,仅此而已。

注入代码是为了让我们可以更方便的获取到签名,最简单的办法就是将签名设置为一个全局变量。

除此之外,为了方便后续调试还可以剔除其中烦人的 debugger

设置签名为全局变量

如果有些忘记了签名是如何生成的,可以先翻到第三节回忆回忆。

由于代码的变量是变化的,所以我们不能直接使用 replace,而是应该用正则匹配的方式去替换或插入代码。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第31张

维普期刊JS逆向详细流程(极客算法技巧口诀)-第32张

以上是两次请求签名生成的代码行。

在变化中找不变,_$f_[5] 和 _$A4[5] 它们都是取索引值 5

所以写出正则如下:

(_$[wd_$]{2}) = _$[wd_$]{2}[5] (_$[wd_$]{2}([^)] );)Tips:这是基于格式化的 js 代码写的正则,实际的签名代码是被压缩的,所以应该把多余的空格删除。

(_$[wd_$]{2}) =_$[wd_$]{2}[5] (_$[wd_$]{2}([^)] );)匹配出这一段后,我们可以在原本的代码后面,再加上一句全局变量赋值。

那么可以写出 JS 代码

签名代码.replace(/(_$[wd$] ) =_$[wd$] [5] (_$[wd$] ([^)] );)/gm, `$1="?" $2window.genUrl=$1;`);剔除 debugger

在最初分析搜索接口时,就遇见了两个 debugger

一个是明文显示的,这个比较简单,使用 签名代码.replace('debugger', '') 剔除即可。

另一个定时器 debugger 则稍稍有点麻烦。

经过多次调式,可以发现整个代码也是在一个 while 循环中跑,这是瑞数的一大特色。

维普期刊JS逆向详细流程(极客算法技巧口诀)-第33张

且 if 语句比较的值是没有变化的,都是变量小于 256,这为我们注入代码提供了方便。

我们以注入的方式,while 内的第一个 if 语句的上方插入以下代码:

console.log(_$Mx)这个 _$Mx 是一直做比较的,通过层层的 if else,最终执行某段代码。

当进入调试工具后,只要进入了此循环,就会打印 _$Mx

而当进入了定时器 debugger,此时循环停止,通过最后输出的数字,就可以找到进入定时器 debugger 的入口

维普期刊JS逆向详细流程(极客算法技巧口诀)-第34张

**Tips:**这一步可能会卡着,稍微等等。

注入后的代码

维普期刊JS逆向详细流程(极客算法技巧口诀)-第35张

进入定时器 debugger 后

维普期刊JS逆向详细流程(极客算法技巧口诀)-第36张

最后的数字是 388,记着这个 388,一路跟着 if 走就可以看到类似如下代码:

维普期刊JS逆向详细流程(极客算法技巧口诀)-第37张

这个代码就是进入定时器 debugger 的入口,那么我们只需要将这行代码注释或者删除即可

签名代码.replace(/(<389){)[^}] /gm, `$1`);至此所有的 debugger 都已去除。

小结

上方的所有代码注入都是在 html源码 上进行的。

这里先理一理我们的流程:

请求搜索页面,获得页面 html 源码python 对 html 源码进行修改将 html 放入浏览器运行调用签名方法获取签名上方的注入是在 html 源码中进行的。实际情况是使用 python 来完成代码注入。

画个图来说明下,即使用 Python 修改 html 源码,使得 html 中的 js 代码能过将目标代码注入到签名代码中

维普期刊JS逆向详细流程(极客算法技巧口诀)-第38张

代码注入示例:

# -*- coding: utf-8 -*-"""Created on 2021/5/24 17:15---------@summary: 注入代码1. 去除定时无限debugger2. 去除死循环debugger3. 插入searchList方法,用于生成签名4. 将得到的签名提升至全局变量,可通过 `genUrl` 访问---------@author: mkdir700@email: mkdir700@gmail.com"""import redefpurify_html():with open("raw.html", Encoding="utf-8") as f: text = f.read() r = re.findall("_$[wd$]{2}(79,_$[wd$] );", text)[0] var_name = re.findall("_$[wd$]{2}(79,(_$[wd$] ));", r)[0] pp = """%(var_name)s = %(var_name)s.replace(/(_$[wd$] ) =_$[wd$] [5] (_$[wd$] ([^)] );)/gm, `$1="?" $2window.genUrl=$1;`);%(var_name)s = %(var_name)s.replace(/(<389){)[^}] /gm, `$1`);%(var_name)s = %(var_name)s.replace("debugger", "");""" % {'var_name': var_name} result = text.replace(r, pp r) result = result.replace("/NJDrTcXo8msX/leE4DkIasHMb.f22c526.js","http://qikan.cqvip.com/NJDrTcXo8msX/leE4DkIasHMb.f22c526.js") result = re.sub(r"(/dist)", "http://qikan.cqvip.com/dist", result) result = result.replace("</body>",'<script>searchList=function(a){$.ajax({url:"/Search/SearchList",type:"post",dataType:"html",data:{searchParamModel:a},beforeSend:function(){loadding()},complete:function(){loaddingClose()},success:function(){console.log("请求成功")},error:function(){loaddingClose()}})};</script></body>')# print(result)with open('pure.html', "w", encoding="utf-8") as f: f.write(result) print("HTML页面代码注入完成")**Tips:**签名的触发机制是发送请求,所以注入了一个 ajax 请求,可供我们手动调用。

签名测试

打开搜索页面右键查看搜索页面源码使用 python 脚本注入代码,生成新的 html 文件在新的 html 文件同目录下,启动简单的 web 服务python-m http.server 9000访问 http://localhost:9000/pure.html打开审查工具,调用 searchList 方法,接着访问 genUrl 变量

维普期刊JS逆向详细流程(极客算法技巧口诀)-第39张

在浏览器中获取 cookie将 cookie 和签名带入,测试请求是否成功,发送请求的脚本如下:# -*- coding: utf-8 -*-"""Created on 2021/5/23 16:38---------@summary: ---------@author: mkdir700@email: mkdir700@gmail.com"""import requestspayload = "searchParamModel={"ObjectType":1,"SearchKeyList":[{"FieldIdentifier":"M","SearchKey":"北大","PreLogicalOperator":"","IsExact":"0"}],"SearchExpression":"","BeginYear":"","EndYear":"","JournalRange":"","DomainRange":"","PageSize":"0","PageNum":"1","Sort":"0","ClusterFilter":"","SType":"","StrIds":"","UpdateTimeType":"","ClusterUseType":"Article","IsNoteHistory":1,"AdvShowTitle":"题名或关键词=北大","ObjectId":"","ObjectSearchType":"0","ChineseEnglishExtend":"0","SynonymExtend":"0","ShowTotalCount":"0","AdvTabGuid":"4361c899-1a1a-2b2b-eefe-cf94a80612f7"}"cookies = input("请键入cookies: ")genUrl = input("请键入genUrl: ")session = requests.Session()session.headers = {"Accept": "text/html, */*; q=0.01","Accept-Encoding": "gzip, deflate, br","Accept-Language": "zh-CN,zh;q=0.9","Cache-Control": "no-cache","Connection": "keep-alive","Content-Type": "application/x-www-form-urlencoded; charset=UTF-8","Cookie": cookies,"Host": "qikan.cqvip.com","Origin": "http://qikan.cqvip.com","Pragma": "no-cache","Referer": "http://qikan.cqvip.com/Qikan/Search/Advance?from=index","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.212 Safari/537.36","X-Requested-With": "XMLHttpRequest",}resp = session.request("POST","http://qikan.cqvip.com/Search/SearchList" genUrl, data=payload)print(resp.url)print(resp.status_code)效果展示

状态码:成功-200,异常-400

维普期刊JS逆向详细流程(极客算法技巧口诀)-第40张

版权声明:该问答观点仅代表作者本人。如有侵犯您版权权利请告知 cpumjj@hotmail.com,我们将尽快删除相关内容。