CrackMe 003是002的加强版,我们看看加强在哪里。
OD运行,发现提示代码可能显示会出现问题,有压缩,用PEDI查一下,
发现程序并没有壳,这样即使有压缩,我们也可以手动脱掉。
首先,伪码检测:
进入OD,查找参考文本字符串,之后Call进去
向上大致查看一下程序的流程,然后找到函数的头部,F2下断,注意这个函数有点长,需要耐心一点
F9跑起来,输入我们的伪码,开始单步调试,还是同样的原则,先F8,遇到不认识的VB API函数就去查一下他是干什么的,我们需要做的是了解程序的流程,再遇到类似vbaobjset或者vbastrset等明显无关的函数时候可以过得稍微过得快一点,知道看到了熟悉的一段代码;
004081E9 > \8B95 50FFFFFF mov edx,dword ptr ss:[ebp-0xB0] 004081EF . 8B45 E4 mov eax,dword ptr ss:[ebp-0x1C] 004081F2 . 50 push eax ; /String = "444489" 004081F3 . 8B1A mov ebx,dword ptr ds:[edx] ; | 004081F5 . FF15 F8B04000 call dword ptr ds:[<&MSVBVM50.__vbaLenBs>; \__vbaLenBstr 004081FB . 8BF8 mov edi,eax ; EDI=计算字符串的长度 004081FD . 8B4D E8 mov ecx,dword ptr ss:[ebp-0x18] 00408200 . 69FF 385B0100 imul edi,edi,0x15B38 ; EDI=字符串长度乘以0x15B38 00408206 . 51 push ecx ; /String = "蘦" 00408207 . 0F80 B7050000 jo AfKayAs_.004087C4 ; | 0040820D . FF15 0CB14000 call dword ptr ds:[<&MSVBVM50.#516>] ; \rtcAnsiValueBstr 00408213 . 0FBFD0 movsx edx,ax ; 计算第一个字符的ASCII码值 00408216 . 03FA add edi,edx ; EDI=字符串长度乘以0x15B38+第一个字符的ASCII码值 00408218 . 0F80 A6050000 jo AfKayAs_.004087C4 0040821E . 57 push edi 0040821F . FF15 F4B04000 call dword ptr ds:[<&MSVBVM50.__vbaStrI4>; msvbvm50.__vbaStrI4 00408225 . 8BD0 mov edx,eax ; EDI结果转成10进制数 00408227 . 8D4D E0 lea ecx,dword ptr ss:[ebp-0x20]
这段汇编代码与CrackMe2中的代码的流程是一样的,算法就是
(name的长度)*0x15B38+第一个字符的ASCII码值)转成10进制字符串,这个简单。
之后,我们再向后看,肯定有不同的位置,发现出现了浮点数运算:
004082E9 . FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>; _msvbvm50._vbaR8Str name转换成浮点数,结果在浮点寄存器里 004082EF . D905 08104000 fld dword ptr ds:[0x401008] ; 将0401008中的硬编码实型存入st0 004082F5 . 833D 00904000>cmp dword ptr ds:[0x409000],0x0 004082FC . 75 08 jnz short AfKayAs_.00408306 004082FE . D835 0C104000 fdiv dword ptr ds:[0x40100C] 00408304 . EB 0B jmp short AfKayAs_.00408311 00408306 > FF35 0C104000 push dword ptr ds:[0x40100C] 0040830C . E8 578DFFFF call <jmp.&MSVBVM50._adj_fdiv_m32> 00408311 > 83EC 08 sub esp,0x8 00408314 . DFE0 fstsw ax ; 将st0中的值复制到ax中
这里推荐一个介绍x86下的浮点数汇编的博客,很详细
inter x86 浮点寄存器笔记
我们继续对代码进行分析,到跳转条件前一共主要出现了四段浮点数的汇编部分:
第一部分:
004082E7 . 8B19 mov ebx,dword ptr ds:[ecx] 004082E9 . FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>; _msvbvm50._vbaR8Str name转换成浮点数,结果在浮点寄存器里 004082EF . D905 08104000 fld dword ptr ds:[0x401008] ; 将0401008中的硬编码实型存入st0 004082F5 . 833D 00904000>cmp dword ptr ds:[0x409000],0x0 004082FC . 75 08 jnz short AfKayAs_.00408306 004082FE . D835 0C104000 fdiv dword ptr ds:[0x40100C] 00408304 . EB 0B jmp short AfKayAs_.00408311 00408306 > FF35 0C104000 push dword ptr ds:[0x40100C] 0040830C . E8 578DFFFF call <jmp.&MSVBVM50._adj_fdiv_m32> 00408311 > 83EC 08 sub esp,0x8 00408314 . DFE0 fstsw ax ; 将st0中的值复制到ax中 00408316 . A8 0D test al,0xD 00408318 . 0F85 A1040000 jnz AfKayAs_.004087BF 0040831E . DEC1 faddp st(1),st ; ;将st(num)和st相加,用和来替换st(num),将st出栈 00408320 . DFE0 fstsw ax 00408322 . A8 0D test al,0xD 00408324 . 0F85 95040000 jnz AfKayAs_.004087BF 0040832A . DD1C24 fstp qword ptr ss:[esp] 0040832D . FF15 48B14000 call dword ptr ds:[<&MSVBVM50.__vbaStrR8>; msvbvm50.__vbaStrR8 00408333 . 8BD0 mov edx,eax
其实这部分并没有做什么实质上的工作,不过通过浮点数运算对内存中的数据进行改动(我们后面会发现这些内存地址都用了,但我们并不需要考虑这些,直接当做硬编码处理就可以了)。
第二部分:
004083F3 . 8B19 mov ebx,dword ptr ds:[ecx] 004083F5 . FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>; msvbvm50.__vbaR8Str 004083FB . DC0D 10104000 fmul qword ptr ds:[0x401010] ; name=name*3 00408401 . 83EC 08 sub esp,0x8 00408404 . DC25 18104000 fsub qword ptr ds:[0x401018] ; name=name-2 0040840A . DFE0 fstsw ax 0040840C . A8 0D test al,0xD 0040840E . 0F85 AB030000 jnz AfKayAs_.004087BF 00408414 . DD1C24 fstp qword ptr ss:[esp] ; 结果存入堆栈 00408417 . FF15 48B14000 call dword ptr ds:[<&MSVBVM50.__vbaStrR8>; msvbvm50.__vbaStrR8 0040841D . 8BD0 mov edx,eax
可以看到,这部分对输入的name进行了简单的浮点运算:name=name*3-2
第三部分:
004084DD . 8B19 mov ebx,dword ptr ds:[ecx] 004084DF . FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>; msvbvm50.__vbaR8Str 004084E5 . DC25 20104000 fsub qword ptr ds:[0x401020] ; ;str+15 004084EB . 83EC 08 sub esp,0x8 004084EE . DFE0 fstsw ax 004084F0 . A8 0D test al,0xD 004084F2 . 0F85 C7020000 jnz AfKayAs_.004087BF 004084F8 . DD1C24 fstp qword ptr ss:[esp] 004084FB . FF15 48B14000 call dword ptr ds:[<&MSVBVM50.__vbaStrR8>; msvbvm50.__vbaStrR8 00408501 . 8BD0 mov edx,eax
又对name进行了简单的操作,name=name-(-0x15);
第四部分:
004085C8 . FF15 18B14000 call dword ptr ds:[<&MSVBVM50.__vbaHresu>; msvbvm50.__vbaHresultCheckObj 004085CE > 8B45 E8 mov eax,dword ptr ss:[ebp-0x18] 004085D1 . 50 push eax ; serial转换成浮点数 004085D2 . FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>; msvbvm50.__vbaR8Str 004085D8 . 8B4D E4 mov ecx,dword ptr ss:[ebp-0x1C] 004085DB . DD9D 1CFFFFFF fstp qword ptr ss:[ebp-0xE4] 004085E1 . 51 push ecx ; 我们输入的serila转化成浮点数 004085E2 . FF15 74B14000 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>; msvbvm50.__vbaR8Str 004085E8 . 833D 00904000>cmp dword ptr ds:[0x409000],0x0 004085EF . 75 08 jnz short AfKayAs_.004085F9 004085F1 . DCBD 1CFFFFFF fdivr qword ptr ss:[ebp-0xE4] ; name/serial值存入st0 004085F7 . EB 11 jmp short AfKayAs_.0040860A 004085F9 > FFB5 20FFFFFF push dword ptr ss:[ebp-0xE0] 004085FF . FFB5 1CFFFFFF push dword ptr ss:[ebp-0xE4] 00408605 . E8 888AFFFF call <jmp.&MSVBVM50._adj_fdivr_m64> 0040860A > DFE0 fstsw ax 0040860C . A8 0D test al,0xD 0040860E . 0F85 AB010000 jnz AfKayAs_.004087BF 00408614 . FF15 34B14000 call dword ptr ds:[<&MSVBVM50.__vbaFpR8>>; msvbvm50.__vbaFpR8 0040861A . DC1D 28104000 fcomp qword ptr ds:[0x401028] ; ;判断name/serial==1? 00408620 . DFE0 fstsw ax 00408622 . F6C4 40 test ah,0x40
第四部分实现的就是上面步骤生成的serial与我们输入的serial进行比较,只不过利用了有一点迷糊性质的浮点数除法,判断结果是否等于1而已。
现在在name=12345的情况下我们得到的serial字符串:
OK,对现在分析得到的serial进行检验:
正确,好的,我们在总结一下算法
首先通过(name的长度)*0x15B38+第一个字符的ASCII码值)转成10进制字符串算出serial,然后serial*3-0x2+0x15算出serial。
写出注册机:(python)
str1=raw_input(‘input your name:‘) length=str1.__len__() c=ord(str1[0]) s=length*0x15B38+c serial=s*3-0x2+0x15 print serial
结果:
检验:
最后,其实很简单,不过用浮点数汇编来迷惑罢了。