新手教程:两个简单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~~~ 难得发帖沙发当然自己做了 :lol 本帖最后由 zapline 于 2009-4-23 22:59 编辑
板凳。。。。
Make in H&Y 都是xiaojiam(看雪ID)莱沙(吾爱ID)大侠的作品,专业研究CM的大牛 板凳。。。。
Make in H&Y 都是xiaojiam(看雪ID)莱沙(吾爱ID)大侠的作品,专业研究CM的大牛
zapline 发表于 2009-4-23 22:56 http://www.52pojie.cn/images/common/back.gif 哇 拜拜 4# wellen
谢谢分享哦 上面是什么我都看不懂啊!!唉!!! 学习算法 一直都入不了门 :'( 算法我还没考虑,直接不去想。 我也看看! 支持一下楼主的好帖,学习学习
页:
[1]