协程6 --- HOOK

文章目录

  • HOOK 概述
    • 链接
    • 运行时动态链接
  • linux上的常见HOOK方式
    • 修改函数指针
    • 用户态动态库拦截
      • getpid
      • malloc 第一版
      • malloc 第二版
      • malloc/free通过指针获取到空间大小
      • malloc 第三版
      • strncmp
    • 内核态系统调用拦截
    • 堆栈式文件系统
  • 协程的HOOK

HOOK 概述

原理:修改符号指向
我们可以通过 HOOK 系统的socket函数族来实现无需修改代码的异步化改造。

链接

  • 编译器可以将我们编写的代码编译成为目标代码,而链接器则负责将多个目标代码收集起来并组合成为一个单一的文件。
  • 链接过程可以执行于编译时(compile time),也可以执行于加载时(load time),甚至可以执行于运行时(run time)。
  • 执行于编译时的链接被称为静态链接,而执行于加载时和运行时被称为动态链接。

运行时动态链接

可以通过共享库的方式来让引用程序在下次运行时执行不同的代码,Unix-like 系统提供了 dlopendlsym系列函数来供程序在运行时操作外部的动态链接库,从而获取动态链接库中的函数或者功能调用。

例如:微信后台的 c/c++ 协程库 libco。 libco 使用动态链接 Hook 系统函数,最大的特点是将系统中的关于网络操作的阻塞函数全部进行相应的非侵入式改造,对于 readwrite 等阻塞函数,libco 均定义了自己的版本,然后通过 LD_PRELOAD 进行运行时地解析,从而来达到阻塞时自动让出协程,并在 IO 事件发生时唤醒协程的目的。

linux上的常见HOOK方式

修改函数指针

通过函数指针来指向不同的函数地址控制执行流,通常运用于编程中。

比如说glic提供__malloc_hook, __realloc_hook, __free_hook可以实现hook自定义malloc/free函数

用户态动态库拦截

LD_PRELOAD可以影响程序的运行时链接,允许用户在运行前优先加载指定库。
可以通过这个指定我们预定义的库,指定库中符号为程序动态链接所需库的同名符号,这样就能实现覆盖,使得程序只访问我们指定符号。
一般情况下,动态库加载加载顺序为:LD_PRELOAD>LD_LIBRARY_PATH>/etc/ld.so.cache>/lib>/usr/lib

getpid

测试程序:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{printf("my uid is %d\n", getpid());return 0;
}

HOOK程序:

#include <unistd.h>
#include <sys/types.h>pid_t getpid()
{return 0;
}

命令:

gcc demo1.c -o demo1
gcc -fPIC -shared hook1.c -o hook1.so
./demo1
LD_PRELOAD=./hook1.so ./demo1

运行:
在这里插入图片描述

malloc 第一版

测试程序:

#include <stdio.h>
#include <stdlib.h>int main()
{char *a = (char*)malloc(sizeof(char) * 8);char *b = (char*)malloc(sizeof(char) * 16);char *c = (char*)malloc(sizeof(char) * 32);char *d = (char*)malloc(sizeof(char) * 64);free(d);free(c);free(b);free(a);return 0;
}

HOOK程序:

#include <stdio.h>
#include <stdlib.h>static size_t allocSize = 0;void *malloc(size_t size)
{void *res = __libc_malloc(size);allocSize += *(int*)((char*)res - sizeof(size_t)) & ~((0x1) | (0x2) | (0x4));printf("malloc allocSize: %ld\n", allocSize);return res;
}void free(void *ptr)
{__libc_free(ptr);allocSize -= *(int*)((char*)ptr - sizeof(size_t)) & ~((0x1) | (0x2) | (0x4));printf("free allocSize: %ld\n", allocSize);
}

命令:

gcc demo2.c -o demo2
gcc -fPIC -shared hook2.c -o hook2.so -w
./demo2
LD_PRELOAD=./hook2.so ./demo2

执行:
在这里插入图片描述

malloc 第二版

这边问题在于printf会调用malloc,产生了递归调用,最终core掉。
HOOK程序:

