wellen 发表于 2009-4-23 22:22

新手教程:两个简单CrackMe的算法分析 适合初学算法的新手

本帖最后由 wellen 于 2009-4-26 21:45 编辑

新手教程:两个简单CrackMe的算法分析 适合初学算法的新手         

本来不想发这东西的感觉好像别人都不屑一看的 难度太低了 但感觉毕竟很多人还是和我一样的菜鸟就做个新

手教程来让广大和我一样菜的人学习下两个CrackMe都论坛上翻到的地址在哪忘了 看了看难度很低 非常适合

初入算法分析又感觉迷茫的人学习!

1.第一个CarckMe      ArturDents CrackMe #2   
首先我们了解下CrackMe的概况 我们运行下 输入Name和Serial 点击Check It没任何提示 Peid看了下无壳

ASM写的 了解大概后我们OD载入
第一步我们首先要确定关键位置 这一步至关重要! 我们可以下断点( BP GetDlgItemTextA等 )或者查找字

符串找到注册成功的部位断下然后F9运行它 输入假码 Name:332211 Serial:wecwec
点击Checkit 可以看到断下了

0013FBA8   00401113/CALL 到 GetDlgItemTextA 来自 AD_CM#2.0040110E
0013FBAC   001E0348|hWnd = 001E0348 ('ArturDents CrackMe #2',class='#32770')
0013FBB0   00000BB8|ControlID = BB8 (3000.)
0013FBB4   00403080|Buffer = AD_CM#2.00403080
0013FBB8   00000014\Count = 14 (20.)

既然说要分析算法 我们就找找算法的位置 来到0040110E处 很明显这里是算法CALL内 取消断点

GetDlgItemTextA 然后断下4010FC 看下它的操作流程 我们F9运行下再点击Check It 断下后F8慢慢看

004010FC    55            push ebp
004010FD    8BEC            mov ebp,esp
004010FF    6A 14         push 14
00401101    68 80304000   push AD_CM#2.00403080               ; ASCII "332211"
00401106    68 B80B0000   push 0BB8                           ; 上面压入用户名
0040110B    FF75 08         push dword ptr ss:
0040110E    E8 77000000   call <jmp.&USER32.GetDlgItemTextA>    ; eax Name长度 下面算法用到
00401113    8BF0            mov esi,eax                           ; esi赋eax 长度
00401115    8D01            lea eax,dword ptr ds:
00401117    83FE 05         cmp esi,5                           ; name长度和5比较
0040111A    7D 18         jge short AD_CM#2.00401134            ; 大于等于5则跳
0040111C    6A 40         push 40
0040111E    68 12304000   push AD_CM#2.00403012               ; ASCII "ArturDents CrackMe#2"
00401123    68 44304000   push AD_CM#2.00403044               ; ASCII "Your name must be at

least five characters long!"
00401128    FF75 08         push dword ptr ss:
0040112B    E8 60000000   call <jmp.&USER32.MessageBoxA>
00401130    33C0            xor eax,eax
00401132    EB 40         jmp short AD_CM#2.00401174
00401134    6A 14         push 14
00401136    68 80324000   push AD_CM#2.00403280               ; ASCII "wecwec"
0040113B    68 B90B0000   push 0BB9                           ; 上面压入我们的假码
00401140    FF75 08         push dword ptr ss:
00401143    E8 42000000   call <jmp.&USER32.GetDlgItemTextA>
00401148    B8 80304000   mov eax,AD_CM#2.00403080            ; ASCII "332211"
0040114D    BB 80324000   mov ebx,AD_CM#2.00403280            ; ASCII "wecwec"
00401152    8BCE            mov ecx,esi                           ; 上面Name的长度
00401154    8A10            mov dl,byte ptr ds:            ; 逐一取Name每一位
00401156    2AD1            sub dl,cl                           ; 每一位减去 cl
00401158    3813            cmp byte ptr ds:,dl            ; 是否相等
0040115A    75 18         jnz short AD_CM#2.00401174            ; 不相等就结束 这里是爆破点
0040115C    40            inc eax
0040115D    43            inc ebx
0040115E^ E2 F4         loopd short AD_CM#2.00401154          ; 循环每一位运算后ECX-1 直到

ECX=1时结束
00401160    6A 40         push 40
00401162    68 12304000   push AD_CM#2.00403012               ; ASCII "ArturDents CrackMe#2"
00401167    68 27304000   push AD_CM#2.00403027               ; ASCII "Yeah, you did it!"
0040116C    FF75 08         push dword ptr ss:
0040116F    E8 1C000000   call <jmp.&USER32.MessageBoxA>
00401174    C9            leave
00401175    C2 0400         retn 4

稍微看下马上就能看到爆破点 0040115A 接下来看看算法 0040110E 取得用户名长度 最后保存到esi 判断用户

