Reprint please indicate the source, I e-mail: [email protected] may be able to obtain a full audit source ~
With the rapid development of virtualization technology in recent years, the number of people using virtualization tools has increased, and has spawned a large number of related industries. The Libvirt virtualization audit was created in this context.
Libvirt provides a unified and abstract virtualization management platform---LIBVIRTD server, through which he can interact with the mainstream virtualization platform, such as QEMU/KVM, to send user virtual machine requests to specific virtualized media, and the virtual machines are operated by the virtualized media. Libvirt also provides the user with a virtualization Management API that allows users to interact with the LIBVIRTD server, where Virsh/virt-manager is the client based on API development.
The subject of this article, Libvirt Audit is to Ld_preload intercept LIBVIRTD registered virtualization driver to implement server audit, Intercept Libvirt API to implement client audit. This article first introduces the implementation of client audit.
The process for client audits is as follows:
1). View libvirt.so Exported functions
2). Implement the same name function (including the same parameter/return value) and get the address of the exported symbol in libvirt.so by Dlopen/dlsym in the same name function
3). Logging parameters and interception operations
4). For legitimate operations, pass parameters to the original function, and return the error value directly for illegal operations
5). Obtain the return value of the original function, record;
The following are specific implementations of the above steps:
1). View the path of the Libvirt that the client tool Virsh depends on and the exported symbols:
>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
From the above command, Virsh relies on the library function is/usr/lib64/libvirt.so.0, the library function exported a number of symbols, here only to enumerate and implement Virconnectopen/virdomainsuspend
2). View LIBVIRT/VIRSH.C Source discovery: When the client connects LIBVIRTD by default, Virconnectopen is called to get the connection handle, which is used by the client to identify the LIBVIRTD operation, and when the client needs to run the virtual machine tentatively, The virdomainsuspend will be called. Their interfaces are declared as:
int virdomainsuspend (virdomainptr domain); virconnectptr virconnectopen (const char *uri);
Since the types of virconnectptr and virdomainptr have been declared in/usr/include/libvirt/libvirt.h, it is only necessary to refer directly to them without special action. What we are going to do is:
2-1): Declare the function pointer, define the pointer variable, for storing the function exported in libvirt.so;
2-2):d Lopen/dlsym to obtain the address of the function;
#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); Macro Framework # 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*);
Assert the same name Virconnectopen in your own. So file, overwriting the original Virconnectopen when the so file is injected into the Virsh ld_preload
So, when Virsh calls Virconnectopen to connect LIBVIRTD, it goes into the code we implement.
Virconnectptr virconnectopen (const char *uri)
{
virconnectptr res = NULL;
Injectvalidproc = 1;
Call Dlsym to get the address of the real Virconnectopen
Detour_prolog (Virconnectopenaddr,detour_virconnectopen);
Implement API interception, for illegal users, you can return directly here
Detour_filter
Detour_filterend
Call the real Virconnectopen function and pass the arguments to it
Detour_callorig
res = (*VIRCONNECTOPENADDR) (URI);
Detour_callorigend
Start auditing, record parameters and output to log
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);
}
The above implements the basic functionality, but there are still some initialization features to be completed:
__attribute ((constructor)) void Detour_init (void) {char logpath[4096] = {0};p thread_t tid;//sprintf (LogPath, "%s%s-% D.log ", Detourlogpath," Detour ", Getpid ()); fp = fopen (Detourlogpath," A + ");d llhnd = 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;}
Because Dlsym needs to specify a handle to the library, the handle is used extensively in the program. Opening the closure every time is a hassle, so open the handle in the entry function of the so program and store it in the global variable.
You can track the startup of Virsh by Strace, and you can observe:
>export ld_preload=/root/desktop/libdetour.so>strace virshexecv (/usr/bin/virsh); Mmap (/root/Desktop/ libdetour.so);
The system first loads the Virsh image, then loads the Virsh dependent so file, and finally runs Virsh, and executes Libdetour.so's entry function with the Virsh!init function
After all, the Virsh connection and the operation of suspending the virtual machine can be found in the/root/detour.log file.
Ld_preload application--based on Libvirt audit (top)