[ 2024 · CISCN x 长城杯 ] pwn avm
1|02024 CISCN x 长城杯 AVM
1|1avm
VM入门题。不过挺吃逆向经验的。之前都是复现,这算是第一次比赛的时候做出vm题。这个题的逆向思路非常经典,所以分享一下。
1.程序逆向
函数主函数如下:
memset了0x300的内存,write提示这块内存作为opcode输入,所以可以得到s变量就是opcode,之后的sub_1230和sub_19F1函数就是对我们输入的opcode的处理了。也是这个vm的主要功能
1.1 寄存器结构体逆向
首先看sub_1230函数:
for循环有一个非常经典的循环32次置空的操作,这个是把32个通用寄存器置空的操作
另外从数组的32到34赋值操作的含义分别:
为33号寄存器赋值opcode的基地址
为34号寄存器赋值opcode的最大长度,可能是用来限制指令读取,防止越界的。
32号寄存器赋值为空,这里暂时不知道作用是什么。
之后可以为IDA编辑如下结构体:
之后去逆向sub_19F1函数
程序上来先memset了0x100的内存,并在执行指令时将内存作为参数传递:(funcs_1AAD[v2])(a1, s);
其中,v2来锁定执行的是哪条命令:
v2 = *(a1[33] + (a1[32] & 0xFFFFFFFFFFFFFFFCLL)) >> 28;
是将opcode转换为指令的译码过程。
其中已知33号寄存器是基地址,那么32号寄存器应该就是偏移地址。两个地址相加处理后的地址是当前执行的指令地址,对当前指令地址右移28位就是指令的操作码。
可以看到这个是一个定长的指令集hh
那么现在可以将结构体修改为下面的样子了:
之后去逆向指令即可
1.2 指令逆向
我们从第一个开始分析:下面是已经逆向好的内容:
此处我们逆向的目的是分析指令编码情况:
- 通过
reg->r[PC & 0x1F]
我们可以看到,指令的低5位是用来指定通用寄存器序号的(0x1f),并且是保存操作结果的 - 之前
v2 = *(a1[33] + (a1[32] & 0xFFFFFFFFFFFFFFFCLL)) >> 28;
可以看到,指令的高36位(64位定长指令)或者高4位(32位定长指令)是用来指定指令操作码的 reg->ip += 4LL;
可以看到,以4字节为一个单位长度,代表指令是32位。
指令码大致如下:32bit:
之后其他指令都与此类似。唯二两个不同的指令如下:
byte_4010中存的是0xff,if检查判断要求之后操作的地址范围在之前memset的0x100以内
检查条件如下:
STR指令赋值操作:
s是memset内存的基地址,(reg->r[(PC >> 5) & 0x1F] + (HIWORD(PC) & 0xFFF))
是偏移
这里可以看到检测条件是0xff以内,但是if里面的赋值操作却可以写s加0x1000偏移以内的数据
2.漏洞利用
很明显,LDR和STR存在越界读写漏洞
没有输出函数,无法获得libc,打法是:
- 先构造一个LDR把一个onegadget附件的libc地址写入寄存器。
- 利用SUB指令将这个libc减去和onegadget的偏移,结果保存在指定寄存器中
- 利用STR把onegadget地址写入到main函数返回地址上。
这里解释一下下面的exp,sub操作只能在寄存器之间操作。所以无法直接写入一个我们希望的数据。利用方法是:先在opcode末尾加入我们计算好的onegadget与指定libc地址的偏移,然后通过STR指令将这个数据读入到寄存器中,再sub即可
3.exp
__EOF__

本文链接:https://www.cnblogs.com/seyedog/p/18636685.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通