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
|
利用
|