2024最新版JavaScript逆向爬虫教程-------基础篇之无限debugger的原理与绕过

目录

  • 一、无限debugger的原理与绕过
    • 1.1 案例介绍
    • 1.2 实现原理
    • 1.3 绕过debugger方法
      • 1.3.1 禁用所有断点
      • 1.3.2 禁用局部断点
      • 1.3.3 替换文件
      • 1.3.4 函数置空与hook
  • 二、补充
    • 2.1 改写JavaScript文件
    • 2.2 浏览器开发者工具中出现的VM开头的JS文件是什么?

一、无限debugger的原理与绕过

debugger 是 JavaScript 中定义的一个专门用于断点调试的关键字,只要遇到它,JavaScript 的执行便会在此处中断,进入调试模式。有了 debugger 这个关键字,我们就可以非常方便地对 JavaScript 代码进行调试,比如使用 JavaScript Hook 时,我们可以加入 debugger 关键字,使其在关键的位置停下来,以便查找逆向突破口。但有时候 debugger 会被网站开发者利用,使其成为阻挠我们正常调试的拦路虎。本节中,我们介绍一个案例来绕过无限 debugger。

1.1 案例介绍

案例介绍:我们先看一个案例,网址是 http://shanzhi.spbeen.com/,打开这个网站,一般操作和之前的网站没有什么不同。但是,一旦我们打开开发者工具,就发现它立即进入了断点模式,如下图所示:

在这里插入图片描述
我们既没有设置任何断点,也没有执行任何额外的脚本,它就直接进入了断点模式。这时候我们可以点击 Resume script execution(恢复脚本执行)按钮,尝试跳过这个断点继续执行,如下图所示:

请添加图片描述
然而不管我们点击多少次按钮,它仍然一次次地进入断点模式,无限循环下去,我们称这样的情况为无限 debugger。怎么办呢?似乎无法正常添加断点调试了,有什么解决办法吗? 办法当然是有的,本节中我们就来总结一下无限 debugger 的应对方案,在后面部分实战的案例中我们也会遇到无限 debugger。

1.2 实现原理

首先要做的是找到无限 debugger 的源头,上面的案例通过堆栈回溯,查看 debugger 是如何生成的,如下图所示:
在这里插入图片描述
继续往上进行追溯,如下图所示:
在这里插入图片描述
这时点击左下角的格式化按钮:

setInterval(()=>{(function(a) {return (function(a) {return (Function('Function(arguments[0]+"' + a + '")()'))})(a)})('bugger')('de', 0, 0, (0,0));
}
, 1000);

利用 Function 产生 debugger,然后通过 setInterval 循环,每秒执行1次产生 debugger 语句的操作。当然,还有很多类似的实现,比如无限 for 循环、无限 while 循环、无限递归调用等,它们都可以实现这样的效果,原理大同小异。ps:从某种意义上来说,无限 debugger 不会真正的死循环(只不过这个执行次数多到我们本身靠手点难以接受罢了),而是有规律得执行逻辑,一般用定时器。 无限 debugger 产生小结:

  1. 一定会先产生 debugger 关键字,产生 debugger 关键字,可以是明文也可以混淆。

    // ① 明文 直接书写完整的 debugger
    debugger;
    // ② 可以混淆(可轻度混淆) 即eval配合 debugger
    eval('debug' + 'ger;')
    // ③ 可以重度混淆
    // 结合constructor,debugger,call,apply,action 等关键字进行混淆,增加调试的困难
    Function('debugger').call()
    Function('debugger').apply()
    Function('debugger').bind()
    Function.constructor('debugger').call('action')
    funObj.constructor('debugger').call('action')
    (function(){return !![];}['constructor']('debugger')['call']('action'))
    eval('(function (){}["constructor"]("debugger")["call"]("action"));')
    //总结:这些debugger方法,是实现debugger的基础,可以理解为是三元素。基于三种元素,可以形成多种多样的玩法
    
  2. 结合循环,循环的方式可以是:while/for 循环、包含 debugger 的函数调用自身、方法间的循环调用、计时器(setInterval)

1.3 绕过debugger方法

因为 debugger 其实就是对应的一个断点,它相当于用代码显示地声明了一个断点,要解除它,我们只需要禁用这个断点就好了。

1.3.1 禁用所有断点

