如果本文有任何问题错误,欢迎评论指正!
前面文章:
爬虫(js逆向)网络基础协议与抓包原理-chrome开发工具-fiddler抓包-重放攻击(1)
https://blog.csdn.net/weixin_44238683/article/details/118468491
爬虫(js逆向)js基础-函数进阶-原型链(prototype、proto、构造函数-this绑定对象(2)
https://blog.csdn.net/weixin_44238683/article/details/118503753
爬虫(js逆向)非指纹built-in函数-js进阶-混淆与伪代码-常见反爬措施-爬虫逆向方法论-(3)
https://blog.csdn.net/weixin_44238683/article/details/118530864
一、常见反爬措施
对于第三篇文章的补充
1、刷新cookie
同一个接口,第一次请求,没有携带cookie,会存在看不到js,第二次会重定向请求才能可能到数据
解决方法:利用抓包工具,session请求,生成cookie措施
2、ajax请求
如何确定是ajax请求,可以通过观察页面某些元素,比如标题,点击相关按钮,页面部分刷新,可以确定是ajax请求
- 发现规律
3、什么是node.js
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
借助node.js 可以通过python代码实现调用js
4、python执行js代码原理
python如何运行js代码,需要借助node.js,需要借助第三方库pyexcejs
5. 构造器的深入理解,如何从根源上修改隐式 new Function
a = function(){}
Function.prototype.constructor = function(){}
二、调试干扰
无限debugger,并不是真正意义上的
死循环debugger,会让整个页面卡死,要么在业务代码前,要么业务代码之后
无限debugger,还有一个非常靓丽的名字,叫debugger地狱
所以无限debugger真正,在业务操作的时候,只是实现很频繁的debugger,而不是真正的死循环
因此安全实现一个debugger是用setTimeout()或者setInerval,但是这个循环一定时有次数限制的
1、debugger实现方法
下面这个网址就存在debugger,出不去
http://cpquery.cnipa.gov.cn/index.jsp
1.1 html或console实现debugger
console实现
function a(){debugger;}
html实现
1.2 eval实现debugger
实际上,通过eval实现debugger,是创建一个虚拟机,操作成本提高,但是无论怎么变化,都有debugger关键字
1.3 通过function实现debugger
Function('debugger').call()
Function('debugger').apply()
a = Function('debugger').bind()
a
1.4 函数与匿名函数实现debugger
函数
function a(){}
a.constructor('debugger').call()
匿名函数,这些都是在匿名函数堆栈没找不到
Function.constructor('debugger').call()
setInterval(function(){debugger;},9000)
1.5 高度混淆debugger
可以通过相关参数替换,类似jsfuck(前面文章提及)
+!+[]
1
a = 'deerere'
"abcd"
eval(a[+[]] + 'ebugger') //相当于 eval('debugger')
可以明显看到是由个数组 拼接成debugger单词
2、处理debugger
无限debugger的处理实际上很简单,有以下几种情况
-
无限debugger贯穿全局
干掉定时器等全局事件(置空或重写) -
无限debugger在加密逻辑之前
详细讨论 -
无限debugger在加密逻辑之后
不用管,script/第一行断点打上,从头开始
2.1 无限debugger贯穿全局
2.1.1 重写定时器
业务代码之前,创建一个debugger
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
function a() {
debugger;
};
setInterval(a, 500);
alert('1111')
</script>
</body>
</html>
可以发现,进入了debugger状态,到达不了业务代码
因为业务代码在debugger之后(debugger在业务之后也可以),如果将setInterval定时器重写,那么是没有影响的,在console执行下面方法就可以跳出无限debugger
这种方式相当于hook(hook是能被检测到的)
setInterval_back = setInterval
setInterval = function(a,b){
if(a.toString().indexOf('debugger') != -1){ //证明有无限debugger
return setInterval_back(a,b)
}
}
2.1.2 置空定时器
置空前提是,业务代码没有涉及定时器用法
setInterval =function(){}
2.2 无限debugger在加密逻辑之后
业务代码已经执行完毕了,debugger没有意义,如果想知道业务代码具体生成,那么在业务代码之前打上断点即可
2.3 无限debugger在加密逻辑之前
1. 业务代码没有涉及setinterval定时器置空或者修改
跟2.1节一样逻辑
2. 添加条件断点,永远不再这停
- 针对debugger没有创建新的虚拟机好用
http://cpquery.cnipa.gov.cn/index.jsp
3. 利用fiddler autoresponse或者Overrides删除debugger
存在弊端,定位位置不好找
添加映射规则,比如,在一些网站,专门编写了一个debugger.js文件,利用fiddler添加规则到此文件,在本地自定义了一个空的debugger.js文件,这样就可以过了,同样devtool工具中overrides也是可以的
这样的话就不会读浏览器缓存,而是读本地的js文件
如何添加规则,使用方式见:https://blog.csdn.net/weixin_44238683/article/details/118468491 第一篇文章
2.4 针对真动态文件或Autoresponse失效或删掉debugger逻辑很繁琐的情况下
- 如果是 Function 原理的 debugger,可以重写 函数构造器
- 如果是eval型的构造器,可以重构 eval函数
- 如果是定时器,并且2失效了,可以重构定时器
- 在以上方式都失效时,向上找堆栈,在进入无限debugger之前打上断点将触发无限debugger的函数置空(最麻烦,但是适用性最广)
1.(过Fcuntion debugger)利用hook,游猴脚本,注入进去
Fcuntion.prototype.constructor_bc = Function.prototype.constructor
Function.prototype.constructor = function(){
if(arguments[0] === 'debugger'){}
else{
return Function.prototype.constuctor_bc.apply(this,arguments)
}
}
2.eval型
eval_bc = eval
eval =function(a){
if (a===='debugger;a=asdasdasdas')
return eval_bc(a)
}
3.定时器
置空或者重写,前面提及
4.调用堆栈
- 以上都失效的话,调用堆栈
- 最麻烦,但是适用性最广,置空
打上断点一步步调试,找到debugger
三、调试检测
1.F12无法打开开发者工具
手动打开开发者工具,火狐浏览器,等换个浏览器
2.检测到打开开发工具,出现js干扰
js调试,一定需要在函数定义之后,即需要调试的js特定加载完成后,才进行重写调试函数,否则js都没有定义,提前操作没有任何意义
-
利用script断点并且先尝试进行关键词搜索找线索。
-
随机打上断点,不断缩小检测范围,直到找到。
-
若是静态js/假动态直接可以Autoresponse干掉。
-
若是真动态则在执行控制台检测逻辑附近的时候重置函数
(这个可以参考无限debugger处理方案,重写函数,hook关键位置等)
干扰:出现鼠标禁止,跳转上一页,播放音乐,出现提示,内存爆破,等等一系列骚操作
2.1 跳转上一页
调试过程当中,按F9下一步,发现js文件,一直会回到上一页的js文件,可以搜索关键词,
url
、open
、goto
、等等,并且记住上一页的url名称,跳转上一页,那么一定会方法指向这个url,
因此搜索上一页的url,或者出现跳转后的url也是可以的
2.2 控制台检测原理
利用了console
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var x = document.createElement('div');
//绑定了一个 id属性
Object.defineProperty(x, 'id', {
get:function() {
alert('1111')
}
});
function use() {
console.log(x);
console.log('禁止调试!!!!!!!')
}
setInterval(use, 200);
</script>
</body>
</html>
可以看到控制台疯狂输出禁止!!!,还有生成console.log(x)创建div标签
2.3 内存爆破
可以类似,上面的样式,检测到调试等,疯狂的在js文件,或者html中进行读写,cookie重写追加,字节追加,导致内存不足够,卡死
内存爆破,指js通过死循环/频繁操作数据库(包括cookie)/频繁调取history等方式,使浏览器崩溃的一种反调试手段。关键词:频繁
还有一种特性情况:js文件很大,电脑内存不足(这种情况在调试层面几乎无解)
特点:
- 正常运行时,一切正常
- 调试时利用时间差,调试特点等讲控制流推入循环
- 利用正则/toString() 判断代码是否进行格式化
- 利用浏览器指纹判断是否为浏览器环境
2.4 OB混淆,正则匹配,判断tostring()长度变化,出现的内存爆破
先了解js中正则的test()方法
test() 方法用于检测一个字符串是否匹配某个模式.
返回一个 Boolean 值,它指出在被查找的字符串中是否匹配给出的正则表达式。 regexp.test(str)
参数
regexp 必选项。包含正则表达式模式或可用标志的正则表达式对象。 str 必选项。要在其上测试查找的字符串。 说明 test
方法检查字符串是否与给出的正则表达式模式相匹配,如果是则返回 true,否则就返回 false
。
tostring()检测原理,如果调用了控制台console,那么会改变tostring()后的长度,定义了一个a函数,通过toString()方法改变了原有的长度,如果将代码拉到本地,格式化等操作。一定会出现下面变化,内存爆破可以检测这一点
st9
案例:
一般来说在,在js文件中看到了如下图格式的样式,一般为OB混淆,利用正则RegExp
关键词搜索
那么搜索RegExp
2.5 如何处理内存爆破
针对2.4节,可以将函数逻辑作为正则取反,实际上内存爆破没有通解,需要具体情况具体分析
寻找蛛丝马迹
四、JS逆向-调试实战
bs12、13
st1、2
案例1: ajax请求参数加密
- 一般来说,先尝试全局搜索,如果是(sign,afsdfsd一些辨识度高的适用)
- XHR搜索(需要开启XHR fetch)
- js文件内搜索(堆栈调试)
点击下一页,发现m出现变化
全局搜索m,可以发现太多m了,无法分辨
可以看到m的生成方式,产生了,是通过base64加密
打上断点,翻页尝试,并在控制台输出,与第一幅图一致
实际上还要校验,是否base64加密方法是否被魔改过
随便找个base65工具验证,发现一致
代码实现略~~~
案例2: cookie刷新变化
bs13
第一次请求2,3页
间隔一段时间,发现cookie变化,这个时候,就需要逆向cookie生成方式
抓包工具
在控制台复制打印,所以就是一个拼接过程
代码实现:
直接访问,没有携带cookie,是得不到数据的,通过抓包工具,是先请求得到一个有效cookie之后,二次请求才能得到值
因此处理第一次请求返回的参数,生成cookie
携带cookie访问,可以看到得到数据
案例3: 请求头headers加密参数
下图是分别翻页后,有个safe
参数变化
因此采用全局搜索的方式,相对前面的ajax
请求m
参数,safe
这个单词具有辨识度
可以看到,设置请求头headers
,这个参数来自tokens
,并且这个参数是通过 base64
加密a
字符串(’9622
’),并且再转md5
,显而易见
(其中要注意,md5,base64加密方法是否被魔改,要进行验证)
案例4:hook技术
下一篇文章撰写~~!!