#include <stdio.h>
#include <stdlib.h>static size_t allocSize = 0;static int enableMallocHook = 1;
static int enableFreeHook = 1;void *malloc(size_t size)
{if (enableMallocHook){enableMallocHook = 0;void *res = __libc_malloc(size);allocSize += *(int*)((char*)res - sizeof(size_t)) & ~((0x1) | (0x2) | (0x4));printf("malloc allocSize: %ld\n", allocSize);enableMallocHook = 1;return res;}else{return __libc_malloc(size);}
}void free(void *ptr)
{if (enableMallocHook){enableMallocHook = 0;__libc_free(ptr);allocSize -= *(int*)((char*)ptr - sizeof(size_t)) & ~((0x1) | (0x2) | (0x4));printf("free allocSize: %ld\n", allocSize);enableMallocHook = 1;}else{__libc_free(ptr);}
}

命令:

gcc demo2.c -o demo2
gcc -fPIC -shared hook2.c -o hook2.so -w
./demo2
LD_PRELOAD=./hook2.so ./demo2

执行:
在这里插入图片描述

试一下ls命令:
在这里插入图片描述
直接挂掉了,这次问题出现在了我们没处理free空指针

malloc/free通过指针获取到空间大小

我们可以看到allocSize += *(int*)((char*)res - sizeof(size_t)) & ~((0x1) | (0x2) | (0x4));,并且

  • 申请8 计算得到32(8+16 后16字节对齐)
  • 申请16 计算得到32(8+16 后16字节对齐)
  • 申请32 计算得到48(8+32 后16字节对齐)
  • 申请64 计算得到80(8+64 后16字节对齐)

malloc_chunk的基础结构:
在这里插入图片描述

  • mchunk_prev_size:该字段记录物理相邻的前一个chunk的大小(低地址chunk)。如果前一个chunk处于空闲,则该字段记录前一个chunk大小;如果前一个chunk已经被使用,则该字段空间可以被前一个chunk的用户数据空间复用。
  • mchunk_size:该字段是chunk的大小。该字段的低三个比特位对 chunk 的大小没有影响,所以被复用为标志位。
    • A:NON_MAIN_ARENA的缩写,指所用arena是不是main arena的flag
    • M:IS_MMAPPED的缩写,指所用chunk是不是经由mmap分配所得
    • P:PREV_INUSE的缩写,指当前chunk的前一个chunk是不是allocated chunk,是的话这个bit为1,否则为0
  • fdbk:当chunk空闲的时候,会放置到bins上双向链表管理。fd 指向下一个(非物理相邻)空闲的 chunk。bk 指向上一个(非物理相邻)空闲的 chunk。由于只有chunk空闲的时候,才会放置到bins上进行空闲管理,所以fd和bk占用的是用户数据区域user data
  • fd_nextsizebk_nextsize:用于管理large块的时候的空闲chunk双向链表的管理。一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列。这样做可以避免在寻找合适 chunk 时挨个遍历,也是复用用户数据区域。large chunk的空间肯定装的下。

最小的空间:mchunk_prev_size字段 + mchunk_size字段 + fd字段 + bk字段 所需要的空间。64位需要16字节,32位需要8字节。
chunk对齐规则:按照2*SIZE_SZ进行对齐,64位系统是16字节,32位系统是8字节。
chunk的size:(chunk的mchunk_size字段空间 + 用户数据区域(最小16))& 对齐字节(2*SIZE_SZ)。

搞个程序gdb看一下:

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main()
{char *p = malloc(100);*p = 'A'; // 0x41char *h = p - 8;printf("%d\n", malloc_usable_size(p));                  // 104printf("%d\n", *((int *)h) & ~((0x1) | (0x2) | (0x4))); // 112printf("%d", *((int *)h));                              // 113
}

运行:

gcc testmalloc.c -o testmalloc -g

在这里插入图片描述
我们可以看到malloc大小100的空间,指针h指向了header,为113,去掉低3位为112。
100+8后16字节对齐是112。

malloc 第三版

demo3.c和demo2.c一模一样
HOOK程序:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>static void* (*sysMalloc)(size_t size) = NULL;
static void (*sysFree)(void *ptr) = NULL;static int allocSize = 0;
static int enableMallocHook = 1;
static int enableFreeHook = 1;void init()
{sysMalloc = dlsym(RTLD_NEXT, "malloc");sysFree = dlsym(RTLD_NEXT, "free");
}void *malloc(size_t size)
{if (sysMalloc){init();}if (enableMallocHook){enableMallocHook = 0;void *res = sysMalloc(size);allocSize += malloc_usable_size(res);printf("malloc allocSize: %ld\n", allocSize);enableMallocHook = 1;return res;}else{return sysMalloc(size);}
}void free(void *ptr)
{if (enableMallocHook){enableMallocHook = 0;sysFree(ptr);allocSize -= malloc_usable_size(ptr);printf("free allocSize: %ld\n", allocSize);enableMallocHook = 1;}else{sysFree(ptr);}
}

