D3ctf-2021-Qualifier-Pwn-D3dev
描述
d3devState结构体:
d3devState
struct __attribute__((aligned(16))) {
    PCIDevice_0 pdev;
    MemoryRegion_0 mmio;
    MemoryRegion_0 pmio;
    uint32_t memory_mode;
    uint32_t seek;
    uint32_t init_flag;                 ; 标志是否初始化
    uint32_t mmio_read_part;
    uint32_t mmio_write_part;
    uint32_t r_seed;
    uint64_t blocks[257];
    uint32_t key[4];                    ; 4个随机数(rand())
    int (*rand_r)(unsigned int *);
}
pci_d3dev_realize:
void __fastcall pci_d3dev_realize(PCIDevice_0 *pdev, Error_0 **errp)
{
memory_region_init_io(
    (MemoryRegion_0 *)&pdev[1],
    &pdev->qdev.parent_obj,
    &d3dev_mmio_ops,
    pdev,
    "d3dev-mmio",
    0x800uLL);
pci_register_bar(pdev, 0, 0, (MemoryRegion_0 *)&pdev[1]);
memory_region_init_io(
    (MemoryRegion_0 *)&pdev[1].name[56],
    &pdev->qdev.parent_obj,
    &d3dev_pmio_ops,
    pdev,
    "d3dev-pmio",
    0x20uLL);
pci_register_bar(pdev, 1, 1u, (MemoryRegion_0 *)&pdev[1].name[56]);
}
可以看到mmio操作结构体d3dev_mmio_ops,大小0x800,pmoi操作结构体d3dev_pmio_ops,大小0x20
void __fastcall d3dev_instance_init(Object_0 obj)
{
  d3devState v1; // rbx
  unsigned int v2; // eax
  int v3; // eax
  v1 = (d3devState )object_dynamic_cast_assert(
                       obj,
                       “d3dev”,
                       “/home/eqqie/CTF/qemu-escape/qemu-source/qemu-3.1.0/hw/misc/d3dev.c”,
                       213,
                       “d3dev_instance_init”);
  v1->rand_r = (int ()(unsigned int *))&rand_r;
  if ( !v1->init_flag )
  {
    v2 = time(0LL);
    srand(v2);
    v1->key[0] = rand();
    v1->key[1] = rand();
    v1->key[2] = rand();
    v3 = rand();
    v1->init_flag = 1;
    v1->key[3] = v3;
  }
}
接下来看mmio和pmio的操作
分析
MMIO
d3dev_mmio_read
已知key[0-3]是一组随机数,addr是我们传进来的地址,在如下循环中
| do | 
| uint64_t __fastcall d3dev_mmio_read(d3devState *opaque, hwaddr addr, unsigned int size) | 
d3dev_mmio_write
| void __fastcall d3dev_mmio_write(d3devState *opaque, hwaddr addr, uint64_t val, unsigned int size) | 
编程访问MMIO
| unsigned char* mmio_mem; | 
PMIO
d3dev_pmio_read
| uint64_t __fastcall d3dev_pmio_read(d3devState *opaque, hwaddr addr, unsigned int size) | 
这里实际上有一波混淆,汇编里是这样的:
.text:00000000004D7D00 ; uint64_t __fastcall d3dev_pmio_read(d3devState *opaque, hwaddr addr, unsigned int size)
.text:00000000004D7D00 d3dev_pmio_read proc near               ; DATA XREF: .data.rel.ro:d3dev_pmio_ops↓o
.text:00000000004D7D00 addr = rsi                              ; hwaddr
.text:00000000004D7D00 size = rdx                              ; unsigned int
.text:00000000004D7D00 opaque = rdi                            ; void *
.text:00000000004D7D00 ; __unwind {
.text:00000000004D7D00                 endbr64
.text:00000000004D7D04 d3dev = rdi                             ; d3devState *
.text:00000000004D7D04                 cmp     addr, 18h
.text:00000000004D7D08                 ja      short loc_4D7D20
.text:00000000004D7D0A                 lea     size, table
.text:00000000004D7D11                 movsxd  rax, dword ptr [rdx+addr*4]
.text:00000000004D7D15                 add     rax, rdx
.text:00000000004D7D18                 db      3Eh
.text:00000000004D7D18                 jmp     rax
.text:00000000004D7D18 ; ---------------------------------------------------------------------------
.text:00000000004D7D1B                 align 20h
.text:00000000004D7D20
.text:00000000004D7D20 loc_4D7D20:                             ; CODE XREF: d3dev_pmio_read+8↑j
.text:00000000004D7D20                 mov     rax, 0FFFFFFFFFFFFFFFFh
.text:00000000004D7D27                 retn
.text:00000000004D7D27 d3dev_pmio_read endp
.text:00000000004D7D27
.text:00000000004D7D27 ; ---------------------------------------------------------------------------
.text:00000000004D7D28                 align 10h
.text:00000000004D7D30                 mov     eax, [rdi+12ECh]
.text:00000000004D7D36                 retn
.text:00000000004D7D36 ; ---------------------------------------------------------------------------
.text:00000000004D7D37                 align 20h
.text:00000000004D7D40                 mov     eax, [rdi+0AC0h]
.text:00000000004D7D46                 retn
.text:00000000004D7D46 ; ---------------------------------------------------------------------------
.text:00000000004D7D47                 align 10h
.text:00000000004D7D50                 mov     eax, [rdi+0AC4h]
.text:00000000004D7D56                 retn
.text:00000000004D7D56 ; ---------------------------------------------------------------------------
.text:00000000004D7D57                 align 20h
.text:00000000004D7D60                 mov     eax, [rdi+12E0h]
.text:00000000004D7D66                 retn
.text:00000000004D7D66 ; ---------------------------------------------------------------------------
.text:00000000004D7D67                 align 10h
.text:00000000004D7D70                 mov     eax, [rdi+12E4h]
.text:00000000004D7D76                 retn
.text:00000000004D7D76 ; ---------------------------------------------------------------------------
.text:00000000004D7D77                 align 20h
.text:00000000004D7D80                 mov     eax, [rdi+12E8h]
.text:00000000004D7D86                 retn
.text:00000000004D7D86 ; } // starts at 4D7D00
table以32位显示是这样的:
.rodata:00000000007ADF30                ; int table[28]
.rodata:00000000007ADF30 10 9E D2 FF    table           dd 0FFD29E10h           ; DATA XREF: d3dev_pmio_read+A↑o
.rodata:00000000007ADF34 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF38 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF3C F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF40 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF44 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF48 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF4C F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF50 20 9E D2 FF                    dd 0FFD29E20h
.rodata:00000000007ADF54 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF58 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF5C F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF60 30 9E D2 FF                    dd 0FFD29E30h
.rodata:00000000007ADF64 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF68 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF6C F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF70 40 9E D2 FF                    dd 0FFD29E40h
.rodata:00000000007ADF74 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF78 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF7C F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF80 50 9E D2 FF                    dd 0FFD29E50h
.rodata:00000000007ADF84 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF88 F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF8C F0 9D D2 FF                    dd 0FFD29DF0h
.rodata:00000000007ADF90 00 9E D2 FF 00+                dq 0FFD29E00h
.rodata:00000000007ADF98 00 00 00 00 00+                dq 0

