JS 逆向之 Hook

什么是 Hook?

Hook 中文译为钩子,Hook 实际上是 Windows 中提供的一种用以替换 DOS 下“中断”的系统机制,Hook 的概念在 Windows 桌面软件开发很常见,特别是各种事件触发的机制,在对特定的系统事件进行 Hook 后,一旦发生已 Hook 事件,对该事件进行 Hook 的程序就会收到系统的通知,这时程序就能在第一时间对该事件做出响应。在程序中将其理解为“劫持”可能会更好理解,我们可以通过 Hook 技术来劫持某个对象,把某个对象的程序拉出来替换成我们自己改写的代码片段,修改参数或替换返回值,从而控制它与其他对象的交互。

通俗来讲,Hook 其实就是拦路打劫,马邦德带着老婆,出了城,吃着火锅,还唱着歌,突然就被麻匪劫了,张麻子劫下县长马邦德的火车,摇身一变化身县长,带着手下赶赴鹅城上任。Hook 的过程,就是张麻子顶替马邦德的过程。

Object.defineProperty()

本语法:Object.defineProperty(obj, prop, descriptor),它的作用就是直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,接收的三个参数含义如下:

obj:需要定义属性的当前对象;

prop:当前需要定义的属性名;

descriptor:属性描述符,可以取以下值:

属性名默认值含义
getundefined存取描述符,目标属性获取值的方法
setundefined存取描述符,目标属性设置值的方法
valueundefined数据描述符,设置属性的值
writablefalse数据描述符,目标属性的值是否可以被重写
enumerablefalse目标属性是否可以被枚举
configurablefalse目标属性是否可以被删除或是否可以再次修改特性

在 Hook 中,使用最多的是存取描述符,即 get 和 set。

get:属性的 getter 函数,如果没有 getter,则为 undefined,当访问该属性时,会调用此函数,执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的 this 并不一定是定义该属性的对象),该函数的返回值会被用作属性的值。

set:属性的 setter 函数,如果没有 setter,则为 undefined,当属性值被修改时,会调用此函数,该方法接受一个参数,也就是被赋予的新值,会传入赋值时的 this 对象。

用一个例子来演示:

  1. var people = {
  2. name: 'Bob',
  3. };
  4. var count = 18;
  5.  
  6. // 定义一个 age 获取值时返回定义好的变量 count
  7. Object.defineProperty(people, 'age', {
  8. get: function () {
  9. console.log('获取值!');
  10. return count;
  11. },
  12. set: function (val) {
  13. console.log('设置值!');
  14. count = val + 1;
  15. },
  16. });
  17.  
  18. console.log(people.age);
  19. people.age = 20;
  20. console.log(people.age);

通过这样的方法,我们就可以在设置某个值的时候,添加一些代码,比如 debugger;,让其断下,然后利用调用栈进行调试,找到参数加密、或者参数生成的地方,需要注意的是,网站加载时首先要运行我们的 Hook 代码,再运行网站自己的代码,才能够成功断下,这个过程我们可以称之为 Hook 代码的注入,以下将介绍几种主流的注入方法。

1、Fiddler 插件注入

编程猫的插件

2、TamperMonkey 注入

油猴插件,是一款免费的浏览器扩展和最为流行的用户脚本管理器

3、浏览器插件注入

浏览器插件官方叫法应该是浏览器扩展(Extension)

常用 Hook 代码总汇

除了使用上述的 Object.defineProperty() 方法,还可以直接捕获相关接口,然后重写这个接口,以下列出了常见的 Hook 代码。注意:以下只是关键的 Hook 代码,具体注入的方式不同,要进行相关的修改。

