本文介绍如何通过Hook原生eval和Function方法,截获前端反爬机制中动态解密执行的JavaScript源码。详细讲解了针对eval和Function的挂钩代码实现,包括处理Function变长参数和this指向的技巧。同时提供了多种注入方式,如油猴脚本、代理工具(Fiddler)修改响应头以及在Chrome DevTools Protocol中注入脚本,确保在页面加载初期执行截获逻辑。
前端动态代码截获原理
当前许多网站采用了多种前端反爬策略,其核心在于将包含反爬逻辑的前端JavaScript代码进行加密或隐藏,并在运行时实时解密后动态执行。
JavaScript的动态执行主要依赖eval和Function两种机制。无论网站的加密算法多么复杂,只要成功Hook这两个方法,即可获取解密后的可执行源码。
需要注意的是,部分网站会检测eval和Function是否被篡改,因此需要采用特定技巧来规避这种检测。
eval与Function挂钩实现
挂钩代码实现
首先展示eval方法的挂钩实现:
(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方法的挂钩代码如下:
(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,其方法与eval类似,此处不再赘述。
挂钩代码注入方式
注入方式探讨
另一个关键问题是挂钩代码的注入时机与方法。
最简单的方式是通过F12打开控制台直接执行上述代码,但这只能Hook之后的调用。若希望在页面加载初期即完成注入,可采用以下几种方式:
- 油猴脚本注入:油猴可以监听文档加载的不同状态,并在特定时刻执行JavaScript代码。具体细节请参考油猴官方手册。
- 代理注入:通过修改服务器响应数据,在<head>标签内的第一个位置插入<script>节点,确保在其他JavaScript加载执行前完成注入。Fiddler、anyproxy等工具均支持编写外部规则,具体操作请参考相应代理工具手册。
- 使用chrome-devtools-protocol:通过Page.addScriptToEvaluateOnNewDocument方法注入外部JavaScript代码。
Fiddler代理规则配置
Fiddler代理规则配置
对于不熟悉代理规则的用户,以下介绍Fiddler规则的编写方法:
在Fiddler菜单中选择Rules > Customize Rules以打开脚本编辑器。
在脚本编辑器中找到OnBeforeResponse方法,并在该方法内添加以下代码:
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代码。
总结
以上介绍了在JavaScript中注入eval、Function等系统函数以截获动态代码的方法,希望对大家有所帮助。如有任何疑问,欢迎留言,小编会及时回复。在此感谢大家对本站的支持!