先看一眼保护
没开PIE
瞅一眼启动脚本
设备就叫cydf-vga
然后在本地的2222端口开启了qemu monitor
脚本跑一下
没跑起来,缺个库
然后这样解决
然后IDA分析一下
首先是总的结构体 cydf_vga_register_types
然后是type_init一直往下调用的函数 do_qemu_init_cydf_vga_register_types ,里面继续调用module_init用来初始化typeinfo、typelmpl结构体。
然后调用 cydf_vga_class_init 初始化基类
然后调用 pci_cydf_vga_realize 初始化类对象
然后我们熟悉的还有cydf_vga_mem_read、cydf_vga_mem_write
我们不熟悉的有啥
isa_cydf_vga_class_init
isa_cydf_vga_realizefn
cydf_vga_ioport_read、cydf_vga_ioport_write
cydf_vga_write_gr
首先我们发现似乎注册了两个module。
所以我们在旁边的函数里面看到了有一些isa开头的
那么isa是啥?
ISA总线:是IBM公司为PC/AT电脑而制定的总线标准,是“Industry Standard Architecture”的英文缩写。由于它是16位体系结构,所以只能支持16位的I/O设备,数据传输率大约是16MB/S。1984年ISA总线在原来8位总线的基础上扩充出16位数据总线宽度。同时地址总线宽度也由20位扩充到24位,但仍保持原8位ISA总线的完整性。形成了现在使用的8位基本插槽加上16位扩充插槽的16位ISA总线标准。
两个模块应该分别对应结构体
怎么找这两个结构体呢
其中我们发现一个结构体是另一个结构体中的成员。
然后我们继续看两个init函数
函数我已经转了结构体
转结构体的方法,也就是为什么会转成这个结构体,也是要看汇编
举个例子
我们看到在PCI对应的那个结构体里面定义了设备号,厂商号啥的
所以其实我们就会有个大体的了解
VGA设备用了两个模块,一个pci,一个isa
他们都有对应的结构体
然后里面有我们上面提到的那个总的结构体。
我们剩下的四个函数,又对应两个结构体
对应的设备
就是最后一个。
在proc目录下有iomem和ioports文件,其主要描述了系统的io内存和io端口资源分布。
还有两个初始化类对象的realize函数也没啥好看的,我们每次都要注意mmio的注册。
我们上面看到应该是有三个mmio空间
但是我们只找到一个。
那另外两个在哪?
我们交叉引用一下
就找到了一大堆的 memory_region_init_io与memory_region_init函数。
那么他俩有啥区别?
x86的qemu模拟的时候有两个全局的memory_region结构体,memory_region就是管理qemu内存的基本单位,有根级MR,实体MR,抽象MR,这里大家需要补一些qemu内存管理相关的知识。
所以memory_region_init_io,其实是对memory_region_init的一个封装,两个函数一个初始化system_io,一个初始化system_memory。
至此,我们找到了三个mmio空间。
但是里面有一个叫cydf_bar。
这个是啥意思?
又需要补一补pci设备的知识了。
网上找了个图
Base Address Registers(BAR)。BAR是PCI配置空间中从0x10 到 0x24的6个register,用来定义PCI需要的配置空间大小以及配置PCI设备占用的地址空间。
每个PCI设备在BAR中描述自己需要占用多少地址空间,UEFI通过所有设备的这些信息构建一张完整的关系图,描述系统中资源的分配情况,然后在合理的将地址空间配置给每个PCI设备。
BAR在bit0来表示该设备是映射到memory还是IO,bar的bit0是readonly的,也就是说,设备寄存器是映射到memory还是IO是由设备制造商决定的,其他人无法修改。
那么所以这块应该是吧设备寄存器映射到了IO。
然后漏洞在哪呢?
我们看着整体的程序还是非常复杂的。
去网上参考了大佬的思路,果然不是硬逆。
是这样的。
首先发现呢有这样的一个字符串
然后程序是改了qemu里面的设备来的。
我们可以直接找到qemu原来的设备,然后看看改了哪些地方,从而快速按定位漏洞。
cydfvgastate结构体里面多了两个成员
漏洞在cydf_vga_mem_write函数
函数主要逻辑很简单,就是对不同的addr做不同的处理
函数里面多了的对addr大于0x10000小于0x18000的处理
其中主要逻辑就是
首先做两个判断,然后将cydfvgastate结构体里sr数组序号0xcc的值拿出来模5,然后根据这个值做一个功能选择。
接下来就是利用
我们上面发现漏洞可以有数组越界跟堆溢出,但是其实我们只用到数组任意写就可以了。
因为我们可以直接控制vs[10]的地方,通过
我们明显能够控制latch数组。
最后再利用一下
qemu_log函数中有一个vfprintf
参数是bss的一个地址。
因为没开PIE且居然可以劫持got表
所以我们直接将vfprintf劫持成system的plt
将printf劫持成vfprintf的plt
然后将bss写上参数,就可以了。
要注意的是,以往都是用户态打开对应的resource0文件进行映射,实现mmio的访问。但是这次不知道该打开哪个文件去映射,访问该地址空间才可以实现对cydf_vga_mem_write以及cydf_vga_mem_read的访问。
这时我们可以利用/dev/mem文件,dev/mem是物理内存的全映像,可以用来访问物理内存,用mmap来访问物理内存以及外设的IO资源,是实现用户空间驱动的一种方法。