Q之逃逸-零之型-基础知识集合

  1. 前言
  2. QEMU
  3. QOM
  4. PCI
  5. 有用的指令
  6. 调试技巧
  7. exp模板
  8. 操作流程
  9. 参考

前言

感觉到了危机感,不撕裂自己去进阶,就永远无法得到真正的进步。

用户态那边再深入学一下IO,再推导一下2.27和2.29的HOF就算圆满收官了!

预计一个月时间左右入门并冲掉qemu!GO!

开始主要是学一下一些基本概念和交互手法。需要了解一些名词,比如PCI,PMIO,MMIO,QOM这些,以及对qemu的整个过程的一些认识,之后也会把一些通用技巧记录在这里。

其实现在出的kernel和qemu,大多数就是把用户态那一套包装一下放到了内核态或者qemu里。

QEMU

qemu其实就是虚拟化的软件,用来为虚拟机提供模拟的内存,模拟的硬件环境,模拟的CPU种种。比如在x86架构上跑个mips的虚拟机,就用qemu为它提供虚拟环境。

其实跟内核编程看起来有一定的相似之处。写函数然后注册PMIO和MMIO的空间和读写函数balabala。

QOM

QEMU提供了一套面向对象编程的模型——QOM,即QEMU Object Module,几乎所有的设备如CPU、内存、总线等都是利用这一面向对象的模型来实现的。就是用来模拟硬件的,像是QEMU准备的一套模板一样。

PCI

PCI设备的配置空间:

1583307174971

有用的指令

lspci:

用于显示当前主机的所有PCI总线信息,以及所有已连接的PCI设备信息。

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)

输出的格式为:总线:设备:功能

调试技巧

我在本机上如果直接gdb attach,qemu会杀死自己的进程。

所以改成跟调内核类似,在启动命令最后加入gdb映射端口,比如这样:

./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 \
    -gdb tcp::1234

后来我发现还是不行,最后找到原因:这里因为QEMU是开了PIE的,所以调试会遇到很奇怪的问题,参考[1]:

The way I have this setup is with a gdb start-up script. QEMU is compiled with full PIE so I will be disabling it to be able to insert breakpoints at constant addresses. I’m not using the -enable-kvm option because I’m on a Mac/Windows otherwise this will make your VM exponentially faster.

参考资料[1],写一个debug,比如:

file qemu-system-x86_64
aslr off
b strng_mmio_read
b strng_mmio_write
b strng_pmio_read
b strng_pmio_write
run  -m 1G -device strng -hda my-disk.img -hdb my-seed.img -nographic -L pc-bios/ -device e1000,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:22

然后gdb启动方式:

$ gdb
Reading symbols from qemu-system-x86_64...done.
gdb-peda$ source debug

中间可能会停顿一下,一发回车搞定它!终于可以调试了,这bug卡了我一个多小时。

从raycp师傅blog[2]里找到的查看一些结构体的小技巧

p *(STRNGState*)$rdi
p ((STRNGState*)$rdi).regs

exp模板

编译指令为静态,架构视情况而定:

gcc -m32 -O0 -static -o exp exp.c

操作mmio的memory部分。

根据IDA里找init函数里的id数字,以及lspci命令的信息,即可找到MMIO_FILE的路径。

#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include<sys/io.h>

#define MMIO_FILE "/sys/devices/pci0000:00/0000:00:03.0/resource0"

unsigned char* mmio_mem;

void die(const char* msg)
{
    perror(msg);
    exit(-1);
}

void init_io(){
    // Open and map I/O memory for the strng device
    int mmio_fd = open(MMIO_FILE, O_RDWR | O_SYNC);
    if (mmio_fd == -1)
        die("mmio_fd open failed");

    mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    if (mmio_mem == MAP_FAILED)
        die("mmap mmio_mem failed");

    printf("mmio_mem: %p\n", mmio_mem);

    // Open and map I/O memory for the strng device
    if (iopl(3) !=0 )
        die("I/O permission is not enough");
//...
}

这时的mmio_mem就是在qemu中mmio的内存空间起始地址了。

操作流程

  1. 看start.sh,找设备名。
  2. IDA逆qemu,找对应设备的注册函数,看mmio和pmio的读写函数。改一改结构体让它看起来更舒服。
  3. 解包文件系统,然后在写好Makefile,gdb的debug文件,文件系统基本上除了cpio就是gz。
  4. 写好基础的交互函数,根据IDA里的设备id信息,就可以找到对应的resource文件。
  5. 找洞,利用,写exp。

参考

[1] https://uaf.io/exploitation/2018/05/17/BlizzardCTF-2017-Strng.html

[2] https://ray-cp.github.io/archivers/qemu-pwn-Blizzard-CTF-2017-Strng-writeup#%E8%B0%83%E8%AF%95


connect 1037178204@qq.com

文章标题:Q之逃逸-零之型-基础知识集合

本文作者:t1an5t

发布时间:2020-03-04, 16:50:00

最后更新:2020-03-09, 22:31:26

原始链接:http://yoursite.com/Q%E4%B9%8B%E9%80%83%E9%80%B8-%E9%9B%B6%E4%B9%8B%E5%9E%8B-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E9%9B%86%E5%90%88/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录