海天一色001 发表于 2019-6-16 14:11

160个Crackme之026学习笔记

第26个CM程序,仍然是VB程序:

点击“chEcK iT !”按钮,弹出提示框,说明name至少需要5个字符。当满足条件后,再点“chEcK iT !”按钮,程序没有任何反应了。

第一步、查壳:

未加壳,用VB5编写:
第二步、爆破
还是先加载入OD,习惯性地使用智能搜索,本以为没什么发现,没想到还是找到了提示成功的字符串!
“Gratulation ,du hast es geschafft!”这一句使用网上翻译过来是“恭喜你,你成功了!”是成功的提示。

双击“Gratulation ,du hast es geschafft!”这一行来到CPU窗口:
00403745这一句向下看,到00403772处,调用msvbvm60.rtcMsgBox函数,应该是弹出成功提示框了;向上看,到004036E处指令为“je Colormas.0040379C”,跳过成功,走向失败,所以在这里nop掉:

修改的内容保存为可执行文件Colormaster_nop.exe,运行一下,输入name=“52pojie”,点击按钮,弹出成功提示,爆破完成。

第三步、追码
返回OD中,撤消修改,再观察代码:
和上一个CM有相似之处,都是先调用MSVBVM60.__vbaStrCmp函数,再经一大堆运算再来个test si,si,然后je 0040379C跳过成功。所以这次先在004036A2处下断,F9运行程序,输入name=“52pojie”, 点“chEcK iT !”按钮,程序中断于004036A2处,此时==“UNICODE "51848C526CA533447-CM"”,堆栈窗口中也显示出两串字符,下面是输入的假码,上面的第一反应就是真serial了。输入026中试试,果然如此。



那么就要再向上查看代码,查找生成注册码的算法了。
先用一下常用的VB动态调试工具,martcheck试一试:

Smartcheck开始运行,就不断地开始了Timer1_timer事件,很快飙升到了6750个事件,而且还在不停地往下走。输入“52pojie/1234567890”点击“chEcK iT !”按钮,程序没有任何反应,Smartcheck除了Timer1_timer事件还在继续增加次数也没有什么反应。只好先关闭Colormaster.exe,点击Smartcheck中的Form1_load事件,看到了“NuMega Smartcheck”的字样,下面还有一个“URsoft”的字样,都是VB运行错误的提示,可能是CM作者反调试的吧:

点击下面的任一Timer1_timer事件,显示内容都一样,说明是进入了无限循环状态了,这应该是反调试的代码作用了。

从网上找到一个API监控的软件,据说也是很牛的SoftSnoop:

点击确定关闭提示,向上查看一下,确实很牛,不仅查到了调用的系统模块,也列出了VB控件及事件地址,还将MSVBVM60.DLL中的函数也显示出来了。下面是SoftSnoop显示的调试结果:
开始查找模块.....
查询到模块开始于: 0x00400000终止于: 0x00407000模块名称: D:\CRACKME160练习\026\Colormaster.exe
查询到模块开始于: 0x73390000终止于: 0x734E3000模块名称: MSVBVM60.DLL
查询到模块开始于: 0x76300000终止于: 0x7631D000模块名称: IMM32.dll
查询到模块开始于: 0x76990000终止于: 0x76ACE000模块名称: ole32.dll
查询到模块开始于: 0x770F0000终止于: 0x7717B000模块名称: OLEAUT32.dll
查询到模块开始于: 0x77BE0000终止于: 0x77C38000模块名称: msvcrt.dll
查询到模块开始于: 0x77D10000终止于: 0x77DA0000模块名称: USER32.dll
查询到模块开始于: 0x77DA0000终止于: 0x77E49000模块名称: ADVAPI32.dll
查询到模块开始于: 0x77E50000终止于: 0x77EE3000模块名称: RPCRT4.dll
查询到模块开始于: 0x77EF0000终止于: 0x77F39000模块名称: GDI32.dll
查询到模块开始于: 0x77FC0000终止于: 0x77FD1000模块名称: Secur32.dll
查询到模块开始于: 0x7C800000终止于: 0x7C91E000模块名称: KERNEL32.dll
查询到模块开始于: 0x7C920000终止于: 0x7C9B6000模块名称: ntdll.dll
共查找到13个模块.....
DetourTransactionCommit函数处理中........(10秒内未返回说明调用失败)
成功获取函数: 7165......
   
