吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4528|回复: 12
收起左侧

[原创] 160个CrakeMe程序之017练习

  [复制链接]
海天一色001 发表于 2019-4-7 13:15
本帖最后由 海天一色001 于 2019-4-7 14:15 编辑

(由于各种原因断断续续,这个程序破解过程写了有一年多了!我是按照自己的练习过程来写的,很啰嗦,隔了一段时间发现中间有错误的地方,重新进行了修改,有的地方加上了新的内容,所以拖得时间很长,可能前后不连贯,敬请大家谅解!)

第17个CM程序,解压文件后有一个主程序和一个README文件,先打开这个README文本,提示不能用SmartCheck这个反编译器,只能找到正确的注册码。可见14、16、17这三个CM是逐步增加难度的程序,通过练习,我们确实能学习到新的知识。不过还是先试着爆破了,再找注册码。
先打开程序,点击“About”菜单,弹出信息框,大意是“这个CM程序制作出来是为了提高新手们在VB方面的R.C.E知识的!(R.C.E是什么意思?)希望你们能从中学习到一些知识!”,与第16个CM相比,除了版本号变成3之外,提示内容基本一样,没有可以利用的地方;
001.png
在注册码文本框中随意输入字符,点击“Check it”按钮,程序弹出错误对话框:
002.png
老规矩,查壳,程序用VB编写,未加壳:
003.png
第一步,爆破程序:
将程序载入OD中,智能搜索字符串,可以看出先判断是否用SmartCheck加载了程序,然后明显的两组文字说明,查了一下,“cheater”是作弊者的意思,下一句是说你在用SmartCheck加载了程序,先关闭调试程序重新再试。失败的在前面,成功的在后面。也就是说这个程序里对反编译进行了一定的限制,如何绕过这个限制,或者在限制之下找到注册算法,这是更进一步的知识点了。这可能是“About”里面说的R.C.E吧!
我先试着爆破程序,使它不管是否输入注册码都能弹出正确信息来,然后再试着将Anti-SmartCheck的限制去掉看看。
004.png
双击00404E6A地址,进入CPU窗口:
005.png
向上看,很轻松地看到00404E27地址处一个比较指令后面跟着跳转指令,跳过了正确信息来到了失败,所以可以先在00404E30地址将跳转nop掉试一试:
006.png
在00404E30处先下断点,然后F9运行程序,
007.png
程序失败,所以继续向下查看代码,很快看到00404ED9处的指令“jmp BJCM30A.00404F86”跳过了失败,但在这个跳转之后的下面00404EDE地址处的跳转来自0040459F, 00404941,这两个地址都在00404E30地址之前,使程序跳过了正确,所以要让这两个跳转失效;下一句00404EE3地址处的跳转来自00404E30,也使程序跳过了正确,已经被nop掉了:
008.png
要让00404EDE地址处的这条跳转命令失效,在信息窗口中“跳转来自0040459F, 00404941”一行上右键点击,先选“转到jnz来自0040459F”来到地址0040459F处将此处nop掉,
009.png
再Ctrl+G转到00404941处再nop掉这个跳转后,运行程序,点掉错误提示,再点击“Check it”按钮,程序中断,F9继续,程序弹出正确信息框!再点掉,随意输入字符后点击“Check it”按钮,程序中断,F9继续,又弹出正确信息框;反复试几次后,程序均弹出正确信息,说明爆破成功了,保存为BJCM30A_nop.EXE,试运行,均成功了。
第二步,去反调试:
VB程序,应该可以用SmartCheck这个软件进行调试,但本软件提示不能用它,先试一试看结果怎样:
用SmartCheck打开BJCM30A.EXE,运行,输入“1234567890”,点击“Check it”按钮,等了一会儿,跳出错误对话框,提示在用SmartCheck,但SmartCheck仍在不停运行,右下角的程序事件一直跳到76万多才停下,点掉错误提示,重新点击 “Check it”按钮,程序又提示了不能用SmartCheck!
smartchk-1.png   
点开Command1_Click事件,看到取了两次时间,然后就是错误提示框了!
smartchk-2.png
看来真不能用SmartCheck啊。
那就用VB Decompiler来加载程序吧:
010.png
从前面几个程序的破解过程中可以轻松地判断出CrackmeV30a是主窗体,Command1_Click是“Check it”按钮。再选择Code -> CrackmeV30a -> Command1_Click_404230,右侧出现按钮事件的代码,将解析的代码与OD对照,可以慢慢地找到注册码的算法。
我一直以来都选中了“程序分析器和优化器”选项,所以代码解析后很简洁,利于迅速把握程序整体结构,但很多重要的参数都没有标明,有时真是看不懂。这个程序本来也是这样解析出来的,经过几天来断续分析,今天重新打开VB Decompiler时鼠标滑到了“程序分析器和优化器”选项上,将其取消了,再反编译后,解析后代码大不一样了!这次的解析可能很繁杂,我反而能明白很多东西了!特别是算法,应该是比较清楚了!
011.png
以后应该这两种方式都要运用啊!不知道这次分析还需要多少天,只要每一次都有进步,我就会坚持下来!!!
在OD中CPU窗口上按Ctrl+G键,输入“00404230”,来到计算注册码代码的段首处,在此下断,F9运行程序,输入字符“1234567890”,点击“Check it”按钮,程序中断。
012.png
F8单步向下运行,注意查看寄存器、信息栏、堆栈窗口的信息,可惜对VB的数据存储方法还是摸不到头脑,找不到什么可用的信息。
运行至00404270地址处,命令是call dword ptr ds:[ecx+0x4],OD自动注释的是“msvbvm60.Zombie_AddRef”,不知道是干什么用的,VB Decompiler中这个地址处的解析是var_eax = Me.AddRef,百度一下,也没有完全搞清楚什么意思,大概是调用函数前先AddRef增加引用计数,传给函数,使用完再Release,这样可防止COM对象使用期间被卸载了。
先不管它了,在OD中继续单步运行到00404320地址处,调用了一个和时间有关的函数!再查,这可能是以时间为种子来取随机数后作为注册码运算的参数吧!
013.png
继续单步向下进行,结合VB Decompiler中的解析,看到0040432C地址处,var_5C= CLng(Timer) ,取当前时间换算成一个长整型的数值存入var_5C中;再往下分析,从0040438B到00404461地址处是一个大循环包含一个小循环,还是要一点点地搞明白它们的具体作用:
1111111111111111111111111111111111.png
从0040433B至0040438B处这一段里面的代码OD自动注释了一些内容,step是循环的步长或者是增量大小,end是循环结束的值,start是循环的初始值,TMPend是终值暂存的地方,TMPstep应该是步长暂存的地方,Counter是计数器,运行到00404391处时可以从上图中看出step=6,end=1,start=2,Tmpend=6,Tmpend=6,Counter=2,跟我想象中的结果一点也不一样,运行了好几次也不明白是如何储存及读取数值的,对SS:[ebp-0x118]之类的堆栈值的查看始终没看明白,只知道这是一个循环的初始化(__vbaVarForInit)过程,下面仍有__vbaVarForNext与之相类似,所以只能先略过分析,看VB Decompiler解析出的注释“For var_80 = 1 To 1000 Step 1”,明白了start =1,end=1000, step =1就够了:
015.png       014_1111111.png
从VB Decompiler中这段解析来看,每次小循环结束后的值始终是var_24 = CInt(1),所以这个小循环可以简化成var_24 = CInt(1);再看大循环,结果还是这个var_24 = CInt(1)!在OD中单步执行,运行到小循环后就运行不下去了,难道真的运行250次吗?即使小循环完了,大循环还有1000次呢!所以感觉这段代码可能就是反调试用的,让人进入一个死循环吧。也不知道对不对。下面是对小循环的代码分析:
[Asm] 纯文本查看 复制代码
004043ED FF 15 38 call      dword ptr ds:[<&MSVBVM60.__vbaVarForInit>]   ;小循环开始:重复执行初始化
004043F3 3B C7    cmp       eax, edi                                   ; 比较eax,edi的大小,作为下一步跳转的依据 
004043F5 74 4D    je        BJCM30A.00404444                          ; 如果eax=edi则跳至00404444地址处,跳出本循环  

仔细分析,因为在004043F3处的edi始终为0,eax为1,所以无法跳出;
(如果此时跳至00404444地址处,那么从下面的代码分析也就很可能跳出了大循环,跳过反调试的内容,进入注册算法代码段中。)
[Asm] 纯文本查看 复制代码
004043F7 68 34 2A push      BJCM30A.00402A34                             ;Unicode "IS SMARTCHECK LOADED???"
004043FC 68 34 2A push      BJCM30A.00402A34                             ;Unicode "IS SMARTCHECK LOADED???"
00404401 FF 15 68 call      dword ptr ds:[<&MSVBVM60.__vbaStrCmp>]       ;字符串比较,结果存入eax中,相等为0,不等为1;
00404407 85 C0    test      eax, eax                                     ; 测试eax是否为0,
00404409 75 1F    jne       BJCM30A.0040442A                          ; 不为0则跳

因为00404401处的字符串比较的内容是一样的,所以下面跳出循环也不能实现,但这一段应该没什么作用吧
[Asm] 纯文本查看 复制代码
0040443B 51       push      ecx                                          
0040443C FF 15 E8 call      dword ptr ds:[<&MSVBVM60.__vbaVarForNext>]   ; 重复执行小循环结构, For... Next...  (Loop)
00404442 EB AF    jmp       BJCM30A.004043F3                         ; 跳回小循环开始    