Cookie Hook 用于定位 Cookie 中关键参数生成位置,以下代码演示了当 Cookie 中匹配到了 __dfp 关键字, 则插入断点:

  1. (function () {
  2. 'use strict';
  3. var cookieTemp = '';
  4. Object.defineProperty(document, 'cookie', {
  5. set: function (val) {
  6. if (val.indexOf('__dfp') != -1) {
  7. debugger;
  8. }
  9. console.log('Hook捕获到cookie设置->', val);
  10. cookieTemp = val;
  11. return val;
  12. },
  13. get: function () {
  14. return cookieTemp;
  15. },
  16. });
  17. })();
  18. (function () {
  19. 'use strict';
  20. var org = document.cookie.__lookupSetter__('cookie');
  21. document.__defineSetter__('cookie', function (cookie) {
  22. if (cookie.indexOf('__dfp') != -1) {
  23. debugger;
  24. }
  25. org = cookie;
  26. });
  27. document.__defineGetter__('cookie', function () {
  28. return org;
  29. });
  30. })();

Hook Header

Header Hook 用于定位 Header 中关键参数生成位置,以下代码演示了当 Header 中包含 Authorization 关键字时,则插入断点:

  1. (function () {
  2. var org = window.XMLHttpRequest.prototype.setRequestHeader;
  3. window.XMLHttpRequest.prototype.setRequestHeader = function (key, value) {
  4. if (key == 'Authorization') {
  5. debugger;
  6. }
  7. return org.apply(this, arguments);
  8. };
  9. })();

Hook URL

URL Hook 用于定位请求 URL 中关键参数生成位置,以下代码演示了当请求的 URL 里包含 login 关键字时,则插入断点:

  1. (function () {
  2. var open = window.XMLHttpRequest.prototype.open;
  3. window.XMLHttpRequest.prototype.open = function (method, url, async) {
  4. if (url.indexOf("login") != 1) {
  5. debugger;
  6. }
  7. return open.apply(this, arguments);
  8. };
  9. })();

Hook JSON.stringify

JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.stringify() 时,则插入断点:

  1. (function() {
  2. var stringify = JSON.stringify;
  3. JSON.stringify = function(params) {
  4. console.log("Hook JSON.stringify ——> ", params);
  5. debugger;
  6. return stringify(params);
  7. }
  8. })();

Hook JSON.parse

JSON.parse() 方法用于将一个 JSON 字符串转换为对象,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.parse() 时,则插入断点:

  1. (function() {
  2. var parse = JSON.parse;
  3. JSON.parse = function(params) {
  4. console.log("Hook JSON.parse ——> ", params);
  5. debugger;
  6. return parse(params);
  7. }
  8. })();

Hook eval

JavaScript eval() 函数的作用是计算 JavaScript 字符串,并把它作为脚本代码来执行。如果参数是一个表达式,eval() 函数将执行表达式。如果参数是 Javascript 语句,eval() 将执行 Javascript 语句,经常被用来动态执行 JS。以下代码执行后,之后所有的 eval() 操作都会在控制台打印输出将要执行的 JS 源码:

  1. (function() {
  2. // 保存原始方法
  3. window.__cr_eval = window.eval;
  4. // 重写 eval
  5. var myeval = function(src) {
  6. console.log(src);
  7. console.log("=============== eval end ===============");
  8. debugger;
  9. return window.__cr_eval(src);
  10. }
  11. // 屏蔽 JS 中对原生函数 native 属性的检测
  12. var _myeval = myeval.bind(null);
  13. _myeval.toString = window.__cr_eval.toString;
  14. Object.defineProperty(window, 'eval', {
  15. value: _myeval
  16. });
  17. })();

Hook Function

以下代码执行后,所有的函数操作都会在控制台打印输出将要执行的 JS 源码:

  1. (function() {
  2. // 保存原始方法
  3. window.__cr_fun = window.Function;
  4. // 重写 function
  5. var myfun = function() {
  6. var args = Array.prototype.slice.call(arguments, 0, -1).join(","),
  7. src = arguments[arguments.length - 1];
  8. console.log(src);
  9. console.log("=============== Function end ===============");
  10. debugger;
  11. return window.__cr_fun.apply(this, arguments);
  12. }
  13. // 屏蔽js中对原生函数native属性的检测
  14. myfun.toString = function() {
  15. return window.__cr_fun + ""
  16. }
  17. Object.defineProperty(window, 'Function', {
  18. value: myfun
  19. });
  20. })();

 

转载于:https://www.cnblogs.com/ikdl/p/15352809.html

发表评论

邮箱地址不会被公开。 必填项已用*标注