虚拟机自省与libvmi库学习

田老师说和乌云老师合作的项目,当然要看乌云老师的论文啦。

文章摘要:通过检测虚拟机内部的隐藏文件,检测工具可以及时判断虚拟机是否受到攻击.传统的文件检测工具驻留在被监视虚拟机中,容易遭到恶意软件的攻击.基于虚拟机自省原理,设计并实现一种模块化的虚拟机文件检测方法 FDM.FDM借助操作系统内核知识,解析虚拟机所依存的物理硬件,构建虚拟机文件语义视图,并通过与内部文件列表比较来发现隐藏文件.FDM将硬件状态解析和操作系统语义信息获取以不同模块实现,不仅具备虚拟机自省技术的抗干扰性,还具备模块化架构的可移植性与高效性.实验结果表明,FDM能够准确快速地检测出虚拟机内部的隐藏文件。

虚拟机自省

参考链接:

http://www.chinacloud.cn/upload/2016-02/16020410023813.pdf
http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CJFQ&dbname=CJFDLAST2016&filename=RJXB201606004&v=MTEyOTQ5RllJUjhlWDFMdXhZUzdEaDFUM3FUcldNMUZyQ1VSTEtmWmVSckZ5RG5VTDdOTnlmVGJMRzRIOWZNcVk=
http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CMFD&dbname=CMFD201501&filename=1014064067.nh&v=MzA2MTJxSkViUElSOGVYMUx1eFlTN0RoMVQzcVRyV00xRnJDVVJMS2ZaZVJyRnlEblVMek9WRjI2R3JPK0d0SEs=


虚拟机自省架构

libvmi库

官网:http://libvmi.com/
参考:https://github.com/libvmi/libvmi

此次项目主要依赖于libvmi库,libvmi库就是一个c库,它提供了对正在运行中的底层虚拟机的运行细节进行监视的功能,监视的功能是由观察内存细节,陷入硬件事件和读取CPU寄存器来完成的。这种方式被称作虚拟机自省(virtual machine introspection)。

主要要搞清楚他的内存自省功能,内存自省能允许用户从dom0监控(也就是读取内存数据)以及控制(也就是改写内存数据)操作系统。

上图是通过libvmi获取内核符号的流程,主要有以下过程组成:

  1. 应用程序请求查看内核符号。
  2. libvmi通过系统的System.map获取内核符号的虚拟地址。
  3. 找到虚拟地址所对应的内核页目录,并获取对应的页表。
  4. 通过页表找到正确的数据页。
  5. 数据页被返回给libvmi。
  6. libvmi将数据返回给vmi应用程序。

安装:

https://libvmi.wordpress.com/

例子

获取虚拟机所有进程,关键是先根据system_map拿到current_task(当前运行进程),就可以顺藤摸瓜,连根拔起。

example_list.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <stdio.h>
#include <inttypes.h>

#include <libvmi/libvmi.h>

int main (int argc, char **argv)
{
vmi_instance_t vmi;
addr_t list_head = 0, cur_list_entry = 0, next_list_entry = 0;
addr_t current_process = 0;
char *procname = NULL;
vmi_pid_t pid = 0;
unsigned long tasks_offset = 0, pid_offset = 0, name_offset = 0;
status_t status;

/* this is the VM or file that we are looking at */
if (argc != 2) {
printf("Usage: %s <vmname>\n", argv[0]);
return 1;
} // if

char *name = argv[1];

/* initialize the libvmi library */
if (VMI_FAILURE ==
vmi_init_complete(&vmi, name, VMI_INIT_DOMAINNAME, NULL,
VMI_CONFIG_GLOBAL_FILE_ENTRY, NULL, NULL)) {
printf("Failed to init LibVMI library.\n");
return 1;
}

/* init the offset values */
if (VMI_OS_LINUX == vmi_get_ostype(vmi)) {
if ( VMI_FAILURE == vmi_get_offset(vmi, "linux_tasks", &tasks_offset) )
goto error_exit;
if ( VMI_FAILURE == vmi_get_offset(vmi, "linux_name", &name_offset) )
goto error_exit;
if ( VMI_FAILURE == vmi_get_offset(vmi, "linux_pid", &pid_offset) )
goto error_exit;
} else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)) {
if ( VMI_FAILURE == vmi_get_offset(vmi, "win_tasks", &tasks_offset) )
goto error_exit;
if ( VMI_FAILURE == vmi_get_offset(vmi, "win_pname", &name_offset) )
goto error_exit;
if ( VMI_FAILURE == vmi_get_offset(vmi, "win_pid", &pid_offset) )
goto error_exit;
} else if (VMI_OS_FREEBSD == vmi_get_ostype(vmi)) {
tasks_offset = 0;
if ( VMI_FAILURE == vmi_get_offset(vmi, "freebsd_name", &name_offset) )
goto error_exit;
if ( VMI_FAILURE == vmi_get_offset(vmi, "freebsd_pid", &pid_offset) )
goto error_exit;
}