同样分析0040438B到00404461地址处的大循环,
[Asm] 纯文本查看 复制代码
0040438B   .  FF15 38104000 call dword ptr ds:[<&MSVBVM60.__vbaVarForInit>]   ; \__vbaVarForInit ‘For var_80 = 1 To 1000 Step 1
00404391   >  3BC7          cmp eax,edi
00404393   .  0F84 C8000000 je BJCM30A.00404461                               ;  ‘If var_14C Then

00404391地址处的eax为1,edi为0,所以00404393处的跳转不能实现;如果实现了,那么也应该是跳出了反调试代码,进入注册算法代码中。
继续向下分析OD代码至00404461处,又一次取时间,转化成整数形式:
[Asm] 纯文本查看 复制代码
00404461   > \FF15 94104000 call dword ptr ds:[<&MSVBVM60.#535>]              ;  msvbvm60.rtcGetTimer 随机数发生器
00404467   .  FF15 D0104000 call dword ptr ds:[<&MSVBVM60.__vbaFpI4>]         ;  msvbvm60.__vbaFpI4 vbaFpI4:是VB中的一个内部函数,它的功能是将一个浮点数(float)转4字节有符号整数(int)
0040446D   .  2B45 A4       sub eax,dword ptr ss:[ebp-0x5C]                   ;  ‘CLng(Timer) = CLng(Timer) - var_5C
00404470   .  0F80 340C0000 jo BJCM30A.004050AA                               ;  ’If Err.Number <> 0 Then
00404476   .  83F8 05       cmp eax,0x5
00404479   .  0F8E AD000000 jle BJCM30A.0040452C                              ;  ‘If CLng(Timer) > 5 Then失败

到0040446D地址处,解析是 CLng(Timer) = CLng(Timer) - var_5C,当前时间减去第一次取值的时间差,这个差值小于5秒时,程序跳转至注册算法,
如果大于5秒,则程序调用错误对话信息框,结果走向失败(GoTo loc_00404F86)。
014.png
所以说从00404320地址至0040452C处就是反调试的内容!因为进行调试的话大多步骤是单步进行,如果真的单步运行了(250X1000=)25万次循环,这样下来时间差绝对大于5秒,程序会显示“You have SmartCheck loaded!...Close it and try again!!!”的错误提示,那么反反调试那就可以直接从00404320跳到0040452C处,直接进入注册算法代码中去!试一试,将00404320处代码改为 jmp 0040452C,另存为可执行文件BJCM30A_antismack.EXE,运行程序,没问题。
用SmartCheck载入BJCM30A_antismack.EXE,运行程序,点击“Check it”按钮后马上跳出来错误对话框,而且点开Command1_Click事件,看到了和原程序截然不同的代码,说明已跳过了反调试的程序段。
smartchk-4.png
第三步,追注册码:
OD中点击0040452C地址这一行,F4键运行到此处,对照VB Decompiler的解析,继续单步运行至0040454F处,
[Asm] 纯文本查看 复制代码
0040454F   .  FF90 A0000000             call dword ptr ds:[eax+0xA0]                           ;  var_84 = Text1.Text
00404555   .  3BC7                      cmp eax,edi
00404557   .  DBE2                      fclex                                                  ;  在检查未决的无掩码浮点异常之后,清除浮点异常标志。
00404559   .  7D 12                     jge short BJCM30A.0040456D                             ;  大于等于时跳转至0040456D

跳转成功,继续向下:
[Asm] 纯文本查看 复制代码
0040456D   >  8B95 7CFFFFFF             mov edx,dword ptr ss:[ebp-0x84]                        ;  取假码
00404573   .  52                        push edx                                               ; /String = “1234567890”
00404574   .  FF15 14104000             call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>]           ; \eax=假码长度(0xA)
0040457A   .  33DB                      xor ebx,ebx
0040457C   .  83F8 05                   cmp eax,0x5                                            ;  注册码长度与5比较,eax>5,zf=0,sf=0,of=0
0040457F   .  0F9CC3                    setl bl                                                ;  setl,即set when less,测试一个有符号比较。(指令:setl D效果是D<-SF^OF  也就是将符号标志位和溢出标志位异或的结果给D寄存器)setl bl; SF==1 或者 OF==1 时 bl=1,否则bl=0
00404582   .  8D8D 7CFFFFFF             lea ecx,dword ptr ss:[ebp-0x84]
00404588   .  F7DB                      neg ebx                                                ;  ebx求补,即0-ebx
0040458A   .  FF15 F0104000             call dword ptr ds:[<&MSVBVM60.__vbaFreeStr>]           ;  msvbvm60.__vbaFreeStr
00404590   .  8D8D 5CFFFFFF             lea ecx,dword ptr ss:[ebp-0xA4]
00404596   .  FF15 F4104000             call dword ptr ds:[<&MSVBVM60.__vbaFreeObj>]           ;  msvbvm60.__vbaFreeObj
0040459C   .  66:3BDF                   cmp bx,di
0040459F   .  0F85 39090000             jnz BJCM30A.00404EDE                                   ;  If ebx<> 0 Then 跳到失败

