JS截获动态代码:Hook eval与Function及注入方法详解
本文介绍如何通过Hook JavaScript的eval和Function系统函数来截获动态执行的代码,以应对前端反爬机制。详细讲解了挂钩代码的编写细节,包括处理Function的变长参数和this指向。此外,还列举了多种注入方式,如油猴、代理工具(Fiddler、anyproxy)及Chrome DevTools Protocol,帮助开发者在页面加载早期注入监测脚本。
JS截获动态代码:Hook eval与Function及注入方法详解
本文重点讲解如何向JavaScript环境中注入eval和Function系统函数,从而截获动态生成的代码。文中提供了详细的示例代码,对学习和实际应用具有较高的参考价值。
正文
前端反爬与动态代码执行机制
当前许多网站采用了多种前端反爬策略,其核心手段通常是将包含反爬逻辑的前端JavaScript代码进行加密隐藏,并在运行时实时解密并动态执行。
动态执行JavaScript代码主要依赖eval和Function两种机制。无论网站的加密代码多么复杂,只要成功Hook这两个方法,即可获取解密后的可执行JavaScript源码。
需要注意的是,部分网站会检测eval和Function是否被修改,以判断其是否保持原生状态。因此,在实现Hook时需要采用一些技巧来规避检测。
Hook代码实现细节
挂钩代码
以下是针对eval的Hook代码示例:
(function() {
if (window.__cr_eval) return
window.__cr_eval = window.eval
var myeval = function (src) {
console.log("================ eval begin: length=" + src.length + ",caller=" + (myeval.caller && myeval.caller.name) + " ===============")
console.log(src);
console.log("================ eval end ================")
return window.__cr_eval(src)
}
var _myeval = myeval.bind(null) // 注意:这句和下一句就是小花招本招了!
_myeval.toString = window.__cr_eval.toString
Object.defineProperty(window, 'eval', { value: _myeval })
console.log(">>>>>>>>>>>>>> eval injected: " + document.location + " <<<<<<<<<<<<<<<<<<<")
})();
执行上述代码后,后续所有的eval操作都会在控制台打印出即将执行的JavaScript源码。
同理,可以编写针对Function的Hook代码:
(function() {
if (window.__cr_fun) return
window.__cr_fun = window.Function
var myfun = function () {
var args = Array.prototype.slice.call(arguments, 0, -1).join(","), src = arguments[arguments.length - 1]
console.log("================ Function begin: args=" + args + ", length=" + src.length + ",caller=" + (myfun.caller && myfun.caller.name) + " ===============")
console.log(src);
console.log("================ Function end ================")
return window.__cr_fun.apply(this, arguments)
}
myfun.toString = function() { return window.__cr_fun + "" } // 小花招
Object.defineProperty(window, 'Function', { value: myfun })
console.log(">>>>>>>>>>>>>> Function injected: " + document.location + " <<<<<<<<<<<<<<<<<<<")
})();
与eval不同,Function是一个支持变长参数的构造函数,因此在Hook时需要特别处理this指向问题。
此外,部分网站可能采用类似机制加密页面内容,并通过document.write输出动态解密后的内容。因此,同样可以Hook document.write,其方法与Hook eval类似,此处不再赘述。
代码注入方式
注入方式
另一个关键问题在于如何注入上述Hook代码。
最简单的方式是通过F12打开浏览器控制台,直接执行上述代码。但这种方式只能Hook住执行之后的eval调用。若希望从页面加载初期就注入Hook,可采用以下几种方式:
- 油猴注入:油猴可以监听文档加载的不同状态,并在特定时刻执行JavaScript代码。具体实现请参考油猴官方手册。
- 代理注入:通过修改服务器返回的响应数据,在<head>标签内的第一个位置插入<script>节点,确保在其他JavaScript加载执行前完成注入。Fiddler、anyproxy等工具均可编写外部规则实现此功能,具体方法见附录。
- 使用chrome-devtools-protocol,通过Page.addScriptToEvaluateOnNewDocument方法注入外部JavaScript代码。
- 附录
代理工具规则编写指南
对于不熟悉代理规则编写的读者,以下介绍Fiddler和anyproxy的规则编写方法:
1. 如何添加Fiddler代理规则
在Fiddler菜单中选择Rules > Customize Rules以打开脚本编辑器。
在脚本编辑器中找到OnBeforeResponse方法,并在该方法内添加以下C#代码:
if (oSession.oResponse.headers.ExistsAndContains("Content-Type", "html")){
oSession.utilDecodeResponse(); // Remove any compression or chunking
var b = System.Text.Encoding.UTF8.GetString(oSession.responseBodyBytes);
var r = /<head[^>]*>/i;
var js = "..."; // 要注入的js源码,见正文
b = b.replace(r, "$0<script>" + js + "</script>");
oSession.utilSetResponseBody(b); // Set the response body back
}
这样即可在所有HTML文档头部自动添加JavaScript代码。
2. 如何添加anyproxy代理规则
在anyproxy根目录下编辑一个rule.js文件,内容如下:
function injectEval() {
if (window.__cr_eval) return
... // 见正文,此处略
console.log(">>>>>>>>>>>>>> eval injected: " + document.location + " <<<<<<<<<<<<<<<<<<<")
}
module.exports = {
summary: 'a rule to hook all eval',
*beforeSendResponse(requestDetail, {response}) {
if (response.header["Content-Type"].indexOf("text/html") >= 0) {
response.body = (response.body + "").replace(/<head[^>]*>/i, `$&<script>(${injectEval})();</script>`)
return {response}
}
},
};
启动anyproxy时带上规则参数。
anyproxy -r rule.js
可以看出,使用基于JavaScript的工具链具有天然优势,即注入的代码可以以源码形式与规则代码共存,从而利用IDE的语法检查、自动完成等功能,显著提高开发效率。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
tween.js 内存泄漏预防:怎么正确停止动画与清理对象
2026-11-05 15:52:56
Vue或React项目中为何不建议直接使用JS刷新页面
2026-11-05 13:57:30
哪里可以找到高质量的免费JS脚本资源?
2026-11-05 13:15:03
JS数组对象怎么根据指定ID字段去重
2026-11-04 21:41:10
JS数组去重方法性能对比:Set、Map与Filter谁更快
2026-11-04 16:14:15