命令:

gcc demo3.c -o demo3
gcc -fPIC -shared hook3.c -o hook3.so -ldl -w
./demo3
LD_PRELOAD=./hook3.so ./demo3

执行:
在这里插入图片描述
再试一下ls命令:
在这里插入图片描述
在这里插入图片描述

strncmp

测试程序:

#include <stdio.h>
#include <string.h>int main()
{int res = strncmp("test", "aaa", 4);printf("%d\n", res);return 0;
}

HOOK程序:

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>static int (*sysStrncmp)(const char *__s1, const char *__s2, size_t __n) = NULL;void init()
{sysStrncmp = dlsym(RTLD_NEXT, "strncmp");
}extern int strncmp(const char *__s1, const char *__s2, size_t __n)
{if (!sysStrncmp){init();}printf("参数:%s, %s, %ld\n", __s1, __s2, __n);return sysStrncmp(__s1, __s2, __n);
}

命令:

gcc demo4.c -o demo4
gcc -fPIC -shared hook4.c -o hook4.so -ldl -w
./demo4
LD_PRELOAD=./hook4.so ./demo4
LD_PRELOAD=./hook4.so ls

运行:
在这里插入图片描述
自己的程序没成功,但是ls却成功了。
在这里插入图片描述
自己写的程序里面没有strncmp的动态符号
汇编看一下,发现已经被编译器优化变成纯汇编了,并没有函数调用:

objdump -d demo40000000000401122 <main>:401122:       55                      push   %rbp401123:       48 89 e5                mov    %rsp,%rbp401126:       48 83 ec 10             sub    $0x10,%rsp40112a:       c7 45 fc 13 00 00 00    movl   $0x13,-0x4(%rbp)401131:       8b 45 fc                mov    -0x4(%rbp),%eax401134:       89 c6                   mov    %eax,%esi401136:       bf 04 20 40 00          mov    $0x402004,%edi40113b:       b8 00 00 00 00          mov    $0x0,%eax401140:       e8 eb fe ff ff          callq  401030 <printf@plt>401145:       b8 00 00 00 00          mov    $0x0,%eax40114a:       c9                      leaveq 40114b:       c3                      retq   40114c:       0f 1f 40 00             nopl   0x0(%rax)

内核态系统调用拦截

Linux内核中所有的系统调用都是放在一个叫做sys_call_table的内核数组中,数组的值就表示这个系统调用服务程序的入口地址。

sys_call_table
在实模式下叫中断向量表
在保护模式中IDT,又称中断描述符表

当用户态发起一个系统调用时,会通过80软中断进入到syscall_hander,进而进入全局的系统调用表sys_call_table去查找具体的系统调用,那么如果我们将这个数组中的地址改成我们自己的程序地址,就可以实现系统调用劫持。
在这里插入图片描述
问题:

  • sys_call_table的符号没有导出,不能直接获取。(grep sys_call_table /boot/ -r)
  • sys_call_table所在的内存页是只读属性的,无法直接进行修改。(清除CR0寄存器的WP控制位)
  • Linux大概从4.8开始加入了保护机制,每次开机sys_call_table的地址都会变化

堆栈式文件系统

Linux通过vfs虚拟文件系统来统一抽象具体的磁盘文件系统,从上到下的IO栈形成了一个堆栈式。
内核中采用了很多c语言形式的面向对象,也就是函数指针的形式,例如read是vfs提供用户的接口,具体底下调用的是ext2的read操作。我们只要实现VFS提供的各种接口,就可以实现一个堆栈式文件系统。
在这里插入图片描述

协程的HOOK

使用用户态动态库拦截,dlsym,在不改造业务代码的情况下,使用协程,如libgo中代码:

