转载请表明出处,本人邮箱:[email protected] 可能可以获得完整审计源码~
随着近年来虚拟化技术飞速发展,使用虚拟化工具的人数日趋增加,同时孕育了大量相关产业。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的入口函数