具体分析这一段,其实就是注册码长度要不小于5位,0040457C处一个比较就可以了,后面的代码我感觉没什么用处,也可能是反汇编后程序自动加的内容吧。
继续向下进行,004045F8----00404623处与0040454F----00404574的代码作用完全一样,得到了输入的假码与假码长度:
[Asm] 纯文本查看 复制代码
004045F8   .  FF92 A0000000             call dword ptr ds:[edx+0xA0]                           ;  var_84 = Text1.Text
004045FE   .  3BC7                      cmp eax,edi
00404600   .  DBE2                      fclex                                              ;  浮点检查错误清除
00404602   .  7D 12                     jge short BJCM30A.00404616                             ;  If  var_84 < 0 Then
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
00404616   >  8B8D 7CFFFFFF             mov ecx,dword ptr ss:[ebp-0x84]
0040461C   .  51                        push ecx                                               ; /String = "1234567890"
0040461D   .  FF15 14104000             call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>]           ; \__vbaLenBstr(eax=0xA)
00404623   .  8985 00FFFFFF             mov dword ptr ss:[ebp-0x100],eax                       ;  var_100 = Len(var_84)

从自动注释中可以看出从00404635到0040466E是“for...next”循环中“for”条件的一些参数设置, 00404880到0040488C是“for...next”循环中“next” 条件的一些参数设置,结合VB Decompiler的解析,可以看出这又是一个循环:
loc_0040466E:   For var_6C = 1 To Len(var_84) Step 1
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
loc_00404886:     Next var_6C
以“11111”为例进行跟踪分析,运行到00404616,初始化循环的相关参数,
[Asm] 纯文本查看 复制代码
00404616 > > \8B8D 7CFFFFFF mov ecx,dword ptr ss:[ebp-0x84]               ;  ecx=假码

