最近在学习CTF Wiki
有一块是专门介绍crackme的,就温故一下
给个链接:https://ctf-wiki.github.io/ctf-wiki/reverse/vm/vm/
打开之后一顿操作,如下图所示
直接拖入IDA,看一下调用了什么模块,如下
我们可以看到调用了GetDlgItemTextA,(必须的啊,要获得用户的输入嘛,就调用这个API),那这个来定位还是不错的,跟进去看一下,如下
调用了两次GetDlgItemTextA,显然一次是输入name,一次是输入key,要判断哪个是哪个直接看参数nIDDlgItem(控件标识符)就好了,用spy++看一下,虽然值不一样,但是可以看见key的标识永远比name大,那么我们就确定哪个是哪个啦
我们可以看到401176处的jump语句,我们跳到4011BC处,如下
可以看见DialogFunc窗口结束了,整个窗口的流程也结束了,那么在401171处的call语句就很关键,这个函数很大概率起到判断即加解密操作,跟进去看,发现还有一层4011C2,再更进去,如下
函数使用cld; repne scasb; not ecx; dec ecx
来计算字符串长度并将结果保存在ecx
里,我们改名为strlength好了
我们返回到上一层,如下
我们可以看到两个cmp 7的操作,如果小于7,就会结束程序,那么很清楚了,我们输入的name和key的长度都必须大于等于7才行,然后若大于等于7,会进行一个操作,我们跟下去看一下,直接转成伪c代码看好了,如下
直接就是个简单的异或操作,脚本如下
#name=xiaoyuyulala
name='xiaoyuyulala'
name_new=''
for i in range(len(name)):
name_new+=chr(ord(name[i])^i)
print(name_new)
#name_new=xhcl}prdhfj
处理完异或操作,会有一个call函数,具体执行的就是一堆赋值操作,完成的是一些虚拟机启动前的初始化工作(其实就是对一些寄存器和相关的部分赋初值),我们命名为initVM
之后会执行有一大堆未编译的字符串,如下图
这一片东西我着实不知道要干嘛……(但总而言之就是在初始化虚拟机之后需要执行的部分)
后来经过一系列的学习吧,知道这些东西都是SEH部分,其中4012B5处压入的就是SEH的句柄seh_handler,那么我们如何才能看出这里压入的是SEH句柄,且接下来的那部分是SEH呢?
逆向这个东西7分分析,3分猜,关于SEH的使用,主要包括终止处理(finally)和异常处理(exception),而我们可以看到4012CD处有个int 3断点,可以手动触发异常,再加上,所以我们极度怀疑这里压入的是SEH的句柄
在seh_handler
位置, IDA并未正确识别出对应的代码,我们可以点击相应位置按下c
键, 将这些数据转换成代码进行识别。
以此类推,底下还有好多代码是可以触发溢出的
在第一个seh_handler里面,将ecx变为0后,div 0,除以0, 触发溢出
在之后有个很坑的地方,是专门对付我们这些反调试的,如下
这里的这个jmp语句导致程序的EIP改变,导致调试无法进行,我们需要爆破掉,然后在00401306
位置创建一个新函数seh_handler2
,类似的, 还有401330h
重命名为seh_handler3,最终如下
我们在seh_handler3中发现了最后一个异常处理函数,40135E,我们可以推测这才是虚拟机真正的main函数(真的是绕来绕去,防止我们分析得太容易,一个异常处理函数套一个,最后一个大概率才是真的,从代码量也看得出来),因此我们将40135Eh
重命名为vm_main
,重命名之后要创建函数IDA才能识别,直接右键函数名,选create function就好了
那我们终于可以看看虚拟机的这些指令到底在干嘛,直接转成伪C代码试试看,失败了……如下图
错误的原因是因为40180B处的堆栈不平衡,要手动调,我们直接去附近看一下,怎么看40180B的堆栈调用情况呢,这里又学到了新姿势,我们可以点击IDA菜单项Options->General
在右侧勾选stack pointer,
这样就会显示出对应的栈指针,真的是tql,调完后如下
我们可以看到到40180A的时候堆栈就已经平很了,直接把leave改成retn就行,这里还有个细节,我直接改assemble改成retn是失败的,可以选择修改byte,改成C3就可以了,改成伪C代码后,就可以分析该虚拟机中的指令对应实际的意思是什么了,是的,你没看错,刚刚可以分析虚拟机指令……
我自己是选择将initVM和vm_main两个函数合在一起看的,只能慢慢分析了,大家加油……
来判断是否输入的密码正确的判断函数部分,可以直接用字符串定位的,如下
直接根据字符串就可以定位到判断函数中,我们将其改名为check,如下
如果把之前的虚拟指令全部分析出来就可以写keygen啦,祝大家好运