【爬虫】网站反debugger、内存爆破以及网站直接限制开发者工具
声明:本文中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
常见情形
目前网站反爬手段有很多,比如:网站反debugger、内存爆破、限制开发者工具等
情况一:debugger(右键不在此停止)
- 问题: 有部分网站做了反debugger处理,当我们打开开发者工具的时候会直接断点停在某个位置。
- 解决办法:右键点击,选择永不在此停止。然后放过断点,即可继续调试。
前提:该debugger构造函数需要没有内容。如果网站开发者自定义的debugger构造函数后面有内容,是在写入数据的话(不断写入空列表、其他字符等),我们跳过断点会导致持续写入,导致内存溢出,页面卡死。
情况二:内存爆破
绝大多数网站反debugger会更加高深,会使用到内存爆破以及混淆技术。导致我们通过简单的
右键不在此停止
无法破解。
我们使用右键不在此停止,并且跳过断点后,会导致浏览器卡死。
情况三:右键被管理员禁用
部分网站会直接限制我们使用开发者工具,我们右键或者F12打开均无效,都被禁用。
我们想右键,打开开发者工具时,发现被禁用。
情况四:监测函数执行时间
我们通过快捷键强制打开开发者工具,发现页面提示非法调试。
我们进入debug断点分析,点击单步执行,查看页面js逻辑。
最终发现页面是通过判断当前window大小以及函数运行时间来确认是否存在非法调试。
解决办法
方法一:控制台-右键不再此停止
如果我们打开开发者工具后,发现自动进入断点。
- 首先尝试,鼠标右击
永不在此停止
- 放过该断点,即可继续调试
方法二:本地注入JS,覆盖之前老的JS
如果方法一无效:放过断点后会导致浏览器页面卡死。即可使用本地注入JS方法,将debugger构造器置空,覆盖之前的JS。
- 分析浏览器卡死原因:网站开发者自定义的debugger构造器不为空,在持续写入数据。
- 本地注入JS不能刷新网站,否则会导致我们本地注入的JS失效。
分析原因:浏览器卡死原因
我们单步执行断点,发现进入了VM虚拟环境,并且该JS是进行了混淆的。
下面这种有特殊字符+数字+字母的,基本就是进行过JS混淆的:
对于这种混淆过的JS代码,我们需要对其进行还原:
还原方法:复制JS代码,然后在控制台执行即可
//我们需要进行分段还原,否则一次性全部执行,会报not a function
[s(0, -259, -213, 0, "I#ue") + f(1271, 974, 1135, "e!3a") + "r"](r[f(1221, 1495, 1373, "v&9u")](r[g(1467, 0, 0, "&UOm")], r[n(0, "ujyL", -6)]))[s(0, 13, 12, 0, "7Iko")](r[g(1540, 0, 0, "nX(R")]);
还原后JS代码:‘constructor’ apply stateObject => 可以推测出,该自定义构造函数在初始化时,还做了加载某个对象的操作。及时我们右键点击了不再此停止,放过断点,但是依然会有源源不断的对象进行加载,最后导致浏览器卡死。
解决办法:本地注入JS
我们已经定位到了浏览器卡死是因为自定义的debugger构造器不断注入对象导致,那么我们就可以本地执行代码,替换该JS片段,让debugger构造器返回空,无法实现注入对象。
- 新建代码片段
// 将debugger构造函数替换置空,防止内存爆破
// 1. after_debugger_handle 接收我们的构造器
// 2. 判断构造器如果构造了一个debugger的东西,我们就将其置空
after_debugger_handle = Function.prototype.constructor;
Function.prototype.constructor = function(a){
if (a == "debugger") {
return function(){};
}
return after_debugger_handle(a);
};
- 点击下方的执行按钮,执行代码片段
控制台未报错,表明注入成功,此时我们放过断点,发现浏览器不再卡死,即可继续调试。
注意:
- 注入后不能刷新页面,否则页面重新加载会导致之前的注入失效。
- 注入需要等网页数据全部加载完成后再进行,否则可能会报注入失败,JS函数找不到。
拓展:JS混淆(对网页的JS代码进行处理加密)
概念
网站开发者为了防止我们对网站进行调试或者逆向分析,会对JS代码进行处理、保护。
- 实际就是对JS代码进行编码、加密处理等。
JS压缩混淆:删除无用空白、缩短变量名
JavaScript 压缩混淆主要通过删除无用的空白字符、注释、缩短变量名等方式减小代码体积。压缩混淆不仅可以减少文件大小,还能在一定程度上增加代码阅读难度。
OB混淆:插入不透明谓词(函数逻辑)
OB 混淆(Opaque Predicate Obfuscation)是一种复杂的混淆技术,通过插入不透明谓词来掩盖代码的真实逻辑,使逆向工程师难以理解代码的实际功能。
原始代码:
if (x > 10) {
console.log("x is greater than 10");
}
OB混淆后:
function isTrue() {
return Math.random() > 0.5; // 不透明谓词
}
if (isTrue() || x > 10) {
console.log("x is greater than 10");
}
在这个示例中,isTrue 函数的返回值是随机的,不透明谓词使得控制流变得更加难以预测。逆向工程师必须理解 isTrue 函数的实现才能准确分析代码逻辑。
变量混淆:重命名函数方法,改为a、b、c等
变量混淆通过重命名变量和函数名来增加代码的阅读难度,使得代码更难以理解。混淆后的变量名通常是无意义的短字符,如 a、b、c 等。
字符串混淆:对字符串进行编码和转换
字符串混淆通过对字符串进行编码和转换,使得字符串的真实内容难以直接查看。常用的字符串混淆技术包括 Base64 编码和字符替换。
属性加密:对部分字段,如:用户名、年龄等进行加密
属性加密通过对对象属性进行加密,保护对象的内部数据。常用的加密方法包括简单的 XOR 操作和更复杂的加密算法。
控制流平坦化:引入虚拟机+状态机,加大代码逻辑复杂度
控制流平坦化通过重构代码的控制流,使得代码逻辑更加复杂和难以理解。平坦化技术常用于保护程序的执行流程。控制流平坦化通过引入虚拟机和状态机来重构代码的控制流。
案例:原始的条件语句被转换为一个状态机,控制流被重构成一系列状态和转换。这种方法使得代码的控制流更加复杂,增加了理解和分析的难度。
原始代码:
if (x > 10) {
console.log("x is greater than 10");
} else {
console.log("x is 10 or less");
}
控制流平坦化处理后代码:
const states = [0, 1];
let state = 0;
function execute() {
switch (state) {
case 0:
if (x > 10) {
state = 2;
return;
} else {
state = 3;
return;
}
case 1:
console.log("x is greater than 10");
state = 4;
return;
case 2:
console.log("x is 10 or less");
state = 4;
return;
case 4:
// end state
return;
}
}
while (state !== 4) {
execute();
}
案例
可以看到下方对有一串字母以及数字,这种情况一般就是JS混淆,让我们无法知晓他JS源代码是什么样子。
解决办法:
- 如果该网站使用的是开源的JS混淆代码,我们可以通过工具进行还原。
- 如果网站使用的是自定义的混淆逻辑,我们只有手动通过控制台进行处理还原(
直接复制混淆代码,然后放入控制台,回车即可进行还原。
)
参考文章:https://blog.csdn.net/weixin_52392194/article/details/141159872