虚拟指令集Pwn入门
虚拟指令集 pwn 入门
19年末尾参加的几场线下赛差不多都有一类题目,虚拟指令集pwn、VM pwn。
这种题目比起常见的菜单堆、栈类型,还算新颖,题目最主要的特点在我看来代码量大一些,逆向起来花一些时间,redhat_final时候一道虚拟指令集pwn等到搞清楚题目逻辑已经是下午了。
此类题目并不需要什么特殊的准备知识,下面按照由易到难介绍几道此类题目的解法,熟悉此类题目的常见形式以及考点,当做虚拟指令集pwn的入门,并介绍我整理题目时候一些感悟。
2019-ogeek-ovm
题目逻辑
此题模拟了vm行了,提供了set,add,read,write等指令,但在读写过程当中对于索引值的处理不当,导致可以越界读写。
首先看程序的bss段,程序的主要控制结构如下。
模拟了内存,寄存器以及stack。
.bss:0000000000202040 comment dq 4 dup(?) ; DATA XREF: main+15↑o
.bss:0000000000202040 ; main+27E↑o …
.bss:0000000000202060 public memory
.bss:0000000000202060 ; _DWORD memory[65536]
.bss:0000000000202060 memory dd 10000h dup(?) ; DATA XREF: fetch+1B↑o
.bss:0000000000202060 ; main+1C8↑o …
.bss:0000000000242060 public reg
.bss:0000000000242060 ; _DWORD reg[16]
.bss:0000000000242060 reg dd 10h dup(?) ; DATA XREF: fetch+4↑o
.bss:0000000000242060 ; fetch+11↑o …
.bss:00000000002420A0 public stack
.bss:00000000002420A0 ; _DWORD stack[16]
.bss:00000000002420A0 stack dd 10h dup(?) ; DATA XREF: execute+1E3↑o
.bss:00000000002420A0 ; execute+219↑o
此题程序逻辑并不复杂,程序初始化过程当中要求输入pc,sp以及code size。
然后程序的主要逻辑位于execue函数中,里面有指令解析过程。
首先将内存区按照四字节长度进行处理,最高字节代表分类标志,地位三字节进行指令操作。
three_byte = (a1 & 0xF0000u) >> 16; // three byte
two_byte = (unsigned __int16)(a1 & 0xF00) >> 8;
one_byte = a1 & 0xF;
篇幅限制,只解析其中主要的指令,对应的解析看注释。
else if ( HIBYTE(a1) == 0x10 )
{
reg[three_byte] = (unsigned __int8)a1;
}
/*
#reg[dst] = num
def set(dst,num):
return u32((p8(0x10)+p8(dst)+p8(0)+p8(num))[::-1])
*/
else if ( HIBYTE(a1) == 0xC0 )
{
reg[three_byte] = reg[two_byte] /*
#reg[a3] = reg[a2]
if ( HIBYTE(a1) == 0x70 )
{
reg[three_byte] = reg[one_byte] + reg[two_byte];
return;
}
/*
#reg[a3] = reg[a2] + reg[a1]
def add(a3,a2,a1):
return u32((p8(0x70)+p8(a3)+p8(a2)+p8(a1))[::-1])
*/
else if ( HIBYTE(a1) == 0x30 )
{
reg[three_byte] = memory[reg[one_byte]];
}