/* 挂起虚拟机,获取内存映像,别忘了解挂起 */
if (vmi_pause_vm(vmi) != VMI_SUCCESS) {
printf("Failed to pause VM\n");
goto error_exit;
}
char *name2 = vmi_get_name(vmi);
vmi_mode_t mode;

if (VMI_FAILURE == vmi_get_access_mode(vmi, NULL, 0, NULL, &mode))
goto error_exit;

if ( VMI_FILE != mode ) {
uint64_t id = vmi_get_vmid(vmi);

printf("Process listing for VM %s (id=%"PRIu64")\n", name2, id);
} else {
printf("Process listing for file %s\n", name2);
}
free(name2);

os_t os = vmi_get_ostype(vmi);

/* get the head of the list */
if (VMI_OS_LINUX == os) {
if ( VMI_FAILURE == vmi_translate_ksym2v(vmi, "init_task", &list_head) )
goto error_exit;

list_head += tasks_offset;
} else if (VMI_OS_WINDOWS == os) {

// find PEPROCESS PsInitialSystemProcess
if (VMI_FAILURE == vmi_read_addr_ksym(vmi, "PsActiveProcessHead", &list_head)) {
printf("Failed to find PsActiveProcessHead\n");
goto error_exit;
}
} else if (VMI_OS_FREEBSD == vmi_get_ostype(vmi)) {
if ( VMI_FAILURE == vmi_translate_ksym2v(vmi, "allproc", &list_head) )
goto error_exit;
}

cur_list_entry = list_head;
if (VMI_FAILURE == vmi_read_addr_va(vmi, cur_list_entry, 0, &next_list_entry)) {
printf("Failed to read next pointer in loop at %"PRIx64"\n", cur_list_entry);
goto error_exit;
}

if (VMI_OS_FREEBSD == vmi_get_ostype(vmi)) {
list_head = 0;
status = vmi_read_addr_va(vmi, cur_list_entry, 0, &cur_list_entry);
if (status == VMI_FAILURE) {
printf("Failed to read next pointer in loop at %"PRIx64"\n", cur_list_entry);
goto error_exit;
}
}

/* 链式结构打印虚拟机进程链表 */
while (1) {

current_process = cur_list_entry - tasks_offset;
vmi_read_32_va(vmi, current_process + pid_offset, 0, (uint32_t*)&pid);

procname = vmi_read_str_va(vmi, current_process + name_offset, 0);

if (!procname) {
printf("Failed to find procname\n");
goto error_exit;
}

/*打印当前运行进程地址 */
printf("[%5d] %s (struct addr:%"PRIx64")\n", pid, procname, current_process);
if (procname) {
free(procname);
procname = NULL;
}

if (VMI_OS_FREEBSD == os && next_list_entry == list_head) {
break;
}

cur_list_entry = next_list_entry;
status = vmi_read_addr_va(vmi, cur_list_entry, 0, &next_list_entry);
if (status == VMI_FAILURE) {
printf("Failed to read next pointer in loop at %"PRIx64"\n", cur_list_entry);
goto error_exit;
}

if (VMI_OS_WINDOWS == os && next_list_entry == list_head) {
break;
} else if (VMI_OS_LINUX == os && cur_list_entry == list_head) {
break;
}
};

error_exit:

vmi_resume_vm(vmi);

vmi_destroy(vmi);

return 0;
}

```

本文标题:虚拟机自省与libvmi库学习

文章作者:Hengliy

发布时间:2017年09月15日 - 14:09

最后更新:2018年02月24日 - 15:02

原始链接:http://hengliy.github.io/2017/09/15/虚拟机自省与LIBVMI库学习/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。