全局禁用开关位于 Sources 面板的右上角,叫做 Deactivate breakpoints,如下图所示:
在这里插入图片描述
点击它,该按钮会被激活,变成蓝色,如下图所示:
在这里插入图片描述
这个时候我们再重新点击一下 Resume script execution(恢复脚本执行)按钮,跳过当前断点,页面就不会再进入到无限 debugger 的状态了。但是这种全局禁用其实并不是一个好的方案,因为禁用之后我们也无法在其他位置增加断点进行调试了,所有的断点都失效了!

ps: 解决无限 debugger 名词解释应该为在没有 debugger 干扰的情况下调试,而不是放弃所有的 debugger 调试(也就是说我们自己的调试还得能正常使用),所以此种方式基本不用。

1.3.2 禁用局部断点

取消刚才的 Deactivate breakpoints 模式,页面会重新进入无限 debugger 模式,尝试使用另一种方法来跳过这个无限 debugger。在 debugger 语句所在的行的行号上单击鼠标右键,此时会出现一个快捷菜单,如下图所示:
在这里插入图片描述
这里有一个 Never pause here 选项,意思是从不在此处暂停。选择这个选项,于是页面变成如下图所示的样子:

在这里插入图片描述
当前断点显示为橙色,并且断点前面多了一个 ? 符号,同时 Breakpoints 也出现了刚才添加的断点位置,这时再次点击 Resume script execution(恢复脚本执行)按钮,就可以发现我们不会再进入无限 debugger 模式了。当然,我们也可以选择另外一个选项 Add conditional breakpoint,如下图所示:

这个模式更加高级,我们可以设置进入断点的条件,比如在调试过程中,期望某个变量的值大于某个具体的值的时候才停下来。但在本案例中,由于这里是无限循环,我们没有什么具体的变量可以作为判定依据,因此可以直接写一个简单的表达式来控制。选择 Add conditional breakpoint 选项,直接填入 false 然后回车即可,如下图所示:

在这里插入图片描述

1.3.3 替换文件

利用 Overrides 面板我们可以将远程的 JavaScript 文件替换成本地的 JavaScript 文件,这里我们依然可以使用这个方法来对文件进行替换,替换成什么呢?很简单,我们只需要在新的文件里把 debugger 这个关键字删除。我们将当前的 JavaScript 文件复制到文本编辑器中,删除或者直接注释掉 debugger 这个关键字,修改如下:

setInterval(()=>{(function(a) {return (function(a) {return (Function('Function(arguments[0]+"' + a + '")()'))})(a)}// 直接把参数置空,当然这里也可以把整个文件替换掉(没啥业务逻辑),或者去掉setInterval等都可以)('')('', 0, 0, (0,0));
}
, 1000);

打开 Sources 面板下的 Overrides 面板,将修改后的完整 JavaScript 文件复制进去。替换完成之后,重新刷新网页,这时候发现不会进入无限 debugger 模式了。如果该操作不熟悉,可以参照下面的 3.1 改写JavaScript文件。

1.3.4 函数置空与hook

ps:一定要在 debugger 进入之前。

无限 debugger 产生的原因是定时器造成的,所以我们可以重写这个函数,使无限 debugger 失效:

// 这里是业务代码和setInterval无关,所以直接置空即可
setInterval = function(){} //定时器置空,置空之后上面的无限debugger消失
function xxx(){} //执行函数置空 xxx

② hook:不同的情况书写不同的 hook 代码,即 hook 不同的函数即可,这里我只以 setInterval 为例,其他类似。

_setInterval = setInterval
setInterval = function (a,b) {if(a.toString().indexOf('debugger') == -1){return function(){}}else{_setInterval(a,b)}
}Function.prototype.toString = function () {return `function ${this.name}() { [native code] }`
}

小结:

  1. 优先尝试禁用局部断点,即 Never pause here (最方便快捷,但是最卡–深有体会,也最容出问题)

  2. 次优先尝试重写调用函数,缺陷:容易破坏业务逻辑,导致控制流变化。如:

    Function = function(){}
    setInterval = function(){}
    
  3. 文件替换。缺陷:操作稍微有一点点的麻烦,对动态情况的支持不太好,也可能会改变控制流走向

  4. 万一以上三个都难受了怎么办? 别逆向了,反调试都那么难了,那加密不得难上天啊。放弃吧,嗷~

二、补充

2.1 改写JavaScript文件

我们知道,一个网页里面的 JavaScript 是从对应服务器上下载下来并在浏览器执行的。有时候,我们可能想要在调试的过程中对 JavaScript 做一些更改,比如说有以下需求:

  1. 发现 JavaScript 文件中包含很多阻挠调试的代码或者无效代码、干扰代码,想要将其删除(如上面的无限 debugger)。
  2. 调试到某处,想要加一行 console.log 输出一些内容,以便观察某个变量或方法在页面加载过程中的调用情况。在某些情况下,这种方法比打断点调试更方便。
  3. 调试过程遇到某个局部变量或方法,想要把它赋值给 window 对象以便全局可以访问或调用。
  4. 在调试的时候,得到的某个变量中可能包含一些关键的结果,想要加一些逻辑将这些结果转发到对应的目标服务器。

这时候我们可以试着在 Sources 面板中对 JavaScript 进行更改,但这种更改并不能长久生效,一旦刷新页面,更改就全都没有了。比如我们在 JavaScript 文件中写入一行 JavaScript 代码,然后保存,如下图所示:

在这里插入图片描述

注意:点击了左下角的格式化按钮后,不能向格式化的文件中添加内容。

这时候我们可以发现 JavaScript 文件名左侧上出现了一个警告标志,提示我们做的更改是不会保存的。这时候重新刷新一下页面,再看一下更改的这个文件,如下图所示:

有什么方法可以修改呢?其实有一些浏览器插件可以实现,比如:ReRes。在插件中,我们可以添加自定义的 JavaScript 文件,并配置 URL 映射规则,这样浏览器在加载某个在线 JavaScript 文件的时候就可以将内容替换成自定义的 JavaScript 文件了。另外,还有一些代理服务器也可以实现,比如 Charles、Fiddler,借助它们可以加载 JavaScript 文件时修改对应 URL 的响应内容,以实现对 JavaScript 文件的修改。其实浏览器的开发者工具已经原生支持这个功能了,即浏览器的 Overrides 功能,它在 Sources 面板左侧,如下图所示:

我们可以在 Overrides 面板上选定一个本地的文件夹,用于保存需要更改的 JavaScript 文件,下面来实际操作一下。切到 Overrides 面板,点击 + 按钮,如下图所示:

这时候浏览器会提示我们选择一个本地文件夹,用于存储要替换的 JavaScript 文件。这里我选定了一个新建的文件夹:FunddbOverrides,注意这时候可能会遇到下图所示的提示,如果没有问题,直接点击 允许 即可。

在这里插入图片描述

这时,在 Overrides 面板下就多了 FunddbOverrides 文件夹,用于存储所有我们想要更改的 JavaScript 文件,如下图所示:

我们可以看到,现在所在的 JavaScript 选项卡是 app.d0a16ab3b7972174cc88.js:formatted,代码已经被格式化了。因为格式化后的代码是无法直接在浏览器中修改的,所以为了方便,我把格式化后的文件复制到了 Notepad++ 中,然后把 window.eval 这行代码注释了,如下图所示:

在这里插入图片描述

接着把修改后的内容替换到原来的 JavaScript 文件中。这里要注意,要切换到 app.d0a16ab3b7972174cc88.js 文件才能修改,直接替换 JavaScript 文件的所有内容即可,如下图所示:

在这里插入图片描述

替换完毕之后 ctrl + s 保存,这时候再切换回 Overrides 面板,就可以发现成功生成了新的 JavaScript 文件,它用于替换原有的 JavaScript 文件,如下图所示:

在这里插入图片描述

替换完成之后,重新刷新网页,正如我们所料,这时候不会再进入无限 debugger 模式了,证明改写 JavaScript 成功!而且刷新页面也不会丢失了,除了注释掉干扰代码外,在一些场景下,我们还可以增加一些 JavaScript 逻辑,比如直接将某个变量的结果通过 API 发送到远程服务器,并通过服务器将数据保存下来,也就完成了直接拦截 Ajax请求并保存数据的过程了,修改 JavaScript 文件有很多用途,此方案可以为我们进行 JavaScript 逆向带来极大便利。

2.2 浏览器开发者工具中出现的VM开头的JS文件是什么?

在 Chrome 的开发者工具中,你可能会看到一些以 VM 开头的 JavaScript 文件(如 VM1057)。
在这里插入图片描述
VM 表示的是 Virtual Machine(虚拟机),这些文件通常表示由浏览器生成和执行的虚拟机脚本环境中的临时脚本。这些脚本并不是项目源代码的一部分,也不是实际存在的物理文件,它们在浏览器的内存中创建并执行。 比如说,当你在调试一个网页时,如果在某些动态生成并执行的 JS 代码上设定了断点,Chrome 调试器会在一个以 VM 开头的文件中显示这些代码,例如 VM1057。这个 VM 文件的存在只是为了调试目的,它并不存在于服务器端,也不会被存储在本地,而是存在于浏览器内存中。一般情况下,这类文件的出现是因为浏览器对 JavaScript 代码的处理方式,如动态编译或者 JavaScript 堆栈跟踪。出现的原因:

  1. 动态执行的 JavaScript 代码。比如通过 eval 函数或者 new Function 方法,Chrome 浏览器会创建一个 VM 文件来展示这段临时执行的代码。比如某个网页因为反爬虫,动态生成了 debugger,这些断点并没有直接写在服务器上的原始 JavaScript 文件中,而是在某些 JavaScript 代码的执行过程中被生成,并因此触发 debugger。这些代码也会在执行时被浏览器视为临时的 VM 脚本,并在执行到 debugger 时暂停执行,从而造成所谓的 无限 debugger 循环
  2. 来自执行栈的代码。有时候,当 JavaScript 引擎处理异步操作(例如 Promise、setTimeout 等)中的错误时,错误堆栈可能包含到 VM 脚本的引用,这是因为内部错误回调函数是在虚拟环境中执行的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/8916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

520送男士内裤给男朋友好吗?五大男士内裤测评种草

相信有很多朋友都选在520这个特殊的日子里为心爱的人挑选一份特别的礼物吧!如果送礼给男朋友或老公,一份实用的礼物肯定是最佳选择哦!很多男性朋友每条内裤都穿很久,如果给男朋友挑选合适的男士内裤,也是一种关心体贴的…

[华为OD]BFS C卷 200 智能驾驶

题目: 有一辆汽车需要从m*n的地图的左上角(起点)开往地图的右下角(终点),去往每一个地区都需 要消耗一定的油量,加油站可进行加油 请你计算汽车确保从起点到达终点时所需的最少初始油量说明: (1)智能汽车可以上下左右四个方向…

C++ 复习2 输入输出 基本数据类型

输入输出 标准输出流 ( cout ) cout 代表标准输出流&#xff0c;通常用于向屏幕输出数据。 使用操作符 << &#xff08;插入操作符&#xff09;向 cout 发送数据。 例如&#xff0c; std::cout << "Hello, world!" << std::endl; 会在屏幕上打印 …

本地搭建AI环境

本地搭建AI 这几天刚刚看到好兄弟分享的一段关于本地搭建AI的短视频&#xff0c;于是我按照视频里的讲解&#xff0c;进行了实践。感觉非常棒&#xff01;&#xff01;&#xff0c;马上整理成文字与大家分享一下。 在本地启动并运行大型语言模型&#xff0c;运行llama3、phi3…

自然语言处理(NLP)技术有哪些运用?

目录 一、自然语言处理&#xff08;NLP&#xff09;技术有哪些运用&#xff1f; 二、Python进行文本的情感分析 1、NLTK库: 2、TextBlob库: 三、错误排除 一、自然语言处理&#xff08;NLP&#xff09;技术有哪些运用&#xff1f; 自然语言处理&#xff08;NLP&#xff09…

区块链 | NFT 水印:Review on Watermarking Techniques(一)

&#x1f34d;原文&#xff1a;Review on Watermarking Techniques Aiming Authentication of Digital Image Artistic Works Minted as NFTs into Blockchains 1 应用于 NFT 的水印技术 常见的水印技术类型可以分为&#xff1a; 可见 v i s i b l e \mathsf{visible} visi…

循环神经网络(RNN)

大家好&#xff0c;这里是七七&#xff0c;这两天在写关于神经网络相关的知识&#xff0c;面对的是有一定基础的读者哦。 一、RNN核心思想 RNN的核心思想就是曾经的输入造成的影响&#xff0c;会以致影响之后的输入&#xff0c;即隐含层的输出取决于历史数据的全部输入。 三个…

Infuse for Mac激活版:高清影音播放软件

对于热爱影音娱乐的Mac用户来说&#xff0c;Infuse for Mac是一个不容错过的选择。它以其简洁的操作界面和强大的播放功能&#xff0c;为用户带来了全新的影音播放体验。 Infuse for Mac支持广泛的音视频格式&#xff0c;无需额外转换&#xff0c;即可轻松播放您喜爱的影片。无…

Mybatis Plus二级缓存 使用@CacheNamespace 失效@CacheNamespace和@CacheNamespaceRef

1、注解 CacheNamespace(flushInterval 100000,eviction LruCache.class,readWrite false,size 1024)2、xml配置 <cache eviction "LRU" flushInterval "100000" readOnly "true" size "1024"/> 二级缓存&#xff0c;配置文…

【Linux 性能详解】CPU性能分析工具篇

目录 uptime mpstat 实时监控 查看特定CPU核心 pidstart 监控指定进程 组合多个监控类型 监控线程资源 按用户过滤进程 vmstart 用途 基本用法 输出字段 perf execsnoop dstat 通俗解释 技术层面解释 使用示例 总结 uptime uptime 是一个在 Linux 和 Unix…

上班不想用脑子写代码了怎么办?那就试试Baidu Comate啊宝贝

本文目录 前言1、视频编程实战1.1、熟悉代码库中的代码1.2、参考现有代码编写新代码 2、下载使用教程3、使用体验3.1、AutoWork 产品测评3.2、解决有关ajax请求后重定向问题3.3、询问编程相关知识3.3.1、cookie和session的区别与联系3.3.2、数据库中主键外键的相关知识 4、问题…

基于EBAZ4205矿板的图像处理:12图像二值化(阈值可调)

基于EBAZ4205矿板的图像处理&#xff1a;12图像二值化(阈值可调) 我的项目是基于EBAZ4205矿板的阈值可调的图像阈值二值化处理&#xff0c;可以通过按键调整二值化的阈值&#xff0c;key1为阈值加1&#xff0c;key4为阈值减1&#xff0c;key2为阈值加10&#xff0c;key5为阈值…

【bug记录】清除僵尸进程,释放GPU显存

目录 1. 为什么会出现这种情况&#xff1f;2. 解决方案方法一&#xff1a;使用 fuser 命令方法二&#xff1a; 3. 小贴士 在进行深度学习或其他需要GPU支持的任务时&#xff0c;我们有时会发现虽然没有可见的进程在执行&#xff0c;但GPU资源却意外地被占用。这种情况往往会阻碍…

AI换脸原理(4)——人脸对齐(关键点检测)参考文献2DFAN:代码解析

注意,本文属于人脸关键点检测步骤的论文,虽然也在人脸对齐的范畴下。 1、介绍 在本文中,重点介绍了以下几项创新性的成果,旨在为人脸关键点检测领域带来新的突破。 首先,成功构建了一个卓越的2D人脸关键点检测基线模型。这一模型不仅集成了目前最优的关键点检测网络结构,…

sqlite3命令行工具无法退出问题处理

一、背景&#xff1a; 软件使用的后台数据库为sqlite&#xff0c;linux主机系统层面使用sqlite3命令行工具登录数据库后&#xff0c;无法执行sql脚本&#xff0c;无法退出sqlite3。无法执行ctrlc&#xff0c;执行ctrlz后sqlite3前台进程被中断&#xff0c;但是该进程没有退出。…

Spring-依赖查找

依赖查找 根据名称进行查找 实时查找 BeanFactory beanFactory new ClassPathXmlApplicationContext("beans.xml"); Object bean beanFactory.getBean("personHolder"); System.out.println(bean);xml如下: <bean id"person" class&qu…

运维自动化工具:Ansible 概念与模块详解

目录 前言 一、运维自动化工具有哪些 二、Ansible 概述 1、Ansible 概念 2、Ansible 特点 3、Ansible 工作流程 4、Ansible 架构 4.1 Ansible 组成 4.2 Ansible 命令执行来源 5、Ansible 的优缺点 三、Ansible 安装部署 1、环境部署 2、管理节点安装 Ansible 3、…

Golang | Leetcode Golang题解之第75题颜色分类

题目&#xff1a; 题解&#xff1a; func sortColors(nums []int) {p0, p2 : 0, len(nums)-1for i : 0; i < p2; i {for ; i < p2 && nums[i] 2; p2-- {nums[i], nums[p2] nums[p2], nums[i]}if nums[i] 0 {nums[i], nums[p0] nums[p0], nums[i]p0}} }

模型全参数训练和LoRA微调所需显存的分析

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

k8s调度原理以及自定义调度器

kube-scheduler 是 kubernetes 的核心组件之一&#xff0c;主要负责整个集群资源的调度功能&#xff0c;根据特定的调度算法和策略&#xff0c;将 Pod 调度到最优的工作节点上面去&#xff0c;从而更加合理、更加充分的利用集群的资源&#xff0c;这也是我们选择使用 kubernete…