GKCTF EzMachine

运行一下程序
在这里插入图片描述

检测程序,32位无壳,直接用IDA打开
在这里插入图片描述
字符串窗口没找到有用的字符串,改用x32dbg动调,搜索ascii字符串找到关键字符串
在这里插入图片描述
在这里插入图片描述

根据当前地址在IDA中找到对应的函数sub_4E10E0
在这里插入图片描述
可以看到一个switch结构,题目名称明显表示这是个vm题,一开始猜测这就是dispatcher,后来发现并不是,在x32dbg中动调发现,程序在case3也就是输出“plz input”字符串后,跳到了地址为4E1594处,一开始我把这部分声明成一个新函数,但是反编译出来的c代码根本没法读,只好动调这部分汇编代码

经多次调试后发现,这里才是这个vm的真正的dispatcher,程序以dword_525BD8为计数器,取出byte_5249A0数组中对应位序的值作为byte_5248F0的位序,而byte_5248F0我们查看之后发现是一个一串标号,对应byte_5248F4中的函数,之后把这两个数组统称为调度表
在这里插入图片描述
输入函数也在里面(第17个函数),程序从case3出来之后,dispatcher第一个调用的就是输入函数,此时计数器dword_525BD8为6,调度表中的函数大部分是执行了之后会使计数器自增3,个别几个会有跳转,然后继续动调,得出程序一些关键变量
首先程序里有一个数组,调度器调用的每个函数基本都在使用这四个变量,
在这里插入图片描述

然后是最开始的switch结构里决定switch分支的变量dword_525BCC和输入存储的位置。
在这里插入图片描述

然后给出动调出来的函数执行顺序即对应的功能(这里变量名字因为调试时候基址不一样所以名称有些不同)

这是自己动调的时候写的一些笔记

第一部分:判断输入长度是否正确
sub_111000   //535BC4=0x11
sub_111300   //535BCC=535BDC-535BC4(0x11)  ;判断长度是否正确
sub_111340   //跳转,535BD8=0x1B

第二部分:判断输入的字符是大写字母还是小写字母还是下划线,对应不同的执行流程
由于输入字符有17位,所以这部分会执行17次

sub_111000   //535BD4=0
sub_111000   //535BDC=0x11
sub_111300   //535BCC=535BDC(0x11)-535BD4(0)
sub_111340   //+3
sub_1114B0   //从输入中取出一个值送入535BDC中
sub_111000   //535BC4=0x61(输入?)
sub_111300   //535BCC=535BDC(0x61)-535BC4(0x61)=0
sub_1113D0   //535BD8+3
(这里开始分支,输入为'_'时)
sub_111000   //535BC4=0x41
sub_111300   //535BCC=535BDC('_'(0x5F))-535BC4(0x41)
sub_1113D0   //+3
sub_111000   //535BC4=0x5A
sub_111300   //535BCC=535BDC(0x5F)-535BC4(0x5A)=5
sub_1113A0   //535BD8=0x69
sub_111000   //535BC4=0x10
sub_111270   //535BDC(0x5F)和535BC4(0x10)做了一次除法,
             //535BDC保存结果(5),535BC4保存余数(F)
sub_111030   //入栈535BDC的结果
sub_111030   //入栈535BC4的结果
sub_111000   //535BC4=1
sub_1111D0   //535BD4(0)+535BC4(1)
sub_1112E0
(分支,输入为大写字母时)
111000   //535BC4=4B
1112B0   //输入^0x4B
111000   //535BC4=1
111200   //输入-=1
sub_111000   //535BC4=0x10
sub_111270   //535BDC(0x27)和535BC4(0x10)做了一次除法,
             //535BDC保存结果,535BC4保存余数
sub_111030   //535BC8++(初始值为零),将535BC4(7)的值存到一个新的变量[535BAC+1]中
sub_111030   //将535BDC(2)的值存到[535BAC+2]中
sub_111000   //535BC4=1
sub_1111D0   //535BD4(0)+535BC4(1)
sub_1112E0
 (输入为小写字母时)
 sub_111000   //535BC4=0x7A
sub_111300   //535BCC=535BDC(0x61)-535BC4(0x7A)=-19(十进制)
sub_1113A0   //+3
sub_111000   //535BC4=0x47

sub_1112B0    //输入异或?    "a"^0x47=0x26
sub_111000    //535BC4=1
sub_1111D0    //??           dword_535BDC(0x26)+535BC4(1)
sub_1112E0

0x69段
sub_111000   //535BC4=0x10
sub_111270   //535BDC(0x27)和535BC4(0x10)做了一次除法,
             //535BDC保存结果,535BC4保存余数
sub_111030   //535BC8++(初始值为零),将535BC4(7)的值存到一个新的变量[535BAC+1]中
sub_111030   //将535BDC(2)的值存到[535BAC+2]中
sub_111000   //535BC4=1
sub_1111D0   //535BD4(0)+535BC4(1)
sub_1112E0   //跳转

第三部分  将正确输入的结果压入栈dword_535BAC中
sub_4E1070
 
第四部分
判断输入生成的栈数据是否正确,也是一位一位比较
sub_111000    //535BD4=1
sub_111470    //535BD0初始化为0,将加密后的输入(535BAC+1(7))送入535BC4
sub_1110A0    //将密文最后一位(535BAC+0x44(2))送入535BDC  (535BC8=0x44?)
sub_111300    //535BCC=535BDC(2)-535BC4(7)
sub_111370    //535BD8=10E(判断函数?)正确+3   输入错误:535BCC=1
sub_111000    //535BC4=0x22
sub_111300    //535BCC=535BD4(1)-0x22
sub_111340    //+3
sub_111000    //535BC4=1
sub_1111D0    //535BD4(1)+=535BC4(1)
sub_1112E0

总结一下就是程序会将输入的每个字符做一定的处理,然后压入栈中
大写字母: 先异或0x4B,再自减1,和0x10做除法后将商和余数压入栈中
小写字母:先异或0x47,再自增1,和0x10做除法后将商和余数压入栈中
其他字符:直接和0x10做除法后将商和余数压入栈中

正确的堆栈结果可以动调出来,也可以自己写脚本,得到的数据为
7, 13, 0, 5, 1, 12, 1, 0, 0, 13, 5, 15, 0, 9, 5, 15, 3, 0, 2, 5, 3, 3, 1, 7, 7, 11, 2, 1, 2, 7, 2, 12, 2, 2(左边为栈底)

知道这些就可以写逆向脚本了

code = [7, 13, 0, 5, 1, 12, 1, 0, 0, 13, 5, 15, 0, 9, 5, 15, 3, 0, 2, 5, 3, 3, 1, 7, 7, 11, 2, 1, 2, 7, 2, 12, 2, 2]

flag=""
i=0
print(len(code))
while(i<34):
    m = code[i]*0x10+code[i+1]
    if(m==95 or m==123 or m==125):
        flag+=chr(m)
    else:
        t=m
        t-=1
        t^=0x47
        if(t<97):
            t = m
            t += 1
            t ^= 0x4B
        flag+=chr(t)
    i+=2
print(flag[::-1])

最后的结果为flag{Such_A_EZVM}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值