unsigned int sleep(unsigned int seconds)
{// 如果没初始化则初始化hookif (!sleep_f) initHook();// 获取当前调度任务Task* tk = Processer::GetCurrentTask();DebugPrint(dbg_hook, "task(%s) hook sleep(seconds=%u). %s coroutine.",tk->DebugInfo(), seconds,Processer::IsCoroutine() ? "In" : "Not in");// 如果没有任务,则调用真正的sleepif (!tk)return sleep_f(seconds);// 挂起当前协程, 并在指定时间后自动唤醒Processer::Suspend(std::chrono::seconds(seconds));Processer::StaticCoYield();return 0;
}

其中initHook中:

sleep_f = (sleep_t)dlsym(RTLD_NEXT, "sleep");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/59966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ResNet 残差网络 (乘法→加法的思想 - 残差连接是所有前沿模型的标配) + 代码实现 ——笔记2.16《动手学深度学习》

目录 前言 0. 乘法变加法的思想 1. 函数类 2. 残差块 (讲解代码) QA: 残差这个概念的体现&#xff1f; 3. ResNet模型 (代码讲解) 补充&#xff1a;更多版本的ResNet 4. 训练模型 5. 小结 6. ResNet的两大卖点 6.1 加深模型可以退化为浅层模型 6.2 用加法解决梯度消…

iphone怎么删除重复的照片的新策略

Phone用户常常面临存储空间不足的问题&#xff0c;其中一个主要原因是相册中的重复照片。这些重复项不仅占用了大量的存储空间&#xff0c;还会影响设备的整体性能。本文将向您展示iphone怎么删除重复的照片的方法&#xff0c;包括一些利用工具来自动化这个过程的创新方法。 识…

软件缺陷等级评定综述

1. 前言 正确评估软件缺陷等级&#xff0c;在项目的生命周期中有着重要的作用&#xff1a; 指导缺陷修复的优先级和资源分配 在软件开发和维护过程中&#xff0c;资源&#xff08;包括人力、时间和资金&#xff09;是有限的。通过明确缺陷的危险等级&#xff0c;可以帮助团队合…

【Pikachu】Cross-Site Scripting跨站脚本攻击实战

只管把目标定在高峰&#xff0c;人家要笑就让他去笑&#xff01; 1.XSS&#xff08;跨站脚本&#xff09;概述 XSS&#xff08;跨站脚本&#xff09;概述 Cross-Site Scripting 简称为“CSS”&#xff0c;为避免与前端叠成样式表的缩写"CSS"冲突&#xff0c;故又称…

【SpringBoot】 黑马大事件笔记-day2

目录 用户部分 实体类属性的参数校验 更新用户密码 文章部分 规定josn日期输出格式 分组校验 上期回顾&#xff1a;【SpringBoot】 黑马大事件笔记-day1 用户部分 实体类属性的参数校验 对应的接口文档&#xff1a; 基本信息 请求路径&#xff1a;/user/update 请求方式&#…

大数据面试题--kafka夺命连环问

1、kafka消息发送的流程&#xff1f; 在消息发送过程中涉及到两个线程&#xff1a;一个是 main 线程和一个 sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给双端队列&#xff0c;sender 线程不断从双端队列 RecordAccumulator 中拉取…

QT信号和槽与自定义的信号和槽

QT信号和槽与自定义的信号和槽 1.概述 这篇文章介绍下QT信号和槽的入门知识&#xff0c;通过一个案例介绍如何创建信号和槽&#xff0c;并调用他们。 2.信号和槽使用 下面通过点击按钮关闭窗口的案例介绍如何使用信号和槽。 创建按钮 在widget.cpp文件中创建按钮代码如下 …

unity显示获取 年月日周几【日期】

