协程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'; // 0x41
    char *h = p - 8;
    printf("%d\n", malloc_usable_size(p));                  // 104
    printf("%d\n", *((int *)h) & ~((0x1) | (0x2) | (0x4))); // 112
    printf("%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 demo4

0000000000401122 <main>:
  401122:       55                      push   %rbp
  401123:       48 89 e5                mov    %rsp,%rbp
  401126:       48 83 ec 10             sub    $0x10,%rsp
  40112a:       c7 45 fc 13 00 00 00    movl   $0x13,-0x4(%rbp)
  401131:       8b 45 fc                mov    -0x4(%rbp),%eax
  401134:       89 c6                   mov    %eax,%esi
  401136:       bf 04 20 40 00          mov    $0x402004,%edi
  40113b:       b8 00 00 00 00          mov    $0x0,%eax
  401140:       e8 eb fe ff ff          callq  401030 <printf@plt>
  401145:       b8 00 00 00 00          mov    $0x0,%eax
  40114a:       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)
{
    // 如果没初始化则初始化hook
    if (!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");

    // 如果没有任务,则调用真正的sleep
    if (!tk)
        return sleep_f(seconds);

    // 挂起当前协程, 并在指定时间后自动唤醒
    Processer::Suspend(std::chrono::seconds(seconds));
    Processer::StaticCoYield();
    return 0;
}

其中initHook中:

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL中,GROUP BY 分组函数

文章目录 示例查询&#xff1a;按性别分组统计每组信息示例查询&#xff1a;按性别分组显示详细信息示例查询&#xff1a;按性别分组并计算平均年龄,如果你还想统计每个性别的平均年龄&#xff0c;可以结合AVG()函数&#xff1a;说明 示例查询&#xff1a;按性别分组统计每组信…

免费数据集网站

1、DataSearch https://datasetsearch.research.google.comhttp://DataSearch 2、FindData findata-科学数据搜索引擎https://www.findata.cn/ 3、Kaggle Kaggle: Your Machine Learning and Data Science CommunityKaggle is the world’s largest data science community …

十二:java web(4)-- Spring核心基础

目录 创建项目 Spring 核心基础 Spring 容器 Spring 容器的作用 Spring 容器的工作流程 Bean Bean 的生命周期 IOC&#xff08;控制反转&#xff09;与依赖注入&#xff08;DI&#xff09; 控制反转的概念 依赖注入的几种方式&#xff08;构造器注入、Setter 注入、接…

MybatisPlus入门(八)MybatisPlus-DQL编程控制

一、字段映射与表名映射 数据库表和实体类名称一样自动关联&#xff0c;数据库表和实体类有部分情况不一样。 问题一&#xff1a;表名与编码开发设计不同步&#xff0c;表名和实体类名称不一致。 解决办法&#xff1a; 在模型类上方&#xff0c;使用TableName注解&#xf…

亚信安全新一代WAF:抵御勒索攻击的坚固防线

近年来&#xff0c;勒索攻击已成为黑客的主要攻击手段。新型勒索攻击事件层出不穷&#xff0c;勒索攻击形势愈发严峻&#xff0c;已经对全球制造、金融、能源、医疗、政府组织等关键领域造成严重危害。如今&#xff0c;勒索攻击手段日趋成熟、攻击目标愈发明确&#xff0c;模式…

代谢组数据分析(二十一):通过MetaboAnalystR标准化构建sPLSDA预测模型

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍MetaboAnalystR标准化sPLSDA分析安装需要的R包加载R包导入数据MetaboAnalystR标准化数据初始化数据清洗数据补足数据过滤数据标准化导出结果sPLSDA分析导入数据数据预处理PCA分析PL…

《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明

参考 《element plus 使用 icon 图标(两种方式)》使用 icon 升级 Vue2 升级 Vue3 项目时&#xff0c;遇到命名时的实心与空心点差异&#xff01; ElementUI&#xff1a; 实心是 el-icon-more空心是 el-icon-more-outline ElementPlus&#xff1a; 实心是 el-icon-more-fill…

Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在数字化时…

用 Python 写了一个天天酷跑(附源码)

Hello&#xff0c;大家好&#xff0c;给大家说一下&#xff0c;我要开始装逼了 这期写个天天酷跑玩一下叭&#xff01; 制作一个完整的“天天酷跑”游戏涉及很多方面&#xff0c;包括图形渲染、物理引擎、用户输入处理、游戏逻辑等。由于Python是一种高级编程语言&#xff0c;…

《重学Java设计模式》之 原型模式

原型模式主要解决的问题就是创建重复对象&#xff0c;而这部分对象内容本身比较复杂&#xff0c;生成过程可能从库或者RPC接口中获取数据的耗时较长&#xff0c;因此采用克隆的方式节省时间。 案例&#xff1a;上机考试抽题&#xff0c;要求打乱题目、答案数据 工厂结构 选择题…

DMFLDR数据载入使用实践

1、DMFLDR概述 1.1DMFLDR功能介绍 dmfldr&#xff08;DM Fast Loader&#xff09;是 DM 提供的快速数据装载命令行工具。用户通过使用 dmfldr 工具能够把按照一定格式 排序的文本数据以简单、快速、高效的方式载入到 DM 数据库中&#xff0c;或把 DM 数据库中的数据按照一定格…

C#笔记 —— 事件

事件的语法 访问修饰符 event 委托类型 事件名&#xff1b; 例&#xff1a; public event Action myEvent; 事件的使用 事件的使用跟委托基本上一模一样&#xff0c; 1.但是事件不能在类外部直接赋值&#xff0c;只能使用 或 - 添加或删除函数&#xff1b; 2.事件不能在类…

传统RAG流程;密集检索器,稀疏检索器:中文的M3E

目录 传统RAG流程 相似性搜索中:神经网络的密集检索器,稀疏检索器 密集检索器 BGE系列模型 text-embedding-ada-002模型 M3E模型 稀疏检索器 示例一:基于TF-IDF的稀疏检索器 示例二:基于BM25的稀疏检索器 稀疏检索器的特点与优势 传统RAG流程 相似性搜索中:神经…

解决职业摔跤手分类问题的算法与实现

解决职业摔跤手分类问题的算法与实现 引言问题定义算法设计二分图判定算法摔跤手划分算法伪代码C代码示例算法分析时间复杂度空间复杂度结论引言 在职业摔跤界,摔跤手通常被分为“娃娃脸”(“好人”)型和“高跟鞋”(“坏人”)型。在任意一对摔跤手之间,都有可能存在竞争关系…

CentOS系统查看CPU、内存、操作系统等信息

Linux系统提供了一系列命令可以用来查看系统硬件信息&#xff0c;如CPU的物理个数、核数、逻辑CPU数量、内存信息和操作系统版本。 查看物理CPU、核数和逻辑CPU 在多核、多线程的系统中&#xff0c;了解物理CPU个数、每个物理CPU的核数和逻辑CPU个数至关重要。超线程技术进一步…

Elasticsearch常用接口_添加数据

插入es数据&#xff1a;_index/_type/ POST { "tabTitle": "森图表_test", "chtTabTitle": "森图表_test", "status": 0 } 注意&#xff1a;Elasticsearch 6.0.0及更高版本中&#xff0c;索引只能包含一个映射类型

【SpringBoot】18 上传文件到数据库(Thymeleaf + MySQL)

Git仓库 https://gitee.com/Lin_DH/system 介绍 使用 Thymeleaf 写的页面&#xff0c;将&#xff08;txt、jpg、png&#xff09;格式文件上传到 MySQL 数据库中。 依赖 pom.xml <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --><depende…

数据库_SQLite3

下载 1、更新软件源&#xff1a; sudo apt-get update 2、下载SQLite3&#xff1a; sudo apt-get install sqlite3 3、验证&#xff1a; sqlite3启动数据库&#xff0c;出现以下界面代表运行正常。输入 .exit 可以退出数据库 4、安装sqlite3的库 sudo apt-get install l…

全星魅-物联网定位终端-北斗定位便携终端-北斗有源终端

在当今快速发展的物流运输行业中&#xff0c;精准定位与实时监控已成为确保货物安全与高效运输的关键因素。为了满足这一需求&#xff0c;QMCZ10作为一款集4G&#xff08;LTE Cat1&#xff09;通讯技术与智能定位功能于一体的终端产品&#xff0c;应运而生。它不仅具备普通定位…

微服务系列六:分布式事务与seata

目录 实验环境说明 前言 一、分布式事务问题与策略 1.1 分布式事务介绍 1.2 分布式事务解决策略分析 二、分布式事务解决方案 Seata 2.1 认识Seata 2.2 Seata的工作原理 2.3 部署Seata微服务 2.3.1 准备数据库表 2.3.2 准备配置文件 2.3.3 docker部署 2.4 微服务集…