ObjectTable地址: 0x401C70
[+0x2A]对象数量: 1
[+0x40]szProjectName: "Projekt1"
Object地址: 0x401CC4
    第对象名称:"Form1"
      Control: "Label5"
      Control: "Text2"
      Control: "Timer1"
      Control: "Label4"
      Control: "Text1"
      Control: "Form"
      Control: "Command1"
      Control: "Label1"
      Control: "Label2"
      Control: "Command2"
      Control: "Label6"
      Control: "Label7"
      Control: "Label3"
      EventTable地址: 0x402508
      [+0x00]Form1_Command1: 0x402B10
      [+0x04]Form1_Command2: 0x403890
      [+0x08]Form1_Form: 0x403970
      [+0x0C]Form1_Timer1: 0x403C60
      
返回地址: 0040133E函数名称: ThunRTMain(MSVBVM60.DLL)
ThunRTMain: 初始化进程并获取进程ID
          lpstring="VB5!?VB6DE.DLL"
   
返回地址: 733E1CE3函数名称: Form1_Form(程序内部函数)
   
返回地址: 004039D7函数名称: __vbaOnError(MSVBVM60.DLL)
__vbaOnError: On Error遇到错误的处理方式
          OnErrEvent=0x00000001
__vbaOnError返回值: 0x00000001
   
返回地址: 004039F8函数名称: __vbaVarDup(MSVBVM60.DLL)
__vbaVarDup: 变量复制,从变量1复制到变量2
          Var1=0x0012FB20
          Var2=0x0012FBF0
__vbaVarDup返回值: 0x0012FAD0 (vb字符串:"NuMega Smartcheck")
   
返回地址: 00403A06函数名称: rtcAppActivate(MSVBVM60.DLL)
   
返回地址: 00403AB2函数名称: __vbaResume(MSVBVM60.DLL)
__vbaResume返回值: 0x00000000
   
返回地址: 00403AC2函数名称: __vbaOnError(MSVBVM60.DLL)
__vbaOnError: On Error遇到错误的处理方式
          OnErrEvent=0x00000002
__vbaOnError返回值: 0x00000002
   
返回地址: 00403ADF函数名称: __vbaVarDup(MSVBVM60.DLL)
__vbaVarDup: 变量复制,从变量1复制到变量2
          Var1=0x0012FB20
          Var2=0x0012FBF0
__vbaVarDup返回值: 0x0012FAD0 (vb字符串:"URSoft")
   
返回地址: 00403AED函数名称: rtcAppActivate(MSVBVM60.DLL)
   
返回地址: 00403C16函数名称: __vbaExitProc(MSVBVM60.DLL)
__vbaExitProc返回值: 0x00000000
rtcAppActivate返回值: 0x00000000
   
返回地址: 00403B1D函数名称: rtcSendKeys(MSVBVM60.DLL)
rtcSendKeys返回值: 0x00000001
   
返回地址: 00403B53函数名称: __vbaObjSetAddref(MSVBVM60.DLL)
__vbaObjSetAddref返回值: 0x0015A128
   
返回地址: 00403B6D函数名称: __vbaHresultCheckObj(MSVBVM60.DLL)
卸载ApiSnoop dll.
成功卸载HOOK总数: 7169
和Smartcheck一样,应该是CM程序检测到“NuMega Smartcheck”这样的调试工具,让程序进入死循环。
以前练习过怎么干掉反调试功能,这次再试一试吧:
先记下CM的Form_load事件开始地址0x403970,再用OD载入Colormaster.exe,Ctrl+G,输入403970,来到Form_load事件开始地址,观察反汇编代码:

