码迷,mamicode.com
首页 > 其他好文 > 详细

LD_PRELOAD应用--基于libvirt审计(上)

时间:2015-06-20 15:49:02      阅读:222      评论:0      收藏:0      [点我收藏+]

标签:linux   虚拟化   

    转载请表明出处,本人邮箱:562703006@qq.com 可能可以获得完整审计源码~

    随着近年来虚拟化技术飞速发展,使用虚拟化工具的人数日趋增加,同时孕育了大量相关产业。libvirt虚拟化审计就是在这个背景下产生的。

    libvirt提供了统一抽象的虚拟化管理平台---libvirtd服务器,通过他可以与主流的虚拟化平台交互,例如QEMU/KVM等, 将用户虚拟机请求发送给特定具体的虚拟化介质,由该虚拟化介质实现虚拟机的操作。同时libvirt也向用户提供了虚拟化管理API,让用户与libvirtd服务器交互,其中virsh/virt-manager就是基于API开发的客户端。

    本文的主题,libvirt审计就是分别以LD_PRELOAD截获libvirtd注册虚拟化驱动实现服务器审计;截获libvirt API实现客户端审计。本文先简单的介绍客户端审计的实现。

    客户端审计的流程如下:

    1).查看libvirt.so导出的函数

    2).自己实现同名函数(包括相同的参数/返回值),并在同名函数中通过dlopen/dlsym获得libvirt.so中导出符号的地址

    3).记录参数和拦截操作

    4).对于合法的操作,将参数传递给原函数;对于非法的操作,直接返回错误值

    5).获得原函数的返回值,记录;

    以下是对以上步骤的具体实现:

    1).查看客户端工具virsh依赖的libvirt的路径及导出的符号:

    >which virsh
    /usr/bin/virsh
    >ldd /usr/bin/virsh
    libvirt.so=>/usr/lib64/libvirt.so.0
    >nm -D /usr/lib64/libvirt.so.0
    T virConnectOpen
    T virDomainSuspend

     从上面的命令来看,virsh依赖的库函数为/usr/lib64/libvirt.so.0,该库函数导出了众多符号,这里仅列举和实现virConnectOpen/virDomainSuspend

    2).查看libvirt/virsh.c源码发现:当客户端用默认方式连接libvirtd时,会调用virConnectOpen获得连接句柄,以后客户端对libvirtd的操作都以此作为标识;当客户端需要暂定虚拟机的运行,则会调用virDomainSuspend。他们的接口声明为:

int virDomainSuspend(virDomainPtr domain);
virConnectPtr virConnectOpen (const char *URI);

由于virConnectPtr和virDomainPtr的类型都已在/usr/include/libvirt/libvirt.h中作出声明,因此只需直接引用,不用做特别的操作。我们要做的是:

    2-1):申明函数指针,定义该指针变量,用于存放libvirt.so中导出的函数;

    2-2):dlopen/dlsym获得函数地址;

#include <dlfcn.h>
#include "/usr/include/libvirt/libvirt.h"

#define LIBVIRTPATH "/usr/lib64/libvirt.so.0"
#define DETOURLOGPATH "/root/detour.log"

#define ClearShareBuff() do{     memset(auditParam.auditLogContext,0,MAX_CONTENT_LEN); }while(0); 
#define WriteShareBuff() do{     (*auditCBFunc)(&auditParam); }while(0); 
//宏框架
#define detour_FILTER {
#define detour_FILTEREND }

#define detour_CALLORIG {
#define detour_CALLORIGEND }

#define detour_AUDIT {
#define detour_AUDITEND }

#define detour_PROLOG(addr,type) do{     if(addr!=NULL)         break;     addr = (type)dlsym(dllHnd, __func__);     assert(addr != NULL); }while(0); 
#define detour_EPILOG(res) do{     return res; }while(0); 
<pre name="code" class="cpp">typedef int (*detour_virDomainSuspend)(virDomainPtr);
typedef virConnectPtr (*detour_virConnectOpen)(const char*);

//在自己的.so文件中申明同名virConnectOpen,当so文件被LD_PRELOAD注入到virsh后,会覆盖原有的virConnectOpen
//如此,virsh调用virConnectOpen连接libvirtd时,会进入我们实现的代码中
virConnectPtr virConnectOpen (const char *URI)
{
    virConnectPtr res = NULL;
    injectValidProc = 1;
//调用dlsym 获得真正的virConnectOpen的地址
    detour_PROLOG(virConnectOpenAddr,detour_virConnectOpen);

//实现api拦截,对于不合法的用户,可以直接在此返回
    detour_FILTER
    detour_FILTEREND
    
//调用真正的virConnectOpen函数,并把参数传递给它
    detour_CALLORIG
    res = (*virConnectOpenAddr)(URI);
    detour_CALLORIGEND
    
//开始审计,记录参数并输出到日志
    detour_AUDIT
    ClearShareBuff();
    sprintf(auditParam.auditLogContext,
                    "<methodResponse><event>%s</event><state>%d</state><param><uri>%s</uri></param></methodResponse>\n",
                    "virConnectOpen",(res!=NULL)?1:0,URI);
    WriteShareBuff();
    detour_AUDITEND
    
    detour_EPILOG(res);
}

int virDomainSuspend(virDomainPtr domain)
{
    int res = 0;
    char name[4096]={0};
    
    detour_PROLOG(virDomainSuspendAddr,detour_virDomainSuspend);
    
    detour_FILTER
    detour_FILTEREND
    
    detour_CALLORIG
    res = (*virDomainSuspendAddr)(domain);
    detour_CALLORIGEND
    
    detour_AUDIT
    ClearShareBuff();
    
    GetDomainName(domain);
    sprintf(auditParam.auditLogContext,
                    "<methodResponse><event>%s</event><state>%d</state><param>name:%s</param></methodResponse>\n",
                    "virDomainSuspend",res,name);
        
    WriteShareBuff();
    detour_AUDITEND
    
    detour_EPILOG(res);
}


以上实现了基本的功能,但还有一些初始化个功能尚待完成:

__attribute ((constructor)) void detour_init(void)
{
	char logPath[4096] = {0};
	pthread_t tid; 
	//sprintf(logPath,"%s%s-%d.log",DETOURLOGPATH,"detour",getpid());
	
	fp = fopen(DETOURLOGPATH, "a+");
	dllHnd = dlopen(LIBVIRTPATH,RTLD_LAZY|RTLD_GLOBAL);
	assert(fp != NULL);
	assert(dllHnd != NULL);
	auditInitilize();
	auditParam.fp = fp;
	auditCBFunc = audit2LogFile;

	return;
}
__attribute ((destructor)) void detour_fini(void)
{
	dlclose(dllHnd);
	fclose(fp);
	free(auditParam.auditLogContext);
	return;
}
因为dlsym需要指定库的句柄,程序中大量使用了这个句柄。每次都打开关闭无异于是件麻烦事,因此在so程序的入口函数中打开这个句柄并存放在全局变量中。

可以通过strace跟踪virsh的启动情况,可以观察到:

>export LD_PRELOAD=/root/Desktop/libdetour.so
>strace virsh
execv(/usr/bin/virsh);
mmap(/root/Desktop/libdetour.so);
系统首先载入virsh的镜像,然后依次载入virsh依赖的so文件,最后运行virsh,并与virsh!init函数中执行libdetour.so的入口函数
    以后凡是virsh的连接和挂起虚拟机的操作都可以在/root/detour.log文件中找到记录。


   

LD_PRELOAD应用--基于libvirt审计(上)

标签:linux   虚拟化   

原文地址:http://blog.csdn.net/lixiangminghate/article/details/46573383

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!