一直想入门一下虚拟化安全这一块,前一阵一直在研究内核方向的内容,正好这几天也换一换脑子。
本文目标:
1.对 QEMU虚拟机 结构有一个初步的介绍及认识。
2.初识 QOM(Qemu Object Model) 对象模型。
3.通过分析 Blizzard CTF 2017 练习qemu调试等内容。
4.实现 qemu逃逸 弹个 gnome-calculator。
题目:Blizzard CTF 2017
strng虚拟机的源码如下:
https://github.com/rcvalle/blizzardctf2017/blob/master/strng.c
在虚拟机中不论是kvm还是qemu有几个关键的地址。
GVA:guest virtual address(虚拟机中的虚拟地址)
GPA:guest physical address(虚拟机中的物理地址)
HVA:host virtual address(宿主机中的虚拟地址)
HPA: host physical address(宿主机中的物理地址)
这里跟kvm一样。HVA对应GPA。
整体地址的话:从GVA -> GPA -> HVA -> HPA 这样的转换。
而GPA实际是由宿主机进程mmap出来的空间。
而在qemu-kvm架构下,QEMU充当kvm的前端,传递IO。kvm负责做内存以及CPU的虚拟化。
我们使用如下脚本启动 strng 虚拟机。
注意最后一行将22端口映射到宿主机的5555端口。所以我们可以通过:
登录虚拟机。
在 VM escape - QEMU Case Study 中给出了一个 mmu.c 用来做地址转换,其代码如下:
官方文档:https://www.kernel.org/doc/Documentation/vm/pagemap.txt
这个mmu.c是要跑在我们虚拟机里的。
用户名:ubuntu,密码:passw0rd。
编译mmu.c:
然后将其scp到虚拟机里:
可以看到已经有了如下内容
然后sudo运行:
接下来使用qemu attach上去:
首先找出qemu的进程pid,然后attach:
之后我们找到这里:
可以看到对应的就是我们这个字符串的地址:
而 0x7fc0a7e00000
就是我们虚拟机的物理地址为0的位置。
通过源码我们可以看到,strng主要实现了:strng_pmio_xxxx
以及 strng_mmio_xxxx
也就是mmio和pmio这两个东西。
简单来说就是IO设备与内存共享同样的地址空间,以此来进行交互。
内存和I/O设备有各自的地址空间,不共享内存(被隔离)。而通过 in、out、outw,、outl
等指令实现交互。
PCI 是 Peripheral Component Interconnect 的缩写。
Q:什么是PCI设备?
A:符合 PCI 总线标准的设备就被称为 PCI 设备 ,PCI 总线架构中可以包含多个 PCI 设备。图中的 Audio、LAN 都是一个 PCI 设备。PCI 设备同时也分为主设备和目标设备两种,主设备是一次访问操作的发起者,而目标设备则是被访问者。
而在我们的strng虚拟机中:
lspci查看我们的pci设备。再看启动命令,发现有一行:
实际这就是告诉我们,strng是以虚拟机中的一个 pci设备 存在的。
通过阅读源码,我们也可发现确实有一个函数负责pci设备(strng)的注册:pci_strng_register_types()
而在lspci的内容中我们发现了:
这样一行unclassfied的设备。他的设备 id是0x11e9 ,猜测这个就是strng。
根据 https://man7.org/linux/man-pages/man8/lspci.8.html
lspci输出的内容对应的格式如下:[domain:]bus:device.function
lspci -t -v 可以树状显示。
那么 strng
的总线对应的就是 00:03.0
,域为[0000]
通过 lspci -v
我们可以查看显示所有设备的详细信息。
我们可以得到如下两个有用的信息:
接下来,我们可以通过 ls -la /sys/devices/pci0000\:00/0000\:00\:03.0/
查看其对应的 resource
文件。
resource0
对应MMIO空间。
resource1
对应PMIO空间。
从左至右依次是:起始地址、结束地址、标志位。
第一行是MMIO、第二行是PMIO。
虽然是第一次接触qemu-pwn,不过对于著名的QOM模型早已经有所耳闻,于是就跟着QOM模型梳理一下strng的源码,加深一下理解。
Everything in QOM is a device
QOM是QEMU 在C的基础上自己实现的一套面向对象机制 ,负责将device、bus等设备都抽象成为对象。
下面给出一些重要的QOM Terminology(术语)
来源:https://www.linux-kvm.org/images/f/f6/2012-forum-QOM_CPU.pdf
• Type(类型) ‒ Defines class
• Class(类) ‒ Stores static data & virtual method pointers ‒ Lazily initialized from type and, optionally, static data(存储静态数据以及一些函数指针)
• Object(对象) ‒ Stores dynamic data (“chunk of allocated memory”) (储存动态数据)
• Property(属性) ‒ Accessor to dynamic object data ‒ Inspectable via monitor interface(访问对象中的动态数据,通过interface可以进行监控)
在QOM中有几个重要的结构体
https://github.com/qemu/qemu/blob/master/include/qom/object.h
类比到 strng 源码:
可以看到,在 pci_strng_register_types
中首先定义了 TypeInfo strng_info
并对其中一些内容进行赋值。
在代码最后部分,首先调用 type_init()
负责注册。
最终调用了 register_module_init()
主要做了如下事情:
而需要注意的是,由于 __attribute__((constructor))
的存在,这一部分内容作为 构造函数
都将在main函数之前调用。
具体的规则可以看:
C Language Constructors and Destructors with GCC
总结一下:
在QEMU真正启动之前,所有 module_entry
都被插入 init_type_list
。并且已经确定了初始化函数。
而接下来,在qemu启动阶段main函数调用的时候,会对 init_type_list[Module_Init_Type]
链表中的每一个 ModuleEntry
调用其 init
函数。所以对于我们的strng里就是 pci_strng_register_types()
。
pci_strng_register_types()
中首先对设备的一些信息做了初始化,接着调用了:type_register_static(&strng_info)
定义如下:
由type_register_static(&strng_info)
最终调用了 type_register_internal(&strng_info)
至此,这个 ModuleEntry
向 TypeImpl表
中的注册就算完成了。
ObjectClass是所有class的基类,定义如下:
在前面几步中,实际上我们只完成了 Module的注册 ,而没有真正完成 类的初始化 。
接下来我们通过 TypeInfo strng_info
中的 .class_init = strng_class_init
简单看一下 Type(类)的初始化 。
在初始化每一个 type 的时候调用的是: type_initialize
函数。
给出对应的注释后的源码:
注意,此处的我们说的类实际是 类对象 ,对应基类ObjectClass
Object是 类实例对象 ,是所有 类实例对象的基类
在我们初始化了对应的 ObjectClass 后,接下来初始化 Object
Object的创建由 k->realize = pci_testdev_realize;
函数实现,不同于 type 和 class 的构造,Object
是根据需要 创建的。
而一个Object对应着一个我们指定的具体的device 。
比如:
这里的strng。
我们以strng为例,用户可以定义Type的自己的Object结构体,继承自PCIDevice
其初始化由:pci_strng_realize(PCIDevice *pdev, Error **errp)
负责。
其中的 STRNGState 定义如下:
至此 QOM最基本的四个type相关的结构体就介绍完了。
初始化顺序可以认为是:TypeInfo -> TypeImpl -> ObjectClass -> Object(自选)
qemu使用 MemoryRegion 来表示对应的内存空间,相应的每一个 MemoryRegion 都有对应的 MemoryRegionOps 来描述其操做。
比如在strng中举这样一对儿例子:
有点类似于内核中的 file ops。
出了前四个结构体,还有 属性 之类的结构体等等,不过跟我们这里准备看的关系不大,就不赘述了。有兴趣的可以自行了解。
Property 作为属性模板,用于创建属性对象。
同时,属性又分为静态属性与动态属性。
直接把qemu拉到ida里逆向。
然后去搜索strng。
首先将函数中的 opaque参数 进行 Set Ivar type 为 STRNGState * ,注意不是转成 struct *,此时的 STRNGState 已经是typedef后的一个type了。
然后再去structures里找。
在IDA中的 STRNGState 如下:
mmio读的操作,如果size=4,并且addr是4的倍数。
那么取 STRNGState 中regs区域中2addr 的内容返回。即 (®s + 2*addr)返回。
首先 Choice = addr* 1/4
已知我们的 MMIO空间为0到255,总共256大小。
我们先打开对应的 /sys/devices/pci0000\:00/0000\:00\:03.0/resource0
,然后将其以 MAP_SHARED 映射。那么我们可以通过操作这一块映射空间来读写操作mmio空间。
接下来只要addr越界即可。
但是实际调试的过程中,我发现根本就写不上去。
If you are thinking, “oh we have out-of-bound access here because addr is not checked anywhere”, you would be wrong. The PCI device internally checks if the addr you are trying to access is within the boundaries of the MMIO thus only 256 bytes.
PCI设备在内部会检查这个地址是否越界,所以这里的任意写其实无法利用。
这里可以看到:
如果我们可以控制 opaque->addr 那么就相当于控制了下标,可以进行任意读了。
刚刚的pmio_read我们得出结论,如果能控制 opaque->addr ,那么就可以做到任意读取。
(1)而在pmio_write中 当addr为0时,可以直接控制 opaque->addr = val ,也就是说此时我们的 任意读 已经有了。
(2)而当我们能控制 opaque->addr 后,相当于我们可以控制v5,v5 = (opaque->addr>>2),进而如果v5不等于3,可以通过v5来做任意写
(3)当v5等于3时,会对 opaque->rand_r 产生一个调用,第一个参数为 opaque->regs[2]的地址 ,如果我们将regs[2]处的内容写成某个命令。然后任意读rand_r地址,计算libc,任意写 为system,那么就可以实现 任意命令执行 。
总结一下思路:
(1)首先将字符串 “gnome-calculator” 通过MMIO_write写到regs[2]的位置。
(2)通过控制 opaque->addr = val 进行任意读取,读srand的地址,计算libc。
(3)通过任意写改写 rand_r 函数的地址为system,然后执行 (opaque->rand_r)(&opaque->regs[2], 4LL, val); 弹计算器。
对于pmio,我们使用<sys/io.h>中的outl,intl进行读写。
而此时我们的addr就相当于读写的端口port。
如果需要打断点,我们直接 b strng_pmio_read 即可
最终效果
VM escape - QEMU Case Study
Linux用户空间将虚拟地址转化为物理地址
浅谈内存映射I/O(MMIO)与端口映射I/O(PMIO)的区别
浅谈 Linux 内核开发之 PCI 设备驱动
QEMU学习笔记——QOM(Qemu Object Model)
QEMU中KVM的初始化调用路径
KVM Log 1-QEMU Object Module (QOM)
https://github.com/grandemk/qemu_devices/blob/master/hello_tic.c
Guest' processes
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Virtual addr space | |
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
(GVA)
| |
\__ Page Table \__
\ \
| | Guest kernel
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Guest's phy. memory | | | | (GPA)
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| |
\__ \__
\ \
| QEMU process |
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Virtual addr space | | | (HVA)
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| |
\__ Page Table \__
\ \
| |
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
Physical memory | | || (HPA)
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
Guest' processes
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Virtual addr space | |
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
(GVA)
| |
\__ Page Table \__
\ \
| | Guest kernel
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Guest's phy. memory | | | | (GPA)
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| |
\__ \__
\ \
| QEMU process |
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
Virtual addr space | | | (HVA)
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| |
\__ Page Table \__
\ \
| |
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
Physical memory | | || (HPA)
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
.
/
qemu
-
system
-
x86_64 \
-
m
1G
\
-
device strng \
-
hda my
-
disk.img \
-
hdb my
-
seed.img \
-
nographic \
-
L pc
-
bios
/
\
-
enable
-
kvm \
-
device e1000,netdev
=
net0 \
-
netdev user,
id
=
net0,hostfwd
=
tcp::
5555
-
:
22
.
/
qemu
-
system
-
x86_64 \
-
m
1G
\
-
device strng \
-
hda my
-
disk.img \
-
hdb my
-
seed.img \
-
nographic \
-
L pc
-
bios
/
\
-
enable
-
kvm \
-
device e1000,netdev
=
net0 \
-
netdev user,
id
=
net0,hostfwd
=
tcp::
5555
-
:
22
ssh ubuntu@
127.0
.
0.1
-
p
5555
ssh ubuntu@
127.0
.
0.1
-
p
5555
-
-
-
[ mmu.c ]
-
-
-
int
fd;
uint32_t page_offset(uint32_t addr)
{
return
addr & ((
1
<< PAGE_SHIFT)
-
1
);
}
uint64_t gva_to_gfn(void
*
addr)
{
uint64_t pme, gfn;
size_t offset;
offset
=
((uintptr_t)addr >>
9
) & ~
7
;
lseek(fd, offset, SEEK_SET);
read(fd, &pme,
8
);
if
(!(pme & PFN_PRESENT))
return
-
1
;
gfn
=
pme & PFN_PFN;
return
gfn;
}
uint64_t gva_to_gpa(void
*
addr)
{
uint64_t gfn
=
gva_to_gfn(addr);
/
/
这里的addr属于gva,获取对应的page frame number
assert
(gfn !
=
-
1
);
return
(gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
/
/
返回对应的GPA
}
int
main()
{
uint8_t
*
ptr;
uint64_t ptr_mem;
fd
=
open
(
"/proc/self/pagemap"
, O_RDONLY);
/
/
通过读取pagemap文件,可以得到进程从虚拟地址到物理地址映射的信息
if
(fd <
0
) {
perror(
"open"
);
exit(
1
);
}
ptr
=
malloc(
256
);
/
/
首先开
256
的空间
strcpy(ptr,
"Where am I?"
);
/
/
将字符串拷贝过去
printf(
"%s\n"
, ptr);
ptr_mem
=
gva_to_gpa(ptr);
/
/
guest virtual mem
-
> guest physicals mem
/
/
此时的ptr_mem对应的是虚拟机里字符串所在的物理内存地址。
printf(
"Your physical address is at 0x%"
PRIx64
"\n"
, ptr_mem);
getchar();
return
0
;
}
-
-
-
[ mmu.c ]
-
-
-
int
fd;
uint32_t page_offset(uint32_t addr)
{
return
addr & ((
1
<< PAGE_SHIFT)
-
1
);
}
uint64_t gva_to_gfn(void
*
addr)
{
uint64_t pme, gfn;
size_t offset;
offset
=
((uintptr_t)addr >>
9
) & ~
7
;
lseek(fd, offset, SEEK_SET);
read(fd, &pme,
8
);
if
(!(pme & PFN_PRESENT))
return
-
1
;
gfn
=
pme & PFN_PFN;
return
gfn;
}
uint64_t gva_to_gpa(void
*
addr)
{
uint64_t gfn
=
gva_to_gfn(addr);
/
/
这里的addr属于gva,获取对应的page frame number
assert
(gfn !
=
-
1
);
return
(gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
/
/
返回对应的GPA
}
int
main()
{
uint8_t
*
ptr;
uint64_t ptr_mem;
fd
=
open
(
"/proc/self/pagemap"
, O_RDONLY);
/
/
通过读取pagemap文件,可以得到进程从虚拟地址到物理地址映射的信息
if
(fd <
0
) {
perror(
"open"
);
exit(
1
);
}
ptr
=
malloc(
256
);
/
/
首先开
256
的空间
strcpy(ptr,
"Where am I?"
);
/
/
将字符串拷贝过去
printf(
"%s\n"
, ptr);
ptr_mem
=
gva_to_gpa(ptr);
/
/
guest virtual mem
-
> guest physicals mem
/
/
此时的ptr_mem对应的是虚拟机里字符串所在的物理内存地址。
printf(
"Your physical address is at 0x%"
PRIx64
"\n"
, ptr_mem);
getchar();
return
0
;
}
root@ubuntu:~
/
strng
root@ubuntu:~
/
strng
The authenticity of host
'[127.0.0.1]:5555 ([127.0.0.1]:5555)'
can't be established.
ECDSA key fingerprint
is
SHA256:zoKoYVjPeMIA7zfSHVgA4
+
Q9bMN1IExTtD0rxTU7cLg.
Are you sure you want to
continue
connecting (yes
/
no)? yes
Warning: Permanently added
'[127.0.0.1]:5555'
(ECDSA) to the
list
of known hosts.
ubuntu@
127.0
.
0.1
's password:
mmu
root@ubuntu:~
/
strng
The authenticity of host
'[127.0.0.1]:5555 ([127.0.0.1]:5555)'
can't be established.
ECDSA key fingerprint
is
SHA256:zoKoYVjPeMIA7zfSHVgA4
+
Q9bMN1IExTtD0rxTU7cLg.
Are you sure you want to
continue
connecting (yes
/
no)? yes
Warning: Permanently added
'[127.0.0.1]:5555'
(ECDSA) to the
list
of known hosts.
ubuntu@
127.0
.
0.1
's password:
mmu
ubuntu@ubuntu:~$ ls
mmu
ubuntu@ubuntu:~$ sudo .
/
mmu
sudo: unable to resolve host ubuntu
Where am I?
Your physical address
is
at
0x30bda008
ubuntu@ubuntu:~$ sudo .
/
mmu
sudo: unable to resolve host ubuntu
Where am I?
Your physical address
is
at
0x30bda008
root@ubuntu:~
/
strng
root
20265
20264
0
Jan18 pts
/
2
00
:
03
:
36
.
/
qemu
-
system
-
x86_64
-
m
1G
-
device strng
-
hda my
-
disk.img
-
hdb my
-
seed.img
-
nographic
-
L pc
-
bios
/
-
enable
-
kvm
-
device e1000,netdev
=
net0
-
netdev user,
id
=
net0,hostfwd
=
tcp::
5555
-
:
22
root@ubuntu:~
/
strng
root
20265
20264
0
Jan18 pts
/
2
00
:
03
:
36
.
/
qemu
-
system
-
x86_64
-
m
1G
-
device strng
-
hda my
-
disk.img
-
hdb my
-
seed.img
-
nographic
-
L pc
-
bios
/
-
enable
-
kvm
-
device e1000,netdev
=
net0
-
netdev user,
id
=
net0,hostfwd
=
tcp::
5555
-
:
22
ubuntu@ubuntu:~$ lspci
00
:
00.0
Host bridge: Intel Corporation
440FX
-
82441FX
PMC [Natoma] (rev
02
)
/
/
主板芯片
00
:
01.0
ISA bridge: Intel Corporation
82371SB
PIIX3 ISA [Natoma
/
Triton II]
00
:
01.1
IDE interface: Intel Corporation
82371SB
PIIX3 IDE [Natoma
/
Triton II]
00
:
01.3
Bridge: Intel Corporation
82371AB
/
EB
/
MB PIIX4 ACPI (rev
03
)
00
:
02.0
VGA compatible controller: Device
1234
:
1111
(rev
02
)
/
/
显卡
00
:
03.0
Unclassified device [
00ff
]: Device
1234
:
11e9
(rev
10
)
00
:
04.0
Ethernet controller: Intel Corporation
82540EM
Gigabit Ethernet Controller (rev
03
)
/
/
网卡
ubuntu@ubuntu:~$ lspci
00
:
00.0
Host bridge: Intel Corporation
440FX
-
82441FX
PMC [Natoma] (rev
02
)
/
/
主板芯片
00
:
01.0
ISA bridge: Intel Corporation
82371SB
PIIX3 ISA [Natoma
/
Triton II]
00
:
01.1
IDE interface: Intel Corporation
82371SB
PIIX3 IDE [Natoma
/
Triton II]
00
:
01.3
Bridge: Intel Corporation
82371AB
/
EB
/
MB PIIX4 ACPI (rev
03
)
00
:
02.0
VGA compatible controller: Device
1234
:
1111
(rev
02
)
/
/
显卡
00
:
03.0
Unclassified device [
00ff
]: Device
1234
:
11e9
(rev
10
)
00
:
04.0
Ethernet controller: Intel Corporation
82540EM
Gigabit Ethernet Controller (rev
03
)
/
/
网卡
-
device strng \
00
:
03.0
Unclassified device [
00ff
]: Device
1234
:
11e9
(rev
10
)
00
:
03.0
Unclassified device [
00ff
]: Device
1234
:
11e9
(rev
10
)
ubuntu@ubuntu:~$ lspci
-
t
-
v
-
[
0000
:
00
]
-
+
-
00.0
Intel Corporation
440FX
-
82441FX
PMC [Natoma]
+
-
01.0
Intel Corporation
82371SB
PIIX3 ISA [Natoma
/
Triton II]
+
-
01.1
Intel Corporation
82371SB
PIIX3 IDE [Natoma
/
Triton II]
+
-
01.3
Intel Corporation
82371AB
/
EB
/
MB PIIX4 ACPI
+
-
02.0
Device
1234
:
1111
+
-
03.0
Device
1234
:
11e9
\
-
04.0
Intel Corporation
82540EM
Gigabit Ethernet Controller
ubuntu@ubuntu:~$ lspci
-
t
-
v
-
[
0000
:
00
]
-
+
-
00.0
Intel Corporation
440FX
-
82441FX
PMC [Natoma]
+
-
01.0
Intel Corporation
82371SB
PIIX3 ISA [Natoma
/
Triton II]
+
-
01.1
Intel Corporation
82371SB
PIIX3 IDE [Natoma
/
Triton II]
+
-
01.3
Intel Corporation
82371AB
/
EB
/
MB PIIX4 ACPI
+
-
02.0
Device
1234
:
1111
+
-
03.0
Device
1234
:
11e9
\
-
04.0
Intel Corporation
82540EM
Gigabit Ethernet Controller
ubuntu@ubuntu:~$ lspci
-
v
00
:
03.0
Unclassified device [
00ff
]: Device
1234
:
11e9
(rev
10
)
Subsystem: Red Hat, Inc Device
1100
Physical Slot:
3
Flags: fast devsel
Memory at febf1000 (
32
-
bit, non
-
prefetchable) [size
=
256
]
/
/
这里给出了mmio的地址空间
0xfebf1000
I
/
O ports at c050 [size
=
8
]
/
/
pmio端口为
0xc050
ubuntu@ubuntu:~$ lspci
-
v
00
:
03.0
Unclassified device [
00ff
]: Device
1234
:
11e9
(rev
10
)
Subsystem: Red Hat, Inc Device
1100
Physical Slot:
3
Flags: fast devsel
Memory at febf1000 (
32
-
bit, non
-
prefetchable) [size
=
256
]
/
/
这里给出了mmio的地址空间
0xfebf1000
I
/
O ports at c050 [size
=
8
]
/
/
pmio端口为
0xc050
ubuntu@ubuntu:~$ ls
-
la
/
sys
/
devices
/
pci0000\:
00
/
0000
\:
00
\:
03.0
/
-
r
-
-
r
-
-
r
-
-
1
root root
4096
Jan
19
11
:
51
resource
-
rw
-
-
-
-
-
-
-
1
root root
256
Jan
19
14
:
36
resource0
-
rw
-
-
-
-
-
-
-
1
root root
8
Jan
19
14
:
36
resource1
ubuntu@ubuntu:~$ ls
-
la
/
sys
/
devices
/
pci0000\:
00
/
0000
\:
00
\:
03.0
/
-
r
-
-
r
-
-
r
-
-
1
root root
4096
Jan
19
11
:
51
resource
-
rw
-
-
-
-
-
-
-
1
root root
256
Jan
19
14
:
36
resource0
-
rw
-
-
-
-
-
-
-
1
root root
8
Jan
19
14
:
36
resource1
ubuntu@ubuntu:~$ cat
/
sys
/
devices
/
pci0000\:
00
/
0000
\:
00
\:
03.0
/
resource
/
*
start addr end addr flags
*
/
0x00000000febf1000
0x00000000febf10ff
0x0000000000040200
(MMIO)
0x000000000000c050
0x000000000000c057
0x0000000000040101
(PMIO)
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
ubuntu@ubuntu:~$ cat
/
sys
/
devices
/
pci0000\:
00
/
0000
\:
00
\:
03.0
/
resource
/
*
start addr end addr flags
*
/
0x00000000febf1000
0x00000000febf10ff
0x0000000000040200
(MMIO)
0x000000000000c050
0x000000000000c057
0x0000000000040101
(PMIO)
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
0x0000000000000000
struct TypeInfo
{
const char
*
name;
const char
*
parent;
/
/
父类
size_t instance_size;
size_t instance_align;
void (
*
instance_init)(
Object
*
obj);
/
/
实例初始化函数
void (
*
instance_post_init)(
Object
*
obj);
void (
*
instance_finalize)(
Object
*
obj);
/
/
实例“析构”函数
bool
abstract;
/
/
是否为抽象类
size_t class_size;
/
/
类大小,官方文档中提出.class_size字段设置为sizeof(MyClass)
void (
*
class_init)(ObjectClass
*
klass, void
*
data);
/
/
在所有父类被初始化完成后调用的子类初始化函数,可用于override virtual methods
void (
*
class_base_init)(ObjectClass
*
klass, void
*
data);
void
*
class_data;
InterfaceInfo
*
interfaces;
/
/
封装了const char
*
type
;
};
struct TypeInfo
{
const char
*
name;
const char
*
parent;
/
/
父类
size_t instance_size;
size_t instance_align;
void (
*
instance_init)(
Object
*
obj);
/
/
实例初始化函数
void (
*
instance_post_init)(
Object
*
obj);
void (
*
instance_finalize)(
Object
*
obj);
/
/
实例“析构”函数
bool
abstract;
/
/
是否为抽象类
size_t class_size;
/
/
类大小,官方文档中提出.class_size字段设置为sizeof(MyClass)
void (
*
class_init)(ObjectClass
*
klass, void
*
data);
/
/
在所有父类被初始化完成后调用的子类初始化函数,可用于override virtual methods
void (
*
class_base_init)(ObjectClass
*
klass, void
*
data);
void
*
class_data;
InterfaceInfo
*
interfaces;
/
/
封装了const char
*
type
;
};
static void pci_strng_register_types(void)
{
static const TypeInfo strng_info
=
{
/
/
这里就是一个TypeInfo结构体
.name
=
"strng"
,
.parent
=
TYPE_PCI_DEVICE,
/
/
父类为 TYPE_PCI_DEVICE
.instance_size
=
sizeof(STRNGState),
.instance_init
=
strng_instance_init,
.class_init
=
strng_class_init,
/
/
类初始化,虚函数覆写
};
type_register_static(&strng_info);
}
type_init(pci_strng_register_types)
static void pci_strng_register_types(void)
{
static const TypeInfo strng_info
=
{
/
/
这里就是一个TypeInfo结构体
.name
=
"strng"
,
.parent
=
TYPE_PCI_DEVICE,
/
/
父类为 TYPE_PCI_DEVICE
.instance_size
=
sizeof(STRNGState),
.instance_init
=
strng_instance_init,
.class_init
=
strng_class_init,
/
/
类初始化,虚函数覆写
};
type_register_static(&strng_info);
}
type_init(pci_strng_register_types)
static void __attribute__((constructor)) do_qemu_init_
{ \
register_module_init(function,
type
); \
}
static void __attribute__((constructor)) do_qemu_init_
{ \
register_module_init(function,
type
); \
}
typedef struct ModuleEntry
{
void (
*
init)(void);
QTAILQ_ENTRY(ModuleEntry) node;
module_init_type
type
;
} ModuleEntry;
static void init_lists(void)
{
static
int
inited;
int
i;
if
(inited) {
return
;
}
for
(i
=
0
; i < MODULE_INIT_MAX; i
+
+
) {
QTAILQ_INIT(&init_type_list[i]);
}
QTAILQ_INIT(&dso_init_list);
inited
=
1
;
}
static ModuleTypeList
*
find_type(module_init_type
type
)
{
init_lists();
return
&init_type_list[
type
];
}
void register_module_init(void (
*
fn)(void), module_init_type
type
)
{
ModuleEntry
*
e;
ModuleTypeList
*
l;
e
=
g_malloc0(sizeof(
*
e));
e
-
>init
=
fn;
e
-
>
type
=
type
;
l
=
find_type(
type
);
/
/
调用init_lists()做了初始化
QTAILQ_INSERT_TAIL(l, e, node);
}
typedef struct ModuleEntry
{
void (
*
init)(void);
QTAILQ_ENTRY(ModuleEntry) node;
module_init_type
type
;
} ModuleEntry;
static void init_lists(void)
{
static
int
inited;
int
i;
if
(inited) {
return
;
}
for
(i
=
0
; i < MODULE_INIT_MAX; i
+
+
) {
QTAILQ_INIT(&init_type_list[i]);
}
QTAILQ_INIT(&dso_init_list);
inited
=
1
;
}
static ModuleTypeList
*
find_type(module_init_type
type
)
{
init_lists();
return
&init_type_list[
type
];
}
void register_module_init(void (
*
fn)(void), module_init_type
type
)
{
ModuleEntry
*
e;
ModuleTypeList
*
l;
e
=
g_malloc0(sizeof(
*
e));
e
-
>init
=
fn;
e
-
>
type
=
type
;
l
=
find_type(
type
);
/
/
调用init_lists()做了初始化
QTAILQ_INSERT_TAIL(l, e, node);
}
struct TypeImpl
{
const char
*
name;
size_t class_size;
size_t instance_size;
size_t instance_align;
void (
*
class_init)(ObjectClass
*
klass, void
*
data);
void (
*
class_base_init)(ObjectClass
*
klass, void
*
data);
void
*
class_data;
void (
*
instance_init)(
Object
*
obj);
void (
*
instance_post_init)(
Object
*
obj);
void (
*
instance_finalize)(
Object
*
obj);
bool
abstract;
const char
*
parent;
TypeImpl
*
parent_type;
ObjectClass
*
class
;
/
/
指向 ObjectClass 的指针
int
num_interfaces;
InterfaceImpl interfaces[MAX_INTERFACES];
};
struct TypeImpl
{
const char
*
name;
size_t class_size;
size_t instance_size;
size_t instance_align;
void (
*
class_init)(ObjectClass
*
klass, void
*
data);
void (
*
class_base_init)(ObjectClass
*
klass, void
*
data);
void
*
class_data;
void (
*
instance_init)(
Object
*
obj);
void (
*
instance_post_init)(
Object
*
obj);
void (
*
instance_finalize)(
Object
*
obj);
bool
abstract;
const char
*
parent;
TypeImpl
*
parent_type;
ObjectClass
*
class
;
/
/
指向 ObjectClass 的指针
int
num_interfaces;
InterfaceImpl interfaces[MAX_INTERFACES];
};
static TypeImpl
*
type_register_internal(const TypeInfo
*
info)
{
TypeImpl
*
ti;
ti
=
type_new(info);
/
/
根据info信息,创建一个TypeImpl对象
type_table_add(ti);
/
/
将新建的TypeImpl对象注册到全局哈希表 type_table 中
return
ti;
}
static TypeImpl
*
type_register_internal(const TypeInfo
*
info)
{
TypeImpl
*
ti;
ti
=
type_new(info);
/
/
根据info信息,创建一个TypeImpl对象
type_table_add(ti);
/
/
将新建的TypeImpl对象注册到全局哈希表 type_table 中
return
ti;
}
struct ObjectClass
{
/
*
private:
*
/
Type
type
;
GSList
*
interfaces;
const char
*
object_cast_cache[OBJECT_CLASS_CAST_CACHE];
const char
*
class_cast_cache[OBJECT_CLASS_CAST_CACHE];
ObjectUnparent
*
unparent;
GHashTable
*
properties;
};
struct ObjectClass
{
/
*
private:
*
/
Type
type
;
GSList
*
interfaces;
const char
*
object_cast_cache[OBJECT_CLASS_CAST_CACHE];
const char
*
class_cast_cache[OBJECT_CLASS_CAST_CACHE];
ObjectUnparent
*
unparent;
GHashTable
*
properties;
};
static void type_initialize(TypeImpl
*
ti)
/
/
针对每一个注册的 TypeImpl 去做
type
初始化
{
TypeImpl
*
parent;
/
/
在 TypeImpl 中有 ObjectClass
*
class
if
(ti
-
>
class
) {
/
/
如果 ti
-
>
class
存在,那么说明初始化过了,直接
return
return
;
}
ti
-
>class_size
=
type_class_get_size(ti);
ti
-
>instance_size
=
type_object_get_size(ti);
if
(ti
-
>instance_size
=
=
0
) {
/
/
如果instance_size为
0
,标记为抽象类
ti
-
>abstract
=
true;
}
if
(type_is_ancestor(ti, type_interface)) {
......
}
ti
-
>
class
=
g_malloc0(ti
-
>class_size);
parent
=
type_get_parent(ti);
/
/
是否有父类?即是否存在继承关系?
if
(parent) {
/
/
如果存在继承关系的话
type_initialize(parent);
/
/
初始化父类
GSList
*
e;
int
i;
g_assert(parent
-
>class_size <
=
ti
-
>class_size);
g_assert(parent
-
>instance_size <
=
ti
-
>instance_size);
memcpy(ti
-
>
class
, parent
-
>
class
, parent
-
>class_size);
/
/
首先用父类数据 初始化
ti
-
>
class
-
>interfaces
=
NULL;
for
(e
=
parent
-
>
class
-
>interfaces; e; e
=
e
-
>
next
) {
InterfaceClass
*
iface
=
e
-
>data;
ObjectClass
*
klass
=
OBJECT_CLASS(iface);
type_initialize_interface(ti, iface
-
>interface_type, klass
-
>
type
);
/
/
初始化对应接口
}
for
(i
=
0
; i < ti
-
>num_interfaces; i
+
+
) {
TypeImpl
*
t
=
type_get_by_name(ti
-
>interfaces[i].typename);
if
(!t) {
error_report(
"missing interface '%s' for object '%s'"
,
ti
-
>interfaces[i].typename, parent
-
>name);
abort();
}
for
(e
=
ti
-
>
class
-
>interfaces; e; e
=
e
-
>
next
) {
TypeImpl
*
target_type
=
OBJECT_CLASS(e
-
>data)
-
>
type
;
if
(type_is_ancestor(target_type, t)) {
break
;
}
}
if
(e) {
continue
;
}
type_initialize_interface(ti, t, t);
/
/
初始化接口
}
}
/
*
建立存放属性的
hash
表
*
/
ti
-
>
class
-
>properties
=
g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
object_property_free);
ti
-
>
class
-
>
type
=
ti;
while
(parent) {
/
*
对于父类来说,如果定义了class_base_init,就调用它
*
/
if
(parent
-
>class_base_init) {
parent
-
>class_base_init(ti
-
>
class
, ti
-
>class_data);
}
parent
=
type_get_parent(parent);
}
/
*
调用当前TypeImpl的class_init
*
/
if
(ti
-
>class_init) {
ti
-
>class_init(ti
-
>
class
, ti
-
>class_data);
}
}
static void type_initialize(TypeImpl
*
ti)
/
/
针对每一个注册的 TypeImpl 去做
type
初始化
{
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-1-22 00:19
被Roland_编辑
,原因: