Hook 钩子技术及代码注入的 3 种方式

Hook 技术介绍

    • Hook 技术中文又叫作钩子技术,它就是在程序运行的过程中,对其中的某个方法进行重写,

    • 在原有的方法前后加入我们自定义的代码。相当于在系统没有调用该函数之前,钩子程序就先捕获该消息,

    • 可以先得到控制权,这时钩子函数便可以加工处理(改变)该函数的执行行为。

    • 通俗点来说呢,比如我要 Hook 一个方法 funA,可以先临时用一个变量存一下,把它存成 A,

    • 然后呢,我再重新声明一个新的方法 funB,里面添加自己的逻辑,比如加点调试语句、输出语句等等,

    • 然后在新的方法 funB 里面再调用 A,这里调用的 A 就是之前原始的方法 funA。

    • 这样就相当于新的方法 funB 里面混入了我们自己定义的逻辑,同时又把原来的方法 A 也执行了一遍。

    • 所以这不会影响原有的执行逻辑和运行效果,但是我们通过这种改写便可以顺利在原来的 A 方法前后加上了我们自己的逻辑,这就是 Hook。

def funA():
    print('hello')
    
A = funA()

def funB():
    print('你好')
    funA()
    print('world')

B = funB()
  • 上面案例就是一Hook方法的简单演示

    • 我们要寻找funA执行的位置,通过执行我们funB方法,输出 你好 以后就是执行 funA 的位置
  • 怎么使用 Hook 的方式来找到加密 id 的加密入口点呢?原理和上面一样

    • 加密 id 是一个 Base64 编码的字符串,那么生成过程中想必就调用了 JavaScript 的 Base64 编码的方法,
    • 这个方法名叫作 btoa,这个 btoa 方法可以将参数转化成 Base64 编码。
    • 当然 Base64 也有其他的实现方式,比如利用 crypto-js 这个库实现的,这个可能底层调用的就不是 btoa 方法了。
    • 所以,其实现在并不确定是不是调用的 btoa 方法实现的 Base64 编码,那就先试试吧。
    • 要实现 Hook,其实关键在于将原来的方法改写,这里我们其实就是 Hook btoa 这个方法了,
    • btoa 这个方法属于 window 对象,我们将 window 对象的 btoa 方法进行改写即可。
  • 具体使用方法如下:

(function () {
   'use strict';
   function hook(object, attr) {
       var func = object[attr];
       object[attr] = function () {
           console.log('hooked', object, attr, arguments);
           var ret = func.apply(object, arguments);
           debugger;
           console.log('result', ret);
           return ret;
      }
  }
   hook(window, 'btoa');
})()

  • 具体步骤:

    • 首先,定义了一个 hook 方法,传入 object 和 attr 参数,意思就是 Hook 的 object 对象的 attr 参数。
      • 例如我们如果想 Hook 一个 alert 方法,那就把 object 对象设置为 window,把 attr 参数设置为要 alert 方法,arguments就是alert方法接收的参数(字符串)。
      • 这里我们想要 Hook Base64 的编码方法,那么这里就只需要传入 Hook 的window 对象(object对象)和 btoa 方法(attr参数)就好了。
      • 后面的 arguments 就是传给 btoa 方法的参数, ret 就是该 btoa 方法执行后的结果,即参数经过base64 编码后的结果
    • 然后是定义一个变量, var func = object[attr],相当于先把object对象和参数赋值为一个变量,
      • 我们调用 func 方法就可以实现和原来相同的功能。
    • 接着,我们再直接改写这个方法的定义(修改该方法),直接改写 object[attr],将其改写成一个新的方法,
      • 新的方法开头加入console.log调试语句
      • 在新的方法中,通过 func.apply 方法又重新调用了原来的方法,将其赋值到一个ret变量中,新方法最后返回原来方法的执行结果
    • 这样我们就可以保证,前后方法的执行效果是不受什么影响的,之前这个方法该干啥就还是干啥的。
      • 但是和之前不同的是,我们自定义方法之后,现在可以在 func 方法执行的前后,
      • 再加入自己的代码,如 console.log 将信息输出到控制台,如 debugger 进入断点等等
    • 最后,我们调用 hook 方法,传入 window 对象和 btoa 字符串(即window对象的btoa方法)即可
  • Hook 逻辑说明:

    • hook 过程中,我们先临时保存下来了 func 方法,然后定义一个新的方法,接管程序控制权,
    • 在新方法中自定义我们想要的实现,同时在新的方法里面再重新调回 func 方法,保证前后结果是不受影响的。
    • 所以,我们达到了在不影响原有方法效果的前提下,可以实现在方法的前后实现自定义的功能,就是 Hook 的完整实现过程。
    • 最后,我们调用 hook 方法,只需要传入 window 对象和 btoa 字符串(方法的名称)即可。

