转载请表明出处,本人邮箱: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*);__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的入口函数
    
原文:http://blog.csdn.net/lixiangminghate/article/details/46573383