美文网首页
linux简单hook

linux简单hook

作者: 烨哥 | 来源:发表于2023-04-04 23:48 被阅读0次

linux hook原理

  • 在 Linux 系统中,要进行函数钩子需要使用动态链接库(Dynamic Link Library,DLL)技术。动态链接库是指在应用程序运行期间才被载入的库,它允许程序在运行时动态地连接到某个库文件上,以执行其中的函数。因为动态链接库是在运行时才被载入的,所以可以在运行期间修改它们的代码,实现函数的替换。
  • 实现hook的流程一般是:
    1. 编写一个动态链接库,包含需要钩子的函数以及它们的替换函数。
    2. 在动态链接库中,使用 LD_PRELOAD 环境变量指定要预先加载的库。
    3. 在需要进行函数钩子的应用程序中,重载需要钩子的函数,使其调用动态链接库中的替换函数,而不是原始的函数。
    4. 在动态链接库中的替换函数中,执行预定的操作,然后调用原始函数。

一个简单通过LD_PRELOAD 注入的代码的例子

  • 创建一个简单的function.c文件
#include <stdio.h>
void my_printf() {
    printf("my_printf in function\n");
}
  • 这段代码只进行了一个简单的打印操作
  • 创建一个app程序main.c 调用这个函数
#include <stdio.h>
void my_printf();

int main()
{
    my_printf();
    return 0;
}
  • 我们进行编译运行
gcc -shared -fPIC -o libfunction.so function.c 
gcc -o app main.c -L. -lfunction
export LD_LIBRARY_PATH=.
./app
  • 这样我们得到一个打印 my_printf in function
  • 现在再创建一个注入的so hook.c
#include <stdio.h>
void my_printf() {
    printf("my_printf in hook\n");
}
  • 分别进行编译和运行
gcc -shared -fPIC -o libhook.so hook.c 
LD_PRELOAD=./libhook.so ./app
  • 得到的结果为 my_printf in hook
  • 这时候我们就完成最简单的函数替换

hook原函数

  • 下面我们将使用修改got表来实现替换到原函数
  • 可执行程序在调用一个so的的函数时候首先是访问plt的重定向表。然后转到got里面取出位置。
  • 首先我们需要获取三个重要的信息
  • 字符串表:包含以空字符结尾的字符序列,使用这些字符来描绘符号和节名;
  • 符号表:保存了一个程序在定位和重定位时需要的定义和引用的信息;
  • plt表:保存了需要重定位的符号的信息;
  • 然后我们遍历plt表根据plt的信息找到对应符号表信息和字符串表信息(主要是找出函数名字)。比对是否是我们需要替换的函数名字。如果是需要替换的名字那么根据plt里面保存的偏移信息找到got项。替换里面的地址为我们函数的地址。完成调用函数的替换
  #include <stdio.h>
#define __USE_GNU
#include<dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <sys/mman.h>
#include <elf.h>
#include <link.h>

 void (*orig_func)();

