如何基于JS截获动态代码


Posted in Javascript onDecember 25, 2019

这篇文章主要介绍了JS注入eval, Function系统函数并截获动态代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

正文

现在很多网站都上了各种前端反爬手段,无论手段如何,最重要的是要把包含反爬手段的前端javascript代码加密隐藏起来,然后在运行时实时解密动态执行。

动态执行js代码无非两种方法,即eval和Function。那么,不管网站加密代码写的多牛,我们只要将这两个方法hook住,即可获取到解密后的可执行js代码。

注意,有些网站会检测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操作都会在控制台打印输出将要执行的js源码。

同理可以写出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是个有变长参数的构造方法,需要处理this

另外,有些网站还会用类似的机制加密页面内容,然后通过document.write输出动态解密的内容,因此同样可以挂钩document.write,挂钩方法类似eval,这里就不重复了。

注入方式

另外,还有个问题需要关注,就是挂钩代码的注入方法。

最简单的就是F12调出控制台,直接执行上面的代码,但这样只能hook住执行之后的eval调用,如果希望从页面刚加载时就注入,那么可以用以下几种方式:

  • 油猴注入,油猴可以监听文档加载的几种不同状态,并在特定时刻执行js代码。我没有太多研究,具体请参见油猴手册
  • 代理注入,修改应答数据,在<head>标签内的第一个位置插入<script>节点,确保在其它js加载执行前注入;Fiddler, anyproxy等都可以编写外部规则,具体请参见附录部分
  • 使用chrome-devtools-protocol, 通过Page.addScriptToEvaluateOnNewDocument注入外部js代码
  • 附录

不少人没用过代理规则,这里写一下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文档头部自动添加js代码了

2. 如何添加anyproxy代理规则

编辑一个rule.js保存在anyproxy根目录下,内容如下:

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

可以看到,使用基于js的工具链有其天然优势,即注入代码可以以源码而不是字符串形式和规则代码共存,这样可以利用到IDE的语法检查、自动完成等机制,能够大大提高生产力。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
HTML DOM的nodeType值介绍
Mar 31 Javascript
基于jquery的多彩百分比 动态进度条 投票效果显示效果实现代码
Aug 28 Javascript
JS实现根据出生年月计算年龄
Jan 10 Javascript
JS控制表格实现一条光线流动分割行的方法
Mar 09 Javascript
Angular 根据 service 的状态更新 directive
Apr 03 Javascript
设置点击文本框或图片弹出日历控件的实现代码
May 12 Javascript
Vuejs第七篇之Vuejs过渡动画案例全面解析
Sep 05 Javascript
10个在JavaScript开发中常遇到的BUG
Dec 18 Javascript
jQuery Migrate 插件用法实例详解
May 22 jQuery
如何解决日期函数new Date()浏览器兼容性问题
Sep 11 Javascript
JavaScript实现移动端拖动元素
Nov 24 Javascript
vue 数字翻牌器动态加载数据
Apr 20 Vue.js
通过微信公众平台获取公众号文章的方法示例
Dec 25 #Javascript
vue远程加载sfc组件思路详解
Dec 25 #Javascript
node实现mock-plugin中间件的方法
Dec 25 #Javascript
微信小程序停止其他视频播放当前视频的实例代码
Dec 25 #Javascript
vue分页插件的使用方法
Dec 25 #Javascript
继承行为在 ES5 与 ES6 中的区别详解
Dec 24 #Javascript
在JavaScript中实现链式调用的实现
Dec 24 #Javascript
You might like
留言板翻页的实现详解
2006/10/09 PHP
PHP 采集获取指定网址的内容
2010/01/05 PHP
PHP-CGI进程CPU 100% 与 file_get_contents 函数的关系分析
2011/08/15 PHP
phpmyadmin打开很慢的解决方法
2014/04/21 PHP
php eval函数一句话木马代码
2015/05/21 PHP
PHP抓取及分析网页的方法详解
2016/04/26 PHP
Thinkphp框架 表单自动验证登录注册 ajax自动验证登录注册
2016/12/27 PHP
php 人员权限管理(RBAC)实例(推荐)
2017/05/24 PHP
详解php框架Yaf路由重写
2017/06/20 PHP
js删除所有的cookie的代码
2010/11/25 Javascript
jquery ajax return没有返回值的解决方法
2011/10/20 Javascript
JQuery boxy插件在IE中边角图片不显示问题的解决
2015/05/20 Javascript
BootStrap的alert提示框的关闭后再显示怎么解决
2016/05/17 Javascript
由简入繁实现Jquery树状结构的方法(推荐)
2016/06/10 Javascript
JS中BOM相关知识点总结(必看篇)
2016/11/22 Javascript
深入理解javascript中concat方法
2016/12/12 Javascript
基于JavaScript实现自动更新倒计时效果
2016/12/19 Javascript
Reactjs实现通用分页组件的实例代码
2017/01/19 Javascript
详解react-native-fs插件的使用以及遇到的坑
2017/09/12 Javascript
浅谈Angular路由复用策略
2017/10/04 Javascript
原生JS实现Ajax跨域请求flask响应内容
2017/10/24 Javascript
详解js静态检查工具eslint配置文件
2018/11/23 Javascript
vue 搭建后台系统模块化开发详解
2019/05/01 Javascript
jQuery操作事件完整实例分析
2020/01/10 jQuery
python实现读取命令行参数的方法
2015/05/22 Python
浅谈Python类的__getitem__和__setitem__特殊方法
2016/12/25 Python
Python中Scrapy爬虫图片处理详解
2017/11/29 Python
python 实现selenium断言和验证的方法
2019/02/13 Python
TensorFlow 多元函数的极值实例
2020/02/10 Python
Python基于os.environ从windows获取环境变量
2020/06/09 Python
Hotels.com香港酒店网:你的自由行酒店订房专家
2018/01/22 全球购物
匈牙利超级网上商店和优惠:Alza.hu
2019/12/17 全球购物
eDreams德国:南欧领先的在线旅游公司
2020/12/07 全球购物
WSDL的操作类型主要有几种
2013/07/19 面试题
物业总经理岗位职责
2014/02/28 职场文书
2019幼儿教师求职信(3篇)
2019/09/20 职场文书