流程就是,首先假设我们传进来的addr是0,那么获取table[0],由指令movsxd运算,将32位的0xFFD29E10扩展为64位地址,
高位用符号位补齐,即扩展为0xFFFFFFFFFFD29E10,然后再加上前面lea读取的table的地址,即00000000007ADF30这个地址,
得到目标地址0x100000000004d7d40,即:
.text:00000000004D7D40 8B 87 C0 0A 00+                mov     eax, [rdi+0AC0h]
.text:00000000004D7D40 00
.text:00000000004D7D46 C3                             retn
据此我们可以把table中的全部地址计算出目标地址,以及对应的addr:
0FFD29E00h -> 0x100000000004d7d30 -> 0x18
0FFD29E10h -> 0x100000000004d7d40 -> 0x00
0FFD29E20h -> 0x100000000004d7d50 -> 0x08
0FFD29E30h -> 0x100000000004d7d60 -> 0x0c
0FFD29E40h -> 0x100000000004d7d70 -> 0x10
0FFD29E50h -> 0x100000000004d7d80 -> 0x14
0FFD29DF0h -> 0x100000000004d7d20 -> check failed
实际上根据出现次数也能看出来,0FFD29DF0h会跳转到
.text:00000000004D7D20 48 C7 C0 FF FF+                mov     rax, 0FFFFFFFFFFFFFFFFh
.text:00000000004D7D20 FF FF
.text:00000000004D7D27 C3                             retn
即check failed的块,所以我们的addr只能是0x20/4,0x30/4,0x40/4,0x50/4,0x60/4,即0x8,0xc,0x10,0x14,0x18
00000000 d3devState      struc ; (sizeof=0x1300, align=0x10, copyof_4545)
00000000 pdev            PCIDevice_0 ?
000008E0 mmio            MemoryRegion_0 ?
000009D0 pmio            MemoryRegion_0 ?
00000AC0 memory_mode     dd ?
00000AC4 seek            dd ?
00000AC8 init_flag       dd ?
00000ACC mmio_read_part  dd ?
00000AD0 mmio_write_part dd ?
00000AD4 r_seed          dd ?
00000AD8 blocks          dq 257 dup(?)
000012E0 key             dd 4 dup(?)             ; 4个double word,1个是4字节,即12e0,12e4,12e8,12ec
000012F0 rand_r          dq ?                    ; offset
000012F8                 db ? ; undefined
000012F9                 db ? ; undefined
000012FA                 db ? ; undefined
000012FB                 db ? ; undefined
000012FC                 db ? ; undefined
000012FD                 db ? ; undefined
000012FE                 db ? ; undefined
000012FF                 db ? ; undefined
00001300 d3devState      ends
根据上述的d3devState结构体来看,我们传进来的addr的值,可以分别读取
0x18: opaque + 0x12ec: opaque->key[3]
0x00: opaque + 0x0ac0: opaque->memory_mode
0x08: opaque + 0x0ac4: opaque->seek
0x0c: opaque + 0x12e0: opaque->key[0]
0x10: opaque + 0x12e4: opaque->key[1]
0x14: opaque + 0x12e8: opaque->key[2]
看W&M的师傅写的WP,有一波操作可以把这里转换成switch结构,后续搞懂怎么弄补一下

d3dev_pmio_write
| // local variable allocation has failed, the output may be wrong! | 
编程访问pmio
| 
 | 
利用
| 
 |