到0040466E,开始大循环:
[Asm] 纯文本查看 复制代码
0040466E > .  FF15 38104000 call dword ptr ds:[<&MSVBVM60.__vbaVarForInit>; \For var_6C = 1 To Len(var_84) Step 1
00404674   .  8D8D 7CFFFFFF lea ecx,dword ptr ss:[ebp-0x84]               ;  将假码地址传给ecx///指向假码地址的指针的地址
0040467A > .  8985 30FEFFFF mov dword ptr ss:[ebp-0x1D0],eax              ;  var_1D0 = var_18C(第一次循环时var_1D0 = 0x1)
到00404698,跳转前的比较:
00404698 > >  39BD 30FEFFFF cmp dword ptr ss:[ebp-0x1D0],edi              ;  CMP(var_1D0,edi)///第一次1D0=1
0040469E > .  0F84 F5010000 je <BJCM30A.loc_404899>                       ;  If var_1D0 = 0 Then 跳走

继续向下, 从00404779到00404787处,得到第n次循环第n个位置的1个字符:
[Asm] 纯文本查看 复制代码
00404779   .  8B8D 7CFFFFFF mov ecx,dword ptr ss:[ebp-0x84]               ;  ecx=假码///eax=n
0040477F   .  8B3D 54104000 mov edi,dword ptr ds:[<&MSVBVM60.#631>]       ;  msvbvm60.rtcMidCharBstr
00404785   .  50            push eax
00404786   .  51            push ecx
00404787   .  FFD7          call edi                                      ;  eax=假码字符串中的第n个(n为第n次循环的当次)///edx=2,ecx=0; <&MSVBVM60.#631>
00404789   .  8BD0          mov edx,eax                                   ;  edx=eax///eax为返回的字符的地址
0040478B   .  8D8D 74FFFFFF lea ecx,dword ptr ss:[ebp-0x8C]
00404791 > .  FFD3          call ebx                                      ;  msvbvm60.__vbaStrMove var_8C = Mid$(var_84, var_ret_1, var_B8)//将刚刚返回的字符的地址copy到ebp-0x8c///var_8C=假码字符串中的第n个

到00404791地址处,解析是var_8C = Mid$(var_84, var_ret_1, var_B8),代入相应的值为Mid$(text1.text,1,2),我的理解是从假码字符串的第1位开始取2个字符,应该就是“12”,但我在寄存器查看的结果var_8C(SS:[ebp-0x8C])地址为0012F584,显示的值为UNICODE的字符“1”,是VB Decompiler的解析发生错误了,还是在什么地方重新给var_B8赋值为1了,更可能是我对Mid$()这个函数理解错误?迫切希望有哪位大神帮助我解答一下这个疑问。
020.png
继续向下,从004047A4到004047CC,作用是得到假码字符串中的第n+1个字符,var_90=假码字符串中的第n+1个
[Asm] 纯文本查看 复制代码
004047A4   .  50            push eax                                      ; /var18 = 001867AC
004047A5   .  8D95 38FFFFFF lea edx,dword ptr ss:[ebp-0xC8]               ; |
004047AB   .  51            push ecx                                      ; |var28 = 0012F580
004047AC   .  52            push edx                                      ; |saveto8 = 001867AC
004047AD   .  FF15 C8104000 call dword ptr ds:[<&MSVBVM60.__vbaVarAdd>]   ; \__vbaVarAdd
004047B3   .  50            push eax                                      ;  上句运行后eax=ecx=0012F548,edx=9
004047B4   .  FF15 C4104000 call dword ptr ds:[<&MSVBVM60.__vbaI4Var>]    ;  var_ret_2 = CLng(var_6C + 1)///var_ret_2=n+1 ;eax=n+1[估计是varadd的作用]
004047BA   .  50            push eax                                      ;  eax=循环次数+1
004047BB   .  8B85 78FFFFFF mov eax,dword ptr ss:[ebp-0x88]               ;  eax=假码
004047C1   .  50            push eax
004047C2   .  FFD7          call edi                                      ;  msvbvm60.rtcMidCharBstr///运行后eax为后一个位置的字符,
004047C4   .  8BD0          mov edx,eax                                   ;  
004047C6   .  8D8D 70FFFFFF lea ecx,dword ptr ss:[ebp-0x90]
004047CC   .  FFD3          call ebx                                      ;  msvbvm60.__vbaStrMove ///var_90 = Mid$(var_88, var_ret_2, 1)="2"

接下来比较var_8C,var_90的值,结果存入edi中,
[Asm] 纯文本查看 复制代码
004047CF   .  FF15 68104000 call dword ptr ds:[<&MSVBVM60.__vbaStrCmp>]   ;  msvbvm60.__vbaStrCmp///cmp(var_8C,var_90):eax=-1,说明两个字符串不等,8C是上一次取的值,90是本次取的值
004047D5   .  8BF8          mov edi,eax                                   ;  将比较结果存到edi,相同返回0,不同返回-1(0xFFFFFFFF)

再经过一堆计算,到00404838处,var_8C,var_90不等就跳过,var_48=0;var_8C,var_90相等则记入ss:[ebp-0x48] (实际位置却是在ss:[ebp-0x40])中一次,var_48=0+n:
[Asm] 纯文本查看 复制代码
00404838   .  66:85FF       test di,di                                    ;  比较edi是否为0,di=0,test di,di的结果是CF=0,ZF=1
0040483B > .  74 37         je short <BJCM30A.loc_404874>                 ;  If (var_8C = var_90) + 1 = 0 Then///跳转成立
0040483D   .  8D4D B8       lea ecx,dword ptr ss:[ebp-0x48]               ;  var_8C,var_90相等则记录1次???????????

实际上运行0040486E的命令后,var_40的值才发生了变化:
[Asm] 纯文本查看 复制代码
0040486E > .  FF15 08104000 call dword ptr ds:[<&MSVBVM60.__vbaVarMove>]  ;  var_48 = var_48 + 1/////???从寄存器中看var_48的值无变化,var_40=1

继续到00404894,跳回00404698进行循环:
[Asm] 纯文本查看 复制代码
00404894 > .^\E9 FFFDFFFF   jmp <BJCM30A.loc_404698>                      ;  GoTo`loc_00404698

循环10次后,此时var_48=0,也就是说注册码每个字符都不相同;此时继续向下,可以看出这个循环的作用是判断注册码是否每一位都相同,相同则var_48 = var_48 + 1:
从004048E4到00404941处,将假码的长度减1存入var_F0中,再与上面的var_48比较,比较的结果存入var_134中,如果相等,到00404941处跳到注册失败,不相等则开始计算验证注册码的真伪:
[Asm] 纯文本查看 复制代码
004048E4 > > \8B95 7CFFFFFF mov edx,dword ptr ss:[ebp-0x84]               ;  取假码
004048EA   .  52            push edx                                      ; /String = "1234567890"//// String = "11111"
004048EB   .  FF15 14104000 call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>]  ; \__vbaLenBstr
004048F1 > .  83E8 01       sub eax,0x1                                   ;  Len(var_84) = Len(var_84) - 00000001h////eax=eax-1
004048F4   .  8D8D 08FFFFFF lea ecx,dword ptr ss:[ebp-0xF8]
004048FA > .  0F80 AA070000 jo <BJCM30A.loc_4050AA>                       ;  If Err.Number <> 0 Then
00404900 > .  8985 10FFFFFF mov dword ptr ss:[ebp-0xF0],eax               ;  var_F0 = Len(var_84)
00404906   .  8D45 B8       lea eax,dword ptr ss:[ebp-0x48]
00404909   .  50            push eax                                      ; /eax=假码长度-1?///eax=var_48=记录的循环次数吧?
0040490A   .  51            push ecx                                      ; |ecx=记录的循环次数////ecx=假码长度-1????????????
0040490B   .  C785 08FFFFFF>mov dword ptr ss:[ebp-0xF8],0x8003            ; |下步的比较很重要??????
00404915   .  FF15 6C104000 call dword ptr ds:[<&MSVBVM60.__vbaVarTstEq>] ; \__vbaVarTstEq
0040491B   .  8D8D 7CFFFFFF lea ecx,dword ptr ss:[ebp-0x84]               ;  上步运行后eax=0,ecx=3,edx=10
00404921 > .  66:8985 CCFEF>mov word ptr ss:[ebp-0x134],ax                ;  var_134 = (var_48 = Len(var_84))
00404928   .  FF15 F0104000 call dword ptr ds:[<&MSVBVM60.__vbaFreeStr>]  ;  msvbvm60.__vbaFreeStr
0040492E   .  8D8D 5CFFFFFF lea ecx,dword ptr ss:[ebp-0xA4]
00404934   .  FF15 F4104000 call dword ptr ds:[<&MSVBVM60.__vbaFreeObj>]  ;  msvbvm60.__vbaFreeObj
0040493A   .  66:39BD CCFEF>cmp word ptr ss:[ebp-0x134],di
00404941 > .  0F85 97050000 jnz <BJCM30A.loc_404EDE>                      ;  If var_134 = True Then 注册失败

因为此时假码为“1234567890”, 所以var_48=0,Len(var_84)-1=9,var_134=false,所以不跳向注册失败
【我后面又用“11111”试了一次,到00404941处var_48 = Len(var_84)-1=4,则var_134 = True,直接失败;用“11112”试,到00404941处var_48 = 3,Len(var_84)-1=4,则var_134 =False,不跳向失败】
继续向下,从004049C5处开始到00404D46处又是一个“for...next”大循环,还得一步一步向下走,弄明白其作用:
[Asm] 纯文本查看 复制代码
004049FE   .  FF15 38104000 call dword ptr ds:[<&MSVBVM60.__vbaVarForInit>]     ; \For var_6C = 1 To Len(var_84) Step 1
.........
00404D3A   .  FF15 E8104000 call dword ptr ds:[<&MSVBVM60.__vbaVarForNext>]     ; \Next var_6C

到00404A79处,取假码,到00404A80处,得到假码的长度为“0xA”:
[Asm] 纯文本查看 复制代码
00404A80   .  FF15 14104000 call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>]   ; \__vbaLenBstr

再向下到00404A9D处,将假码的长度的16进制值“0xA”转换成字符unicode "A":
[Asm] 纯文本查看 复制代码
00404A9D   .  FF15 A8104000 call dword ptr ds:[<&MSVBVM60.#572>]   ;  msvbvm60.rtcHexBstrFromVar

到00404ABE处,取假码;
[Asm] 纯文本查看 复制代码
00404ABE > .  FF15 40104000 call dword ptr ds:[<&MSVBVM60.__vbaObjSet>]  ;  Set var_A8 = CrackmeV30a.Text1

[Asm] 纯文本查看 复制代码
00404AE0 > .  8985 40FFFFFF mov dword ptr ss:[ebp-0xC0],eax              ;  var_C0 = var_A8///假码存入var_C0
00404AE6   .  C785 38FFFFFF>mov dword ptr ss:[ebp-0xC8],0x9
00404AF0 > .  FF15 D4104000 call dword ptr ds:[<&MSVBVM60.#617>]         ;  var_D8 = Left(var_A8, 1)////EBP-D8=8???实际上取字符串左边第一位存入EBP-D0中
00404B04 > .  FF15 90104000 call dword ptr ds:[<&MSVBVM60.__vbaStrVarVal>; \var_88 = CStr(var_D8) 假码第一位字符的16进制Asc值31
00404B0B   .  FF15 28104000 call dword ptr ds:[<&MSVBVM60.#516>]         ; \rtcAnsiValueBstr
00404B11   .  8D95 18FFFFFF lea edx,dword ptr ss:[ebp-0xE8]                 ; 上一步得到假码第一位字符的16进制Asc值31 
00404B17 > .  66:8985 20FFF>mov word ptr ss:[ebp-0xE0],ax                ;  var_E0 = Asc(var_88)存入var_E0
00404B2F   .  8BD0          mov edx,eax                                  ;  上步将数值31转换成UNICode文本31,此时eax=UNICode “31”
..................... 
00404B5C > .  FFD3          call ebx                                     ;  var_90 = Hex$(Asc(var_88))///转成16进制数值,var_90 = UNICode “31”
.....................
00404B70 > .  FFD3          call ebx                                     ;  var_8C = Hex$(Len(var_84))///转成16进制数值,var_8C = UNICode “A”
00404B82   .  8D8D 70FFFFFF lea ecx,dword ptr ss:[ebp-0x90]
00404B88   .  8D95 74FFFFFF lea edx,dword ptr ss:[ebp-0x8C]
00404B8E   .  51            push ecx                                     ;  假码第一个字符的ASCII码值
00404B8F   .  52            push edx                                     ;  假码长度

继续向下至00404B91处,代码call dword ptr ds:[eax+0x6F8]不知道是什么意思,注释var_eax = TextBox.1784更不知道是什么意思!
[Asm] 纯文本查看 复制代码
00404B91 > .  FF90 F8060000 call dword ptr ds:[eax+0x6F8]            ;  var_eax = TextBox.1784

运行这一命令后,这时从图中看到EBP-98地址的注释变成了“1EA”,用计算器的十六进制进行计算,正好是31*A=1EA!那么这一句命令的作用就是一个计算表达式: Serial[0] * Serial的长度。
023.png
[Asm] 纯文本查看 复制代码
00404B91 > .  FF90 F8060000 call dword ptr ds:[eax+0x6F8]                ;  var_eax = TextBox.1784:::计算表达式:(Serial[0] * Serial的长度)
00404B97   .  3BC7          cmp eax,edi                                  ;  比较谁的结果?

继续运行到00404BBC处,注释是var_38 = var_98,即将运算结果复制到ebp - 0x38处,也就是0012F5D8处,这时从堆栈窗口中可以看到ebp - 0x38的数值是“1EA”;
024.png       
继续向下单步运行到00404CF1处,
[Asm] 纯文本查看 复制代码
00404CF1 > .  FF15 08104000 call dword ptr ds:[<&MSVBVM60.__vbaVarMo>;  var_34 = var_34 + Asc(var_88)

注释是将结果保存到ebp - 0x34处,可在堆栈窗口中查看时,EBP-34的数值从00000001变成了00000002!非常郁闷的结果!而在下面的EBP-2C处的数值却从0变成了31,这正是字符“1”的16进制AscII码,这是怎么回事?
025.png
继续运行下去看看:第二次运行到00404BBC处,EBP-38的数值由0018601C变成了00186034,注释仍是UNICODE ‘1EA’;继续到00404CF1处,EBP-2C处的数值却从31变成了00D20063,63应该是31+32的结果;第三次运行到到00404CF1处,EBP-2C处的数值由63变成了96(63+33);第四次时为CA(96+34),一直到第十次,EBP-2C处的数值成了20D。
耐心的循环了10次,取了10次假码及假码的长度,再用它们相乘得到一个结果,存入堆栈[EBP-38]中(我这里是0x31*0xA=0x1EA);循环中分别取假码字符串中每一位字符的AscII值进行累加,存入堆栈[EBP-34]中(而实际存储位置却是在[EBP-2C]?)新的疑问等待高人解惑。
循环结束,来到00404D4B处,将堆栈[EBP-34]地址传给ecx,下一句取出结果(将[EBP-2C]的值传给ecx):
[Asm] 纯文本查看 复制代码
00404D4B > > \8D4D CC       lea ecx,dword ptr ss:[ebp-0x34]          ;  loc_404D4B
00404D4E   .  51            push ecx                                 ;  取出结果(Serial的16进制ASCII码总和)

[Asm] 纯文本查看 复制代码
00404D94   .  8D85 7CFFFFFF lea eax,dword ptr ss:[ebp-0x84]
00404D9A   .  8D4D C8       lea ecx,dword ptr ss:[ebp-0x38]
00404D9D   .  50            push eax                                 ;  Serial中的16进制ASCII码和
00404D9E   .  51            push ecx                                 ;  Serial[0] * Serial的长度
00404D9F   .  56            push esi
00404DA0 > .  FF92 F8060000 call dword ptr ds:[edx+0x6F8]            ;  var_eax = TextBox.1784///验证 Serial[0] * Serial的长度 = Serial中的16进制ASCII码和 是否成立

从这里大概猜测,注册算法应该是Serial[0] * Serial的长度 = Serial中的16进制ASCII码和了。
先从最小位数开始试一试:假设注册码是“1****”,那么Serial[0] * Serial的长度=0x31*0x5=0xF5,转换成10进制是245;而0至9的10进制ASCII码分别为48至57,而(245-49)/4=49,所以后四位字符可以先用chr(49)=“1”,又因为不能每一位字符都相等,所以随意将第二位减1得到“0”;第三位“1”不变,第四位再减1得到“0”,最后一位就要加上减去的两个1得到“3”。所以注册码是“10103”。打开程序试一试,成功了!继续试:12101,也成功了!
编写注册机,先是纯数字的运算,再扩展到AscII打印字符的运算:
先尝试了输入一串假码,然后进行检验,如果不正确,则从最后一位更改,尽量保持输入的字符串最少的改动的思路;然后又想了输入注册码长度,随机生成字符串再检测生成注册码;还有过不要任何输入内容,点击命令按钮根据算法直接生成注册码的想法等等。
编写中不断出错,遇到了各种各样的问题,如中文字符、控制字符等,甚至是找不到问题所在,整个破解陷入停滞之中。。。。。。
好在,终于完成了注册机!!!
在练习过程中,我感到自己还有许多不到位及分析错误的地方,迫切希望能有大神指正!
附件 017.rar (42.13 KB, 下载次数: 3) ,含CM原程序、爆破后的程序及017注册机。百度链接是:http://pan.baidu.com/s/1skMkJY9密码: 86pm,160个CM、我已练习过的前17个crackme程序(不含012)都在里面。

免费评分

参与人数 8威望 +1 吾爱币 +12 热心值 +8 收起 理由
Hmily + 1 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
leevi + 1 用心讨论,共获提升!
liphily + 1 通知站长,就说我说了给你加一个优秀徽章
lph619 + 1 + 1 用心讨论,共获提升!
smile1110 + 1 谢谢@Thanks!
fengshidou + 1 + 1 谢谢@Thanks!
pk8900 + 2 + 1 写的很详细,不错,加油哟~~~~
qidechang + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 海天一色001 发表于 2019-4-8 14:15
灰太狼大王 发表于 2019-4-8 06:39
你一直发布了18,第12个怎么没有

012是NE格式的16位程序,不是常见的PE程序,在OD中没办法调试,IDA猜测可以调试(不过我不懂),网上有大神说要构建系统环境Win95系统+TRW2000,我试了试,还是不会,所以一直没有做这个练习。
wi5788 发表于 2019-4-7 18:18
N.A.T 发表于 2019-4-8 02:04
灰太狼大王 发表于 2019-4-8 06:39
你一直发布了18,第12个怎么没有
轩可雅 发表于 2019-4-8 09:40
多谢楼主分享
大海萌萌 发表于 2019-4-8 10:02
膜拜大神!
lovejoan 发表于 2019-4-8 11:02
很详细,也很厉害了
xinzaixin 发表于 2019-4-8 13:14
膜拜大神,厉害厉害。。。。。。。。。。。
13790439860 发表于 2019-4-8 18:17
楼主是个大神,辛苦了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 16:19

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表