到004039D1处,调用MSVBVM60.__vbaOnError函数,因为OD与“NuMega Smartcheck”不同,所以没有触发错误处理程序:
004039CF   .6A 01         push 0x1                                                   ; /OnErrEvent = Goto Address
004039D1   .FF15 4C104000 call dword ptr ds:[<&MSVBVM60.__vbaOnError>]               ; \__vbaOnError
004039D7   .C745 FC 03000>mov dword ptr ss:,0x3
004039DE   .C745 B4 0C204>mov dword ptr ss:,Colormas.0040200C            ;NuMega Smartcheck
004039E5   .C745 AC 08000>mov dword ptr ss:,0x8
继续向下,又调用了rtcSendKeys函数,作用是模拟键盘输入,估计也是反调试的吧:
00403A26   .8D4D BC       lea ecx,dword ptr ss:
00403A29   .51            push ecx
00403A2A   .68 34204000   push Colormas.00402034                                     ;%{F4}~
00403A2F   .FF15 60104000 call dword ptr ds:[<&MSVBVM60.#rtcSendKeys_599>]         ;msvbvm60.rtcSendKeys
一直到00403C56处,Form_load事件结束,指令是retn 0x4。
那么可以直接在段首地址处改指令为retn 0x4,跳过这一段代码,是不是就能去掉反调试功能呢?想到就做,

将修改后的程序保存为Colormaster.fts.exe,打开Colormaster.fts.exe,能正常打开,不输入name/serial,点击“chEcK iT !”按钮,弹出错误揭示;输入name/serial,点击“chEcK iT !”按钮,程序没什么反应,也是正常的。
用Smartcheck加载Colormaster.fts.exe,运行,这次正常进行了,不过Smartcheck运算过程不够完整,还差几步没有完成:

再使用SoftSnoop,打开Colormaster.fts.exe,马上开始调试,弹出CM界面:

输入name/serial, 点击“chEcK iT !”按钮,CM界面上name框看到“52pojie”迅速依次删除,又迅速出现,而SoftSnoop信息一直到了真假码比较:

看来反调试功能已破解,而SoftSnoop确实很牛,以后多用一下吧!
从SoftSnoop调试信息最后几个函数的解析中很轻松地看到了serial是六段字符相连的结果,“51848”、“C526”、“CA53”、“344”、“7”、“-CM”,分别使用__vbaVarCat和__vbaStrCat函数进行变量及字符串联接,最后的“7”、“-CM”很容易知道一个是len(name)=7,一个是程序中设定的字符串常量“-CM”,但其他的几段还是搞不清楚是怎么来的,那就从“chEcK iT !”按钮事件地址00402B10处开始要真对照OD中的反汇编代码仔细研究:
在00402B10处下断,Ctrl+F12,F9运行,程序中断,F8向下单步,
从00402CB2至00402D97处为预处理name字符串,即要求name的字符个数不少于5个;
从00402DF1至00403094处先取name字符串长度为循环次数,然后进行for...next循环:
其中每次到00402EB1处得到name字符串,在此处下断,取消段首断点;
00402EB4处将取得的字符压栈,00402EB5处把字符转为数据,在eax反馈结果,00402EBE处将这个数据存入ss:中,即ss:== hex(asc(name(i)));
这一小段代码中如果没对照OD可能还不迷糊,对照之后却有点不明白:如第一次循环00402EB1运行后得到name字符串,信息窗口和堆栈中均显示堆栈 ss:=00160074, (UNICODE "52pojie"),寄存器窗口中eax为00160074,

在数据窗口中跟随,显示如下:

下一步压栈应该是整体压入啊,但实际上只压入了第一个字符“5”,这一点以前就有疑问,到现在还没有解决,只是看到注释中出现了,没有深究下去。可是SoftSnoop中的注释是“BString=0x00160134”,从数据窗口查找到0x00160134处,显示内容是“00”,和“35”对不上啊!盼望有大神能给我解惑!
继续向下,到00402F12处,得到= "432.4",然后转化为实数形式;到00402F97处,都是浮点数运算,结果是ST=asc(name(i)*432.4* 17.78999999999999/15;循环结束后,将结果存入ss:中;到00402FA2处,调用__vbaStrVarVal函数,将结果转化为字符串;
到00403027处,会跳出OD,跳到CM界面,到00403065处,调用msvbvm60.rtcSendKeys函数,模拟键盘输入,作用是到name框首位,删除一个字符,这就是在SoftSnoop调试时看到CM界面上name依次消失的原因了,本以为是反调试的内容,后来发现应该是程序正常的代码,少了它还不行,第一次运行本句后name字符串变成了“2pojie”,到第二次循环开始时才能得到第二个字符进行运算:
到00403094处,构成for...next循环:在调试时又出现了F8单步在00402E64到00403094之间只取第一个字符无限循环的现象,这是为什么也是要认真弄懂的一个问题。
只有在循环外00403099处下断用F9运行,才会正常向下运行,取得name字符串的后面几个字符。
每次运行F9后,CM程序界面显示name框内容比上一次少了前一个字符,如下图所示,一直到没有字符显示,然后字符又会全部出现在name框中,和SoftSnoop调试时出现的情况一致:

本以为是name每个字符都运算后相加减或者其他运算才利用了循环,检查后发现循环没有什么用处,只取最后一个字符计算就行了,这一段的作用就是ST= asc(name(len(name)))*432.4* 17.79/15;是注册码前三段的基础(或前置性数字),我用“52pojie”计算的结果是“51795.000000000000”;
继续向下,004030F6处调用msvbvm60.__vbaR8Str函数,把字符串转变为实数;
004030FC处,调用msvbvm60.__vbaFPFix函数,对浮点寄存器st0四舍五入取整;
00403108处,调用msvbvm60.__vbaStrR8函数,将数值转成文本;
一直到004032DD处,=ss:=name.text,
继续到00403319处,ST0清零,ss:=ST+asc(name(1)= asc(name(1)+ asc(name(len(name)))*432.4* 17.79/15(双精度浮点数),此时的结果是“51848”,是注册码的第一段内容;
从00403361至004033AF处,先得到第一段代码计算出的字符串,在00403365处用msvbvm60.__vbaR8Str函数再转成实数;0040336B处= hex(asc(name(1))*25);到00403381处再转成10进制数(double类型)存入ST中,再在00403396处存入ss:中,0040339C处ST=ST0-ss:=51795.000000-asc(name(1))*25;然后存入ss:中;004033AF处调用msvbvm60.rtcHexVarFromVar函数将ss:的值转化为十六进制数表示,这里的结果是“C526”,作为注册码的第二段;
004033E6至0040340E处,先得到字符串“51795”,再调用msvbvm60.rtcHexVarFromVar函数转换成十六进制形式为“CA53”,作为注册码的第三段;
00403482至004034C1处,先得到name字符串,再取第一个字符的数值,乘以name字符串的长度,再减去0x1B,得到数值为0x158,转换成十进制数表示为344,作为注册码的第四段;
0040365C至0040366F处,取ame字符串的长度(7),作为注册码的第五段;
00403693处,程序中的固有的字符UNICODE "-CM",作为注册码的第六段。
这六段连接起来就是相应的注册码了。
按照这个思路用VB写了一个注册机,输入了几个不同的name进行测试,将生成的Serial拷贝到CM程序中,都成功了。说明注册算法是正确的。
Private Sub Command1_Click()
    Dim Name, Serial1, Serial2, Serial3, Serial4, Serial5, Serial6, Serial7
    If Len(Text1.Text) < 5 Then
      Text1.Text = "52pojie"
      Text2.Text = "51848C526CA533447-CM"
      MsgBox ("Name至少要输入5个字符,因你的输入不合要求,所以默认为52pojie。如果不愿用默认Name,请重新输入!")
    End If
    Name = Text1.Text
    Serial1 = Fix(Asc(Mid(Name, Len(Name), 1)) * 432.4 * 17.79 / 15)
    Serial2 = Serial1 + Asc(Mid(Name, 1, 1))
    Serial3 = Hex(Serial1)
    Serial4 = Hex(Serial1 - Asc(Mid(Name, 1, 1)) * 25)
    Serial5 = Asc(Mid(Name, 1, 1)) * Len(Name) - 27
    Serial6 = Len(Name)
    Serial7 = "-CM"
    Text2.Text = Serial2 & Serial4 & Serial3 & Serial5 & Serial6 & Serial7
End Sub
当然,不可避免地可能有溢出问题,这也是一个长期学习的知识点,盼望有大神能指点一下!!!
附件内含原CM程序、Nop爆破程序,fts反调试程序、OD的调试文件、SoftSnoop的调试文件(debug.txt)、注册机等,百度链接是:http://pan.baidu.com/s/1skMkJY9,密码:86pm,160个CM、我已练习过的前26个crackme程序(不含012)都在里面。

Yang|阳 发表于 2019-6-16 15:05

感谢分享!~~

xiaoxue_8899 发表于 2019-6-17 08:08

大神啊,根本看不懂啊。

绵绵青山 发表于 2019-6-17 09:01

收藏,感谢分享!

wubudomain 发表于 2019-6-17 10:04

谢谢分享,支持支持。

young24 发表于 2019-6-17 10:31

感谢分享

Nonoby 发表于 2019-6-17 12:00

很牛逼的样子{:1_901:}

郁闷lan 发表于 2019-6-21 16:04

厉害了,感觉很多都看不懂

lujiejie636625 发表于 2019-6-21 16:13

能坚持,挺厉害的。
页: [1]
查看完整版本: 160个Crackme之026学习笔记