void my_printf_hook() {
    printf("my_printf in hook on enter\n");
    orig_func();
    printf("my_printf in hook on level\n");
}
const Elf64_Dyn* find_dyn(const Elf64_Dyn* dyn, Elf64_Sxword tag) {
    while (dyn->d_tag != DT_NULL) {
        if (dyn->d_tag == tag) {
            return dyn;
        }
        dyn++;
    }
    return NULL;
}
void do_replace_function() {
    // 打开我们的可执行文件
    void* handle = dlopen(RTLD_DEFAULT, RTLD_LAZY);
    if (handle == NULL) {
        printf("open so error %s", dlerror());
        return;
    }
    struct link_map* map = NULL;
    //获取可执行文件的dlinfo
    if (dlinfo(handle, RTLD_DI_LINKMAP, &map) != 0) {
        printf("dlinfo error");
    }
    const char* plt_addr_base = (const char *)map->l_addr;
    const Elf64_Dyn* dyn = NULL;
    // 获取符号表信息
    //.dynsym
    dyn = find_dyn(map->l_ld, DT_SYMTAB);
    if (dyn == NULL) {
        printf("failed to find DT_SYMTAB\n");
        return;
    }
    const Elf64_Sym* dyn_systab =(const Elf64_Sym*) dyn->d_un.d_ptr;
    //获取字符串表
    //.dynstr
    dyn = find_dyn(map->l_ld, DT_STRTAB);
    if (dyn == NULL) {
        printf("failed to find DT_STRTAB\n");
        return;
    }
    const char* dyn_strtab = (const char*)dyn->d_un.d_ptr;
    //获取plt
    dyn = find_dyn(map->l_ld, DT_JMPREL);
    if (dyn == NULL) {
        printf("failed to find DT_JMPREL\n");
        return;
    }
    const Elf64_Rela* plt = (const Elf64_Rela*)dyn->d_un.d_ptr;
    //获取plt大小
    dyn = find_dyn(map->l_ld, DT_PLTRELSZ);
    if (dyn == NULL) {
        printf("failed to find DT_PLTRELSZ\n");
        return;
    }
    int plt_cont = dyn->d_un.d_val / sizeof(Elf64_Rela);
    int i = 0;
    size_t len = strlen("my_printf");
    //便利plt
    for ( i = 0; i < plt_cont; i++) {
        const Elf64_Rela* current_plt = plt + i;
        if (ELF64_R_TYPE(current_plt->r_info) != R_X86_64_JUMP_SLOT) {
            continue;
        }
        size_t index = ELF64_R_SYM(current_plt->r_info);//找到符号表的偏移
        size_t index2 = dyn_systab[index].st_name;//站到字符串表的偏移
        const char* name = dyn_strtab + index2;
        if (strncmp("my_printf", name, len) == 0) {
            void** addr = (void **)(plt_addr_base + current_plt->r_offset);//找到这个got表的偏移的位置
            orig_func = (void (*)())(*addr);//把记录的值取出来
            *addr = (void *)my_printf_hook;//新的地址复制过去 完成替换
        }
    }
}

void __attribute__((constructor)) my_init(void) {
    printf("Hello from LD_PRELOAD!\n");
    do_replace_function();
}
  • 编译hook.c 链接执行
  gcc -shared   -fPIC -o libhook.so hook.c  -ldl
  LD_PRELOAD=./libhook.so ./app 
  • 得到结果为
Hello from LD_PRELOAD!
my_printf in hook on enter
my_printf in function
my_printf in hook on level
  • 成功的替换my_printf为my_hook_prinf

相关文章

  • Linux Preload Hook原理与实践

    Preload简介Linux常见Hook技术对比函数调用类型内核模块Hook应用层Inline HookGot表应...

  • 插件化—Hook-代理

    1.什么是Hook? hook英文翻译为钩子。简单来说,hook技术就是劫持应用程序本要执行的对象或者方法,将其替...

  • 08 性能优化-耗电优化-耗电监控

    利用Hook方式监控排查耗电 Java Hook Hook 方案的好处在于使用者接入非常简单,不需要去修改自己的代...

  • hook请求数据

    useEffect不能在内部直接使用异步函数 添加loading,错误码 变成hook 简单的ajax hook ...

  • HOOK 简单防护

    创建项目 创建一个项目1,添加 fishhook.c 和 fishhook.h 拖两个按钮,测试一下,我们的目的是...

  • setHook

    利用 setHook 实现简单的 tag 选择 hook 实现

  • 使用git和码云实现代码的自动部署

    linux python环境 1、安装 pip install web.py 2、新建hook.py 3、vim...

  • React 函数式组件

    简单函数式组件 使用 hook 的函数式组件

  • Fishhook 学习笔记

    一、Fishhook 是什么? 简单来说Fishhook就是hook函数的一种工具,当然它hook的原理和我们熟知...

  • Linux Hook技术(四)

    这一节主要是讲述的是符号节.要怎么才能找到符号节呢,其实只要在上一期讲的遍历节头的时候,判断每一个节类型是不是SH...

网友评论

      本文标题:linux简单hook

      本文链接:https://www.haomeiwen.com/subject/ukcjddtx.html