首先这个creakme是逛看雪的时候看到的,别人已经写了n多分析,看了看,对于我这种新手可能是一个好练手的题目,我把每一行汇编都分析了下,其中有不少浮点数的汇编指令,边记录边学习。
首先拿到程序在虚拟机先运行下,是输序列号,然后正确弹正确的框,错误弹Error。
既然是弹对话框,首先想到的就是MessageBox,当然,分析一个程序最需要做的是找到主函数,因为实现的功能一般都在主函数中。那常见的程序可能会加壳或者有其它一些反调试的东西,PEid看一下是否加壳了。
这个程序是没加壳的,用VC6写的,所以不用考虑脱壳问题。
然后就考虑定位关键的函数,既然是正确弹正确的框,错误弹错误的框,那么肯定有个判断的跳转,如何定位这个位置,一是可以在常见的弹消息框的函数下断点,(当然也可以查找字符串来定位),bp MessageBoxA,bp MessageBoxW,当弹出对话框的时候,判断的分支应该就在附近。
F9跑起来,随便输一串111111,点击按钮,发现程序被断在设定的MessageBoxA断点
在call的地址右键在反汇编窗口中跟随,到了调用MessageBox函数的地址。
可以看到,error字符串被压入栈,这里就是弹出的错误框的地方。往上翻一下,有个jnz的条件跳转,jnz也就是zf=0的时候,会跳转,按程序的流程来看,应该是输入错误,就会通过和正确的字符串做比较,然后往各自的分支进行跳转。
jnz往上找可以看到有个fcomp dword ptr ds:[0x407118]的比较,这个是浮点数比较,比较完后st0出栈。找到比较的位置。就可以推断上面这一段代码是在判断输入字符串和正确的字符串是否相等。
那接下来,我考虑既然要比较字符串,是不是从获取输入的字符串处开始看可能更详细知道是如何比较的。获取输入字符串的函数GetDlgTextA,继续往上找
定位到这里,这GetDlgItemTextA处下断点,ctrl+F2重新运行程序,依然输入11111111,断点已经断下,在参数buffer地址右键数据跟随,当调用GetDlgItemTextA后,观察数据窗口中的值
输入的11111111已经存在buffer中。然后把字符串取出来,存到eax中,eax入栈,call WannaLoL.00401300,跟进去看下。
分析过程中因为功底太浅有的汇编代码意思不清楚,但总体函数的意思就是在计算输入字符串的长度。返回后可以看到eax的值位8,也就是11111111的长度。
那么可以看到第一个判断就是当字符串长度不等于4,就说明输入错误,弹那个错误的框。
到这里知道了第一个条件,就是输入字符串的长度必须为4。重新运行程序,这次输1111。因为知道了函数401300的作用,所以可以F8步过。第一次长度比较通过,又进入了一个新的条件比较。
这个判断,就是要求输入的字符串,必须只有四位,并且四位不能出现0,并且第一位和第二位分别为1和5,也就是输入的字符串为15??。继续重新加载程序,这次输入一个1511,输入以后前面的判断都通过,到达最后的一个条件判断
总的流程就是,现在输入了‘1511’四个数字,(第三位-(第一位/第二位))*第四位*16=384
现在第一位和第二位已经知道分别是 1 和 5,那么假设第三位是x,第四位是y,简化一下就是x*y-0.2y=24,所以x和y为5的时候,等式成立。所以序列号为1555.输入验证下,正确。
这里面用到的浮点数汇编指令我整理了一下。
movsx 带扩展传送
mov BL,80H
movsx AX,BL
运行完后,AX的值为FF80H,由于BL为80H=1000 0000,最高位也即符号位为1,在进行带符号扩展时,其扩展的高8位均为1,AX为1111 1111 1000 0000,即AX=FF80H
fild/fstp
fild和fstp都是x87指令
fild是将整数转化为长双精FP80压栈(压到st0)
fstp是将弹栈指令,将st0弹出。
fidiv 浮点数除法
FIDIV src
st(0) <- st(0) /src (mem16/mem32)
fsubp
FSUBP st(i),st
st(i) <-st(i) - st(0),然后执行一次出栈操作
fimul 乘上一个整数
FIMUL src
st(0) <- st(0) * src (mem16/mem32)
fld
FLD src
装入实数到st(0)
st(0) <- src (mem32/mem64/mem80)
fcomp
实数比较后出栈
fstsw
FSTSW dest
保存状态字的值到dest
sahf
将AH存至FLAG低8位
本指令将用AH的内容改写FLAGS标志寄存器中的SF、ZF、AF、PF、和CF标志,从而改变原来的标志位.