Hook 注入代码的三种方式:

- 上面已经 hook 了 btoa 方法,接下来就是注入代码,三种注入方法:
- 直接控制台注入;
- 重写 JavaScript 代码;
- Tampermonkey 油猴注入。

Hook 注入代码方法1:控制台注入

    • 直接复制上面代码到Chrome的console控制台,然后enter执行,执行完这段代码之后

    • 相当于我们就已经把 window 对象的 btoa 方法改写为我们自定义的新方法了,

    • 此时,我们可以控制台调用下 btoa 方法,回车之后就可以看到它进入了我们自定义方法的 debugger 的位置停下了

    • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cf1kEwwV-1596948392910)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/041_JavaScript逆向实例1_Hook钩子方法_代码注入_控制台注入1.png)]

    • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dXHqvBHb-1596948392924)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/041_JavaScript逆向实例1_Hook钩子方法_代码注入_控制台注入2.png)]

    • 点击调试继续键,就会继续向下执行代码:console.log(‘result’, ret);

    • 控制台输出经过 base64 加密后的字符串

    • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AzPV6O33-1596948392934)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/041_JavaScript逆向实例1_Hook钩子方法_代码注入_控制台注入3.png)]

    • 控制台直接输入的 Hook 代码,所以页面一旦刷新就无效了,但由于我们这个网站是 SPA 式的页面,

    • 所以在点击详情页的时候页面是不会整个刷新的,所以这段代码依然还会生效。

    • 但是如果不是 SPA 式的页面,即每次访问都需要刷新页面的网站,这种注入方式就不生效了

    • 经过上面分析,我们就可以查找详情页加密ID逻辑:

      • 详情页的token 值是访问列表页的 ajax 请求就生成了,
      • 注入了上面代码,每一次base64编码都会暂停并输出编码前和编码后的结果
      • 我们注入代码目的是为了 Hook 列表页 Ajax 加载完成后的加密 id 的 Base64 编码的过程,
      • 那怎么在不刷新页面的情况下再次复现这个操作呢?很简单,注入代码,enter后,点下一页就好了。
      • 这时候我们可以点击第 2 页的按钮,但是此时还是从第一页开始的,因为所有经过 btoa 方法都会执行一遍,
      • 可以看到它确实再次停到了 Hook 方法的 debugger 处,
      • 由于列表页的 Ajax 和加密 id 都会带有 Base64 编码的操作,因此它每一个都能 Hook 到,
      • 通过观察对应的 Arguments 或当前网站的行为或者观察栈信息,我们就能大体知道现在走到了哪个位置了,
      • 从而进一步通过栈的调用信息找到调用 Base64 编码的位置。
      • 我们点击右上角调试继续按钮,如果是灰色先点击一下即可,多点击几次发现
      • 控制台和右侧调试栏都会输出相应信息,观察加密前的 arguments 只有末尾一个字符不同
      • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyZP718e-1596948392946)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/041_JavaScript逆向实例1_Hook钩子方法_代码注入_控制台注入4.png)]
      • 我们暂停在某个加密位置,然后右侧 栈 一层层向上找,就可以找到加密前字符串的位置,然后继续向上找
      • 同时配合查看 网页返回的 json 内容,可以发现加密前的字符串是由一个写死的字符串
      • ef34#teuq0btua#(-57w1q5o5–j@98xygimlyfxs*-!i-0-mb和电影的真实ID拼接在一起的
      • 拼接后的字符串,然后进行base64编码
    • 控制台注入代码存在问题:

      • Hook 代码是在控制台手动输入的,一旦刷新页面就不生效了,这的确是个问题。
      • 而且它必须是在页面加载完了才注入的,所以它并不能在一开始就生效。
      • 上面通过点击下一页,重新发起请求完成

