一个crackme的算法分析(适合初学者)
【破文标题】一个crackme的算法分析【破文作者】windycandy
【破解工具】OD,Peid0.94
【破解平台】Windows xp
【软件名称】crackme
【软件大小】12K
【保护方式】name & SN
【破解声明】我是一只小菜鸟,偶得一点心得,愿与大家分享
------------------------------------------------------------------------
1.前言
最近学习CCDebuger前辈的<OllyDBG 入门系列>,使我等菜鸟受益匪浅,今有所成,又巧遇一crackme,可用前辈的
<消息断点及 RUN 跟踪>方法破之,遂将过程写出来,与跟我一样的菜鸟共勉,同时激励前辈在有时间情况下写出
更多的教程,以供我们观摩学习.
2.破解过程
先运行程序看看情况,点HELP随便输入name:windycandy,serial:87654321.按OK,出现错误提示框.好了,现在拿
工具开始行动.
Peid 查壳无壳,为MASM32 / TASM32 编写的程序.
OD载入,停在入口:
00401000 C>/$6A 00 push 0 ; /(初始化 cpu 选择状态)
00401002 |.E8 FF040000 call <jmp.&KERNEL32.GetModuleHandle>; \GetModuleHandleA
00401007 |.A3 CA204000 mov dword ptr ds:,eax
0040100C |.6A 00 push 0 ; /Title = NULL
0040100E |.68 F4204000 push CRACKME.004020F4 ; |no need to disasm the code!
00401013 |.E8 A6040000 call <jmp.&USER32.FindWindowA> ; \FindWindowA
00401018 |.0BC0 or eax,eax
0040101A |.74 01 je short CRACKME.0040101D
用字符串插件能找到有效字符"great work, mate!\nnow try the next crackme!"及"no luck there, mate!"分别
双击都能来到反汇编窗口,但是向上找都没有找到能跳过错误提示框的关键跳,这下好象有点意思了.这样找关键算法
的call好象也比较困难,改用<消息断点及 RUN 跟踪>方法则轻松破之.
OD重新载入,F9运行,到注册界面输入"windycandy"及"8765432100",不按"OK"
在OD菜单找"查看"下的"窗口"或直接按"M"(有时按"窗口"或"M"看不到内容,这时可以轮换按"M"或"窗口")
出现以下窗口
句柄 标题 父级 WinProc ID ? 样式 ExtStyle 线程 ClsProc Class
00020684 Topmost 0CC00000 00000100 主 10005F60 ATL:10055250
00040682 Register Topmost 14C800C4 00010101 主 77D1C6C4 #32770
K0002064E Serial 00040682 0000FFFF 50020000 00000004 主 77D1CC6C Static
K00020652 Name 00040682 0000FFFF 50020000 00000004 主 77D1CC6C Static
K00020654 Cancel 00040682 000003EB 50010000 00000004 主 77D30515 Button
K00020664 OK 00040682 000003EA 50010000 00000004 主 77D30515 Button
K00020666 00040682 000003E9 50010080 00000204 主 77D28530 Edit
K00030668 00040682 000003E8 50010080 00000204 主 77D28530 Edit
E00090650 Default IME 00040682 8C000000 主 77D63CE2 IME
NE0008066C M 00090650 8C000000 主 FFFF037D MSCTFIME UI
00060644 Hammer of Thor Topmost 8C000000 主 77D15C55 Mjolnir
E00040642 THOR MAIN WINDOW 00060644 8C400000 00000188 主 10037BCB CiceroUIWndFrame
NK0002063E CiceroUIWndFrame 00040642 8C000000 00080008 主 10037BCB CiceroUIWndFrame
NIK0002062E PadListView 0002063E 56000000 00000200 主 10028210 PadListView
NIIE000206A2 0002062E 00000020 50000002 主 FFFF02C1 SysHeader32
NIE0002063C CiceroUIWndFrame 0002063E 8C800000 00000008 主 10037BCB CiceroUIWndFrame
NK00020640 CiceroUIWndFrame 00040642 8C800000 00000008 主 10037BCB CiceroUIWndFrame
NE00020698 PadListView 00040642 56000000 00000200 主 10028210 PadListView
NNE00020658 00020698 00000020 40000002 主 FFFF02C1 SysHeader32
00090648 CrackMe v1.0 Topmost 00070781 1CCF0000 00000100 主 00401128 No need to disasm the code!
找到按钮"OK"
单击右键------->"在Classproc上设消息断点"---------> 选"202 WM_LBUTTONUP"--------> 点"确定"
再在"调试"菜单下"打开或清除RUN跟踪",开始RUN跟踪
确保在反汇编窗口下
单击右键---------> RUN跟踪------------->添加全部函数例程的入口
返回到crackme窗口,按"OK"
条件中断在
77D30515 [>55 push ebp--------中断在这里
77D30516 8BEC mov ebp,esp
77D30518 8B4D 08 mov ecx,dword ptr ss:
77D3051B 56 push esi
77D3051C E8 0335FEFF call USER32.77D13A24
77D30521 8BF0 mov esi,eax
77D30523 85F6 test esi,esi
77D30525 74 38 je short USER32.77D3055F
77D30527 8B55 0C mov edx,dword ptr ss:
77D3052A 3B15 C8C0D677 cmp edx,dword ptr ds:
77D30530 77 1E ja short USER32.77D30550
77D30532 33C0 xor eax,eax
按Alt+M
内存映射,项目 23
地址=00401000
大小=00001000 (4096.)
宿主=CRACKME00400000
区段=CODE
包含=code
类型=Imag 01001002
访问=R
初始访问=RWE
在crackme, code 段下F2断点,F9运行,中断在00401253
00401253 /.C8 000000 enter 0,0-------断在这里
00401257 |.53 push ebx
00401258 |.56 push esi
00401259 |.57 push edi
0040125A |.817D 0C 10010>cmp dword ptr ss:,110
00401261 |.74 34 je short CRACKME.00401297
00401263 |.817D 0C 11010>cmp dword ptr ss:,111
0040126A |.74 35 je short CRACKME.004012A1
0040126C |.837D 0C 10 cmp dword ptr ss:,10
00401270 |.0F84 81000000 je CRACKME.004012F7
00401276 |.817D 0C 01020>cmp dword ptr ss:,201
0040127D |.74 0C je short CRACKME.0040128B
0040127F |.B8 00000000 mov eax,0
00401284 |>5F pop edi
00401285 |.5E pop esi
向下翻,看到
004012B5 |.6A 0B |push 0B ; /Count = B (11.)
004012B7 |.68 8E214000 |push CRACKME.0040218E ; |Buffer = CRACKME.0040218E
004012BC |.68 E8030000 |push 3E8 ; |ControlID = 3E8 (1000.)
004012C1 |.FF75 08 |push dword ptr ss: ; |hWnd
004012C4 |.E8 07020000 |call <jmp.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA--------------这个,我们喜欢
004012C9 |.83F8 01 |cmp eax,1
004012CC |.C745 10 EB030>|mov dword ptr ss:,3EB
004012D3 |.^ 72 CC \jb short CRACKME.004012A1
004012D5 |.6A 0B push 0B ; /Count = B (11.)
004012D7 |.68 7E214000 push CRACKME.0040217E ; |Buffer = CRACKME.0040217E
004012DC |.68 E9030000 push 3E9 ; |ControlID = 3E9 (1001.)
004012E1 |.FF75 08 push dword ptr ss: ; |hWnd
004012E4 |.E8 E7010000 call <jmp.&USER32.GetDlgItemTextA>; \GetDlgItemTextA--------------这个,我们喜欢
004012E9 |.B8 01000000 mov eax,1
004012EE |.EB 07 jmp short CRACKME.004012F7
于是在4012B5,F2下断,删除其他断点, Ctrl+F2重新载入,F9运行,输入name和serail,点确定中断在4012B5
F8一路来到
00401223 .83F8 00 cmp eax,0
00401226 .^ 74 BE je short CRACKME.004011E6
00401228 .68 8E214000 push CRACKME.0040218E ;ASCII "windycandy"姓名入栈
0040122D .E8 4C010000 call CRACKME.0040137E -----------------对name进行算法运算,F7跟进
00401232 .50 push eax
00401233 .68 7E214000 push CRACKME.0040217E ;ASCII "8765432100"
00401238 .E8 9B010000 call CRACKME.004013D8
0040123D .83C4 04 add esp,4
00401240 .58 pop eax
00401241 .3BC3 cmp eax,ebx
00401243 .74 07 je short CRACKME.0040124C
00401245 .E8 18010000 call CRACKME.00401362
0040124A .^ EB 9A jmp short CRACKME.004011E6
0040124C >E8 FC000000 call CRACKME.0040134D
00401251 .^ EB 93 jmp short CRACKME.004011E6
进入40122D来到
0040137E /$8B7424 04 mov esi,dword ptr ss:------------输入的name放入esi
00401382 |.56 push esi
00401383 |>8A06 /mov al,byte ptr ds:---------------取name的字符进行逐个判断
00401385 |.84C0 |test al,al
00401387 |.74 13 |je short CRACKME.0040139C---------------所有字符判断结束则跳走
00401389 |.3C 41 |cmp al,41 ----------------是否是字母"A"以上的字符
0040138B |.72 1F |jb short CRACKME.004013AC---------------不是,over
0040138D |.3C 5A |cmp al,5A
0040138F |.73 03 |jnb short CRACKME.00401394---------------与字母"Z"比较,不小于则进行处理
00401391 |.46 |inc esi ------------------下一个字符
00401392 |.^ EB EF |jmp short CRACKME.00401383
00401394 |>E8 39000000 |call CRACKME.004013D2------------------------字符转换,关键call,F7跟进
00401399 |.46 |inc esi ;CRACKME.0040218E
0040139A |.^ EB E7 \jmp short CRACKME.00401383
F7进入401394
004013D2 /$2C 20 sub al,20---------------------------------------将name中的小写字母换成大写字母
004013D4 |.8806 mov byte ptr ds:,al
004013D6 \.C3 retn-----------------------------------------------返回
字符转换完后,返回到这里继续以下处理
0040139C |> \5E pop esi ---------------------------换算结果入esi
0040139D |.E8 20000000 call CRACKME.004013C2--------------------------算法call,F7跟进
004013A2 |.81F7 78560000 xor edi,5678
004013A8 |.8BC7 mov eax,edi
F7进入40139D
004013C2 /$33FF xor edi,edi-----------------清零,准备运算
004013C4 |.33DB xor ebx,ebx-----------------清零,准备运算
004013C6 |>8A1E /mov bl,byte ptr ds:---逐一取esi的字符
004013C8 |.84DB |test bl,bl
004013CA |.74 05 |je short CRACKME.004013D1--esi内的字符处理完则跳走
004013CC |.03FB |add edi,ebx------------------累加,结果放入edi
004013CE |.46 |inc esi-------------------下一位字符
004013CF |.^ EB F5 \jmp short CRACKME.004013C6
004013D1 \>C3 retn----------------------------返回
返回到这里
004013A2 |.81F7 78560000 xor edi,5678---------------运算结果与"5678"异域运算
004013A8 |.8BC7 mov eax,edi-----------------运算结果送入eax
004013AA |.EB 15 jmp short CRACKME.004013C1---跳到4013C1,返回
返回这里
00401232 .50 push eax--------------------返回这里
00401233 .68 7E214000 push CRACKME.0040217E ;ASCII "8765432100"假码入栈
00401238 .E8 9B010000 call CRACKME.004013D8------------------算法call,F7跟进
0040123D .83C4 04 add esp,4
00401240 .58 pop eax
00401241 .3BC3 cmp eax,ebx
00401243 .74 07 je short CRACKME.0040124C
00401245 .E8 18010000 call CRACKME.00401362
0040124A .^ EB 9A jmp short CRACKME.004011E6
0040124C >E8 FC000000 call CRACKME.0040134D
00401251 .^ EB 93 jmp short CRACKME.004011E6
F7进入
004013D8 /$33C0 xor eax,eax-----------------清零,准备运算
004013DA |.33FF xor edi,edi-----------------清零,准备运算
004013DC |.33DB xor ebx,ebx-----------------清零,准备运算
004013DE |.8B7424 04 mov esi,dword ptr ss:----假码送入esi
004013E2 |>B0 0A /mov al,0A
004013E4 |.8A1E |mov bl,byte ptr ds:----逐一取假码进行运算
004013E6 |.84DB |test bl,bl
004013E8 |.74 0B |je short CRACKME.004013F5---处理完则跳走
004013EA |.80EB 30 |sub bl,30-------------------减30
004013ED |.0FAFF8 |imul edi,eax-----------------edi=edi*eax
004013F0 |.03FB |add edi,ebx------------------edi=eid+ebx
004013F2 |.46 |inc esi----------------------下一位
004013F3 |.^ EB ED \jmp short CRACKME.004013E2
004013F5 |>81F7 34120000 xor edi,1234--------------------运算结果与1234异域
004013FB |.8BDF mov ebx,edi----------------------运算结果送入edx
004013FD \.C3 retn-----------------------------返回
返回这里
0040123D .83C4 04 add esp,4------返回这里
00401240 .58 pop eax
00401241 .3BC3 cmp eax,ebx-----------------------name的算法运算结果与serial的算法运算结果比较
00401243 .74 07 je short CRACKME.0040124C---------------关键跳,爆破点
00401245 .E8 18010000 call CRACKME.00401362--------------------不等则OVER
0040124A .^ EB 9A jmp short CRACKME.004011E6
0040124C >E8 FC000000 call CRACKME.0040134D--------------------相等"congraduatin!"
00401251 .^ EB 93 jmp short CRACKME.004011E6
破解总结:
这个crackme的注册属于F'(name)=F'(serial)的类型,其算法为:
①输入的name必须全部是字母,在name的字母中如果有小写字母则需要将该小写字母换成大写
字母,本身是大写字母的不用转换,然后将name(都是大写字母)的各字符串的ARSII值累加,累
加结果与"5678"进行异域运算,得出F'(name);
②取输入假码的每位ARSCII值(B),作如下运算(A为常数):
y<=B-30
Z<=Z*A
Z<=Z+Y
算完一位再取下一位.
最后将累加结果(Z)与"1234"作异域运算,得出F'(serial)
如果F'(name)=F'(serial)则注册成功.
注册机,我不会写,只能算出自己的注册码,有时间再学写写注册机.
name=windycandy
serial=B102或serial=18102
推算过程:
windycany
│
│转换为大写
↓
WINDYCANDY
│
│name的算法、累加处理
↓
2FA
│
│xor 5678
↓
5482----F'(name)
│
│xor 1234
↓
46B6
│
│除以常数“A”------------余数“2”,2+30=32-----ARSII表中为字符“2”------注册码倒数第一位
↓
712
│
│除以常数“A”------------余数“0”,0+30=30-----ARSII表中为字符“0”------注册码倒数第二位
↓
B5
│
│除以常数“A”------------余数“1”,1+30=31-----ARSII表中为字符“1”------注册码倒数第三位
↓
12---------(如果只算到这里余数“12”,12+30=42---ARSII表中为字符“B”------注册码倒数第四位)----得出serial=B102
│
│除以常数“A”------------余数“8”,8+30=38-----ARSII表中为字符“8”------注册码倒数第四位
↓
1--------------------------余数“1”,1+30=31-----ARSII表中为字符“1”------注册码倒数第五位----得出serial=18102
最后,如果使用字符串插件找到"no luck there, mate!",双击来到反汇编窗口
00401362 /$6A 00 push 0 ; /BeepType = MB_OK
00401364 |.E8 AD000000 call <jmp.&USER32.MessageBeep> ; \MessageBeep
00401369 |.6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
0040136B |.68 60214000 push CRACKME.00402160 ; |no luck!
00401370 |.68 69214000 push CRACKME.00402169 ; |no luck there, mate!--------------到这里
00401375 |.FF75 08 push dword ptr ss: ; |hOwner
00401378 |.E8 BD000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
0040137D \.C3 retn
到这里后,注意401362及40137D,可以猜测这是某个CALL调用的子程序,于是使用Ctrl+F查找"call 401362"也能找到关键点
00401241 .3BC3 cmp eax,ebx
00401243 .74 07 je short CRACKME.0040124C
00401245 .E8 18010000 call CRACKME.00401362------------找到这里
0040124A .^ EB 9A jmp short CRACKME.004011E6
0040124C >E8 FC000000 call CRACKME.0040134D
00401251 .^ EB 93 jmp short CRACKME.004011E6
以下分析相同.
------------------------------------------------------------------------
【版权声明】本文仅属于技术交流,如有转载请注明出处.
[ 本帖最后由 windycandy 于 2006-3-19 01:30 编辑 ] 建议将CrackMe贴出来,给大家学习!
写注册机时,对算法求逆就可以了,呵呵! 其实只要在这里下断就OK:
00401223 .83F8 00 cmp eax,0
往下的两个CALL 就是算法CALL了!很容易看出来!
对于这个练习,好像有点麻烦了!呵呵!
不过对于学习,我是非常支持的!!
先计算出用户名的算法,再计算注册码,是非常容易的!呵呵!
NAME: lengxue
CODE: 18004 强啊,适合我等新手 好文章哪!! 顶起来!!! 辛苦了,支持一下
页:
[1]