名长度大于5就继续运行 00401154 开始就是真正的计算部分了 它怎么在算已经摆的很明了了 循环取Name每一

位的16进制ACSII码减去ECX每循环一次ECX-1 0040115A处判断是否和假码每一位相等直到ECX=1时跳出循环
弹出注册成功信息 332211 计算下就能得出注册码(33-6)(33-5)(32-4)(32-3)(31-2)(31-1) = -..//0

既然知道了算法就可以开动写注册机了 这就作为作业留给大家了



2.Make in H&Y   记得好像是Z前辈发的   此程序相对上面个提高了点难度不过还是十分简单的 同样我们OD载

入上面我们断GetDlgItemTextA 这次再试试
输入用户名332211 假码 53212214
同样断下了断下后我们先看堆栈
0013FBB4   00401128/CALL 到 GetDlgItemTextA 来自 Keygenme.00401123
0013FBB8   00080600|hWnd = 00080600 ('Make in H&Y.',class='#32770')
0013FBBC   0000000C|ControlID = C (12.)
0013FBC0   0040352C|Buffer = Keygenme.0040352C
0013FBC4   00000100\Count = 100 (256.)

来到00401123处 F8单步向下看

00401123|.E8 FA010000   call    <jmp.&user32.GetDlgItemTextA>    ; \GetDlgItemTextA
00401128|.33C0          xor   eax, eax
0040112A|.52            push    edx                              ; /IsSigned
0040112B|.68 2C344000   push    0040342C                         ; |pSuccess =

Keygenme.0040342C
00401130|.6A 0B         push    0B                               ; |ControlID = B (11.)
00401132|.FF75 08       push    dword ptr                 ; |hWnd
00401135|.E8 E2010000   call    <jmp.&user32.GetDlgItemInt>      ; \GetDlgItemInt
0040113A|.803D 2C344000>cmp   byte ptr , 0
00401141|.75 30         jnz   short 00401173

稍微分析下就能发现它检测了是否输入的为整数 如果用户名输入了非数字字符就字节跳向失败


继续向下看 很明显的关键跳 0040117A|. /75 33         jnz   short 004011AF   这里可以爆破

那看样子 401173处就是关键算法call了 我们跟进去看看 算得多了可以看出它注册码的生成形式为A+B+C型不

过别急慢慢来

00401201/$8BD8          mov   ebx, eax                         ;ebx 赋 ID
00401203|.8BC3          mov   eax, ebx                         ;EAX = EBX
00401205|.B9 E4120000   mov   ecx, 12E4
0040120A|.33D2          xor   edx, edx
0040120C|.F7F1          div   ecx                              ;eax余ecx
0040120E|.81F2 F1250B00 xor   edx, 0B25F1                      ;结果代入进行异或运算
00401214|.8BDA          mov   ebx, edx                         ;ebx 保存运算后结果
00401216|.8BC3          mov   eax, ebx                         ;eax = ebx
00401218|.33D2          xor   edx, edx                         ;edx 清零
0040121A|.52            push    edx
0040121B|.50            push    eax
0040121C|.8BD0          mov   edx, eax                         ;edx 赋 731346
0040121E|.52            push    edx                              ; /<%d>
0040121F|.68 00304000   push    00403000                         ; |%d
00401224|.68 10304000   push    00403010                         ; |731346
00401229|.E8 D0000000   call    <jmp.&user32.wsprintfA>          ; \wsprintfA
0040122E|.83C4 0C       add   esp, 0C
00401231|.5A            pop   edx
00401232|.5A            pop   edx


这里可以算第一步了我们慢慢来先理清下算法的思路 先是ID的16进制和12E4求余 计算后的结果再和0B25F1

进行异或运算 结果就是 B28D2 也就是 10进制的 731346

好了我们继续看下去

00401233|.8D05 10304000 lea   eax, dword ptr           ;取第一位
00401239|.0FB605 103040>movzx   eax, byte ptr          ;取第一位ASCII eax 37
00401240|.8D15 10304000 lea   edx, dword ptr           ;取第一位
00401246|.0FB615 113040>movzx   edx, byte ptr          ;取第二位ASCII edx 33
0040124D|.03C2          add   eax, edx                         ;eax+edx
0040124F|.B9 05000000   mov   ecx, 5                           ;ecx赋5
00401254|.33D2          xor   edx, edx                         ;edx清0
00401256|.F7F1          div   ecx                              ;eax余ecx
00401258|.80C2 04       add   dl, 4                            ;edx+4
0040125B|.52            push    edx
0040125C|.50            push    eax
0040125D|.8BC2          mov   eax, edx                         ;eax = 5
0040125F|.50            push    eax                              ; /<%d>
00401260|.68 03304000   push    00403003                         ; |%d
00401265|.68 10314000   push    00403110                         ; |5
0040126A|.E8 8F000000   call    <jmp.&user32.wsprintfA>          ; \wsprintfA
0040126F|.83C4 0C       add   esp, 0C
00401272|.5A            pop   edx
00401273|.5A            pop   edx

