好友
阅读权限25
听众
最后登录1970-1-1
|
本帖最后由 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:[ebp+8]
0040110E E8 77000000 call <jmp.&USER32.GetDlgItemTextA> ; eax Name长度 下面算法用到
00401113 8BF0 mov esi,eax ; esi赋eax 长度
00401115 8D01 lea eax,dword ptr ds:[ecx]
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:[ebp+8]
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:[ebp+8]
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:[eax] ; 逐一取Name每一位
00401156 2AD1 sub dl,cl ; 每一位减去 cl
00401158 3813 cmp byte ptr ds:[ebx],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:[ebp+8]
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 [ebp+8] ; |hWnd
00401135 |. E8 E2010000 call <jmp.&user32.GetDlgItemInt> ; \GetDlgItemInt
0040113A |. 803D 2C344000>cmp byte ptr [40342C], 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 [403010] ; 取第一位
00401239 |. 0FB605 103040>movzx eax, byte ptr [403010] ; 取第一位ASCII eax 37
00401240 |. 8D15 10304000 lea edx, dword ptr [403010] ; 取第一位
00401246 |. 0FB615 113040>movzx edx, byte ptr [403011] ; 取第二位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 [403010]
0040127A |. 0FB605 123040>movzx eax, byte ptr [403012] ; EAX 31
00401281 |. 8D15 10304000 lea edx, dword ptr [403010]
00401287 |. 0FB615 133040>movzx edx, byte ptr [403013] ; 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~~~ |
免费评分
-
查看全部评分
|