虚拟机分析(经典crackme)

最近在学习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啦,祝大家好运

猜你喜欢

转载自blog.csdn.net/qq_42192672/article/details/82924563