这里我们就算第二步 分析下看看首先把第一步的运算结果搬过来 731346 取第一位的acsii码 37 然后再取

第二位的 acsii码 33接着 两数相加 再余5 结果再加4 最后出来的是5初次学的是不是感觉有点头晕呢 没

关系坚持下去因为胜利就在眼前了 !

00401274|.8D05 10304000 lea   eax, dword ptr
0040127A|.0FB605 123040>movzx   eax, byte ptr          ;EAX 31
00401281|.8D15 10304000 lea   edx, dword ptr
00401287|.0FB615 133040>movzx   edx, byte ptr          ;EDX 33
0040128E|.03C2          add   eax, edx                         ;EAX 31+33
00401290|.B9 05000000   mov   ecx, 5                           ;ECX 5
00401295|.33D2          xor   edx, edx                         ;edx清零
00401297|.F7F1          div   ecx
00401299|.80C2 03       add   dl, 3                            ;(initial cpu selection)
0040129C|.52            push    edx
0040129D|.50            push    eax
0040129E|.8BC2          mov   eax, edx
004012A0|.50            push    eax                              ; /<%d>
004012A1|.68 06304000   push    00403006                         ; |%d
004012A6|.68 10324000   push    00403210                         ; |3
004012AB|.E8 4E000000   call    <jmp.&user32.wsprintfA>          ; \wsprintfA

这是第三步 取 731346 第三位acsii码31   第四位acsii码 33 又是两位相加和5求余 结果再加3 最后出来

的是 3


最后看看最后的运算
004012B0|.83C4 0C       add   esp, 0C
004012B3|.68 10324000   push    00403210                     
004012B8|.68 10314000   push    00403110                     
004012BD|.68 10304000   push    00403010                     
004012C2|.68 09304000   push    00403009                     
004012C7|.68 10334000   push    00403310                     
004012CC|.E8 2D000000   call    <jmp.&user32.wsprintfA>      
004012D1|.83C4 14       add   esp, 14
004012D4|.68 10334000   push    00403310                     
004012D9|.68 2C354000   push    0040352C                     
004012DE|.E8 15000000   call    <jmp.&kernel32.lstrcmpA>   
004012E3|.5A            pop   edx
004012E4|.5A            pop   edx


可以看到把第一步 第二步 第三步的结果都加了起来 731346 + 5 + 3也就是 73134653

用户名 332211 注册码 73134653 我们输入进去瞧瞧 注册成功了吧

最后我用最最通俗易懂的语言来概括下它的算法 别说这样你都看不懂

B25F1 的10进制 730609
12E4的10进制   4836

&1 = (用户名 div 4836) xor 730609
&2 =((&1第一位ASCII + &1第二位ASCII ) div 5) +4

&3 =((&1第三位ASCII + &1第四位ASCII ) div 5) +3

Serial = &1 + &2 + &3

囧 最笨的表达方式 如果还看不懂那我就真的没办法了

第一次发这帖不知道写的格式对不对 = = 最后留给大家个作业 完成第一个和第二个CrackMe的KeyGen~~~

wellen 发表于 2009-4-23 22:25

难得发帖沙发当然自己做了 :lol

zapline 发表于 2009-4-23 22:56

本帖最后由 zapline 于 2009-4-23 22:59 编辑

板凳。。。。
Make in H&Y 都是xiaojiam(看雪ID)莱沙(吾爱ID)大侠的作品,专业研究CM的大牛

wellen 发表于 2009-4-23 23:06

板凳。。。。
Make in H&Y 都是xiaojiam(看雪ID)莱沙(吾爱ID)大侠的作品,专业研究CM的大牛
zapline 发表于 2009-4-23 22:56 http://www.52pojie.cn/images/common/back.gif 哇 拜拜

mayl8822 发表于 2009-4-23 23:14

4# wellen


谢谢分享哦

冰封秋雪 发表于 2009-4-23 23:14

上面是什么我都看不懂啊!!唉!!!

wgz001 发表于 2009-4-23 23:18

学习算法   一直都入不了门   :'(

CHHSun 发表于 2009-4-24 10:32

算法我还没考虑,直接不去想。

ming_feiyang 发表于 2009-4-24 11:46

我也看看!

热火朝天 发表于 2009-4-24 11:55

支持一下楼主的好帖,学习学习
页: [1]
查看完整版本: 新手教程:两个简单CrackMe的算法分析 适合初学算法的新手