unity显示获取 年月日周几【日期】 public void ShowDate(Text txt){//txt.text DateTime now DateTime.Now; // 获取当前时间int year now.Year; // 获取年份int month now.Month; // 获取月份&#xff08;1-12&#xff09;int day now.Day; // 获取天数&#xff08;1-31&…

emr上使用sparkrunner运行beam数据流水线

参考资料 https://time.geekbang.org/column/intro/167?tabcatalog Apache Beam和其他开源项目不太一样&#xff0c;它并不是一个数据处理平台&#xff0c;本身也无法对数据进行处理。Beam所提供的是一个统一的编程模型思想&#xff0c;而我们可以通过这个统一出来的接口来编…

AUTOSAR CP SocketAdaptor(SoAd)规范导读

《AUTOSAR_SWS_SocketAdaptor》规范的主要内容包括&#xff1a; 简介和功能概述&#xff1a;说明了 AUTOSAR 基本软件模块 Socket Adaptor&#xff08;SoAd&#xff09;的功能、API 和配置。数据传输的 TCP/IP 概念在计算和电信环境中已成为标准&#xff0c;应用程序的寻址等…

代码随想录-栈和队列-用栈实现队列

问题描述 题目描述中有说不存在空栈的pop和peek&#xff0c;所以无需判断这个 解析 重点在于思路&#xff0c;代码白给。 要用栈实现队列&#xff0c;肯定是两个栈才可以。一个做入队操作&#xff0c;一个做出队操作。 首先入队简单&#xff0c;往栈里加就完事了。 出队复…

【设计模式】结构型模式(四):组合模式、享元模式

《设计模式之结构型模式》系列&#xff0c;共包含以下文章&#xff1a; 结构型模式&#xff08;一&#xff09;&#xff1a;适配器模式、装饰器模式结构型模式&#xff08;二&#xff09;&#xff1a;代理模式结构型模式&#xff08;三&#xff09;&#xff1a;桥接模式、外观…

轻型民用无人驾驶航空器安全操控------理论考试多旋翼部分笔记

官网&#xff1a;民用无人驾驶航空器综合管理平台 (caac.gov.cn) 说明&#xff1a;一是法规部分&#xff1b;二是多旋翼部分 本笔记全部来源于轻型民用无人驾驶航空器安全操控视频讲解平台 目录 官网&#xff1a;民用无人驾驶航空器综合管理平台 (caac.gov.cn) 一、轻型民用无人…

【leetcode练习·二叉树】用「分解问题」思维解题 I

本文参考labuladong算法笔记[【强化练习】用「分解问题」思维解题 I | labuladong 的算法笔记] 105. 从前序与中序遍历序列构造二叉树 | 力扣 | LeetCode | 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵…

深入解析四种核心网络设备:集线器、桥接器、路由器和交换机

计算机网络系列课程《网络核心设备》 在现代网络技术中&#xff0c;集线器、桥接器、路由器和交换机扮演着至关重要的角色。本文&#xff0c;将深入探讨这四种设备的功能、工作原理及其在网络架构中的重要性。 集线器&#xff1a;基础网络连接设备 集线器&#xff08;Hub&…

宏景eHR uploadLogo.do 任意文件上传致RCE漏洞复现

0x01 产品简介 宏景eHR人力资源管理软件是一款专为复杂单组织或多组织客户设计的人力资源管理软件,融合了最新的互联网技术和先进的人力资源管理理念和实践。宏景eHR软件支持B/S架构,特别适合集团化管理和跨地域使用。它提供了全面的人力资源管理功能,包括人员、组织机构、…

ssm基于JAVA的网上订餐管理系统+vue

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码看文章最下面 需要定制看文章最下面 目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容…

NVR设备ONVIF接入平台EasyCVR私有化部署视频平台如何安装欧拉OpenEuler 20.3 MySQL

在当今数字化时代&#xff0c;安防视频监控系统已成为保障公共安全和个人财产安全的重要工具。NVR设备ONVIF接入平台EasyCVR作为一款功能强大的智能视频监控管理平台&#xff0c;它不仅提供了视频远程监控、录像、存储与回放等基础功能&#xff0c;还涵盖了视频转码、视频快照、…

测试网空投进行中 — 全面了解 DePIN 赛道潜力项目 ICN Protocol 及其不可错过的早期红利

随着云计算技术的飞速发展&#xff0c;越来越多的企业和个人对云服务的需求变得多样化且复杂化。然而&#xff0c;传统的中心化云服务平台&#xff08;如AWS、微软Azure等&#xff09;往往存在着高成本、数据隐私保护不足以及灵活性差等问题。 为了解决这些挑战&#xff0c;Imp…

CulturalBench :一个旨在评估大型语言模型在全球不同文化背景下知识掌握情况的基准测试数据集

2024-10-04&#xff0c;为了提升大型语言模型在不同文化背景下的实用性&#xff0c;华盛顿大学、艾伦人工智能研究所等机构联合创建了CulturalBench。这个数据集包含1,227个由人类编写和验证的问题&#xff0c;覆盖了包括被边缘化地区在内的45个全球区域。CulturalBench的推出&…