重写 JavaScript

  • Hook 注入代码方法2:
  • 重写 JavaScript
    • 我们可以借助于 Chrome 浏览器的 Overrides (重写)功能实现某些 JavaScript 文件的重写和保存,

    • 它会在本地生成一个 JavaScript 文件副本,以后每次刷新的时候会使用副本的内容。

    • 这里我们需要切换到 Sources 选项卡的 Overrides 选项卡,

    • 然后选择一个文件夹,比如这里进入项目文件里面新建了一个文件夹名字叫作 modify

    • 然后切换到Sources-Page-js文件夹中,随便选择一个js文件脚本,后面贴上上面 Hook 后的 btoa 代码

    • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QyF1c8F1-1596948392948)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/042_JavaScript逆向实例1_Hook钩子方法_代码注入_重写JavaScript1.png)]

    • 注意:修改代码不要格式化代码,直接原始js文件,先end 到结尾 然后换行空一行,粘贴代码,末尾可以添加上 ; 号,添加多个需要加上。

    • 右键刚刚修改的js文件名称,保存到上面新建的modify文件夹中

    • 此时可能提示页面崩溃,但是不用担心,重新刷新页面就好了,这时候我们就发现现在浏览器加载的 JavaScript 文件就是我们修改过后的了,文件的下方会有一个标识符,蓝色小点

    • 然后点击调试继续按钮,没执行到 btoa 方法就暂停一下,我们进入控制台console里面查看

    • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uhHKWUVn-1596948392952)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/042_JavaScript逆向实例1_Hook钩子方法_代码注入_重写JavaScript2.png)]

    • 最先执行了 列表页 的token加密,然后就是详情页id加密,elements里面详情页ID出现了两次,执行了两次

    • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7HwlLqMn-1596948392955)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/042_JavaScript逆向实例1_Hook钩子方法_代码注入_重写JavaScript3.png)]

    • 其实 Overrides 的这个功能非常有用,我们保存了修改后文件到本地,有了它我们可以持久化保存我们任意修改的 JavaScript 代码,

    • 所以我们想在哪里改都可以了,甚至可以直接修改 JavaScript 的原始执行逻辑也都可以的

Tampermonkey 油猴插件注入

  • Tampermonkey 油猴注入

    • 如果我们不想用 Overrides 的方式改写 JavaScript 的方式注入的话,还可以借助于浏览器插件来实现注入,
    • 这里推荐的浏览器插件叫作 Tampermonkey,中文叫作“油猴”。它是一款浏览器插件,支持 Chrome。
    • 利用它我们可以在浏览器加载页面时自动执行某些 JavaScript 脚本。
    • 由于执行的是 JavaScript,所以我们几乎可以在网页中完成任何我们想实现的效果,
    • 如自动爬虫、自动修改页面、自动响应事件等等。
    • 可以管理网上下载的一些脚本
  • 安装Tampermonkey插件

    • 官网下载:https://www.tampermonkey.net/
    • 脚本管理器:https://greasyfork.org/zh-CN
    • 一些使用的三方脚本网站:https://greasyfork.org/zh-CN/scripts
    • 网络问题,会安装失败,进入第三方地址下载解压得到crx文件安装
    • 下载地址:http://www.pc6.com/soft/FireFox_247376.html
    • 下载解压后,Chrome进入更多工具-扩展程序界面,将crx文件拖入即可安装
    • 查看[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-caJV8wJT-1596948392957)(003_JavaScript混淆加密_Ajax动态请求_哔哩哔哩JS动态请求分析提取/043_JavaScript逆向实例1_Hook钩子方法_代码注入_tampermonkey油猴js脚本管理器安装.png)]
    • 然后点击右上角,扩展程序,点击tampermonkey后面的固定,固定在工具栏
    • 安装完成之后,在 Chrome 浏览器的右上角会出现 Tampermonkey 的图标
  • 使用 Tampermonkey插件

    • 点击 Tampermonkey 插件图标,点击“管理面板”按钮,打开脚本管理页面
    • 显示了我们已经有的一些 Tampermonkey 脚本,包括我们自行创建的,也包括从第三方网站下载安装的
    • 编辑、调试、删除等管理功能,我们可以方便地对脚本进行管理。
    • 接下来我们来创建一个新的脚本来试试,点击左侧的“+”号
    • 会出来一个初始化文件脚本,版本不同可能注释内容有区别
// ==UserScript==
// @name         New Userscript
// @namespace   http://tampermonkey.net/
// @version     0.1
// @description try to take over the world!
// @author       You
// @match       https://www.tampermonkey.net/documentation.php?ext=dhdg
// @grant       none
// ==/UserScript==

(function() {
   'use strict';

   // Your code here...
})();
  • 最上面是一些注释,但这些注释是非常有用的,这部分内容叫作 UserScript Header ,

    • 我们可以在里面配置一些脚本的信息,如名称、版本、描述、生效站点等等。
    • 在 UserScript Header 下方是 JavaScript 函数和调用的代码,
    • 其中 use strict 标明代码使用 JavaScript 的严格模式,
    • 在严格模式下可以消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为,
    • 如不能直接使用未声明的变量,这样可以保证代码的运行安全,
    • 同时提高编译器的效率,提高运行速度。
    • 在下方 // Your code here… 这里我们就可以编写自己的代码了。
  • 我们将上面 hook 后的 btoa 代码复制进去,然后保存

    • 重新访问目标网址:https://dynamic6.scrape.cuiqingcai.com/
    • 进入开发者工具,然后刷新页面,就会停留在脚本的debugger位置
    • 然后就可以点击调试工具栏继续按钮,调试断点运行,然后找到所有经过btoa编码的代码

你可能感兴趣的:(反爬虫)