【文章标题】: 小菜分析的第一个CrackMe
【文章作者】: petnt
【作者邮箱】: petnt@sohu.com
【软件名称】: TheBigMan's CrackMe #6
【软件大小】: 8K
【下载地址】: 见附件
【使用工具】: OLLYDBG MASM32
【操作平台】: XP SP2
【作者声明】: 如果你和我一样也是刚刚入门,第一次接触CrackMe,就进来看看交流一下。大侠请略过!
--------------------------------------------------------------------------------
【详细过程】
前两天下了PEDIY CrackME 2007精华集(上网时间不多,所以好多事情都慢人一步),按照kanxue老大的要求,一切从头学起。看完简介,第一篇为4nil大侠2005-02-17完成的大作。(汗!马上08年了,差距太大了,只能用那句话鼓励自己:从下一秒开始,一切都不会晚!)看了他的软件简介,看来是个不错的CrackMe,就从这个开始吧。
同样按照老大的要求,没有继续看他的破解过程。直接下了附件,正式开战!
为了便于总结和以后复习以及小菜们更容易看清整个过程,下面将整个过程分为三部分:下断、分析 和编写注册机。
第一部分 下断
打开CrackMe,随便输入用户名、序列号,点击Check,没有反应。好了,不管这么多,右键请出我的OLLYDBG,程序顺利地停在了入口处。
不管3721,Command: bpx GetWindowTextA。晕,No references。右键,查找,当前模块中的名称。再晕,只看到3个api,GetModuleHandleA,GetProcAddress,LoadLibraryA。看来我们遇到麻烦了,OllyDBG并没有提示我有壳,难道是作者把所有API都自己读入?还是在故意在隐藏什么?不管他,当程序运行以后,总该能让我们看到什么吧,F9。界面出来了,好,Alt+W,在Check上设LEFTBUTTONUP的消息断点。Alt+M,在代码段设内存访问断点。设置好之后,准备输入用户名,鼠标刚到窗口,程序就被断了下来。
004015A1 /. 55 push ebp ;断在了这里
004015A2 |. 89E5 mov ebp, esp
004015A4 |. 83EC 3C sub esp, 3C
004015A7 |. 53 push ebx
004015A8 |. 56 push esi
004015A9 |. 57 push edi
004015AA |. 8B45 0C mov eax, [ebp+C]
004015AD |. 83F8 10 cmp eax, 10 ; Switch (cases 10..111)
004015B0 |. 0F84 BB000000 je 00401671
004015B6 |. 0F8C C4000000 jl 00401680
004015BC |. 3D 10010000 cmp eax, 110
004015C1 |. 74 0C je short 004015CF
004015C3 |. 3D 11010000 cmp eax, 111
004015C8 |. 74 1E je short 004015E8
004015CA |. E9 B1000000 jmp 00401680
004015CF |> FF75 14 push dword ptr [ebp+14] ; Case 110 (WM_INITDIALOG) of switch 004015AD
004015D2 |. FF75 10 push dword ptr [ebp+10]
004015D5 |. FF75 08 push dword ptr [ebp+8]
004015D8 |. E8 C0FFFFFF call 0040159D
004015DD |. 83C4 0C add esp, 0C
004015E0 |. 31C0 xor eax, eax
004015E2 |. 40 inc eax
004015E3 |. E9 9A000000 jmp 00401682
004015E8 |> 0FB745 10 movzx eax, word ptr [ebp+10] ; Case 111 (WM_COMMAND) of switch 004015AD
004015EC |. 25 FFFF0000 and eax, 0FFFF
004015F1 |. 83F8 01 cmp eax, 1 ; Switch (cases 1..3) Check按钮
004015F4 |. 74 0C je short 00401602
004015F6 |. 83F8 02 cmp eax, 2 ; Exit按钮
004015F9 |. 74 6A je short 00401665
004015FB |. 83F8 03 cmp eax, 3 ; About按钮
004015FE |. 74 43 je short 00401643
00401600 |. EB 7E jmp short 00401680
00401602 |> 8D7D D3 lea edi, [ebp-2D] ; Case 1 of switch 004015F1
00401605 |. 8D35 5E254000 lea esi, [40255E]
0040160B |. B9 29000000 mov ecx, 29
00401610 |. F3:A4 rep movs byte ptr es:[edi],>
00401612 |. 8D7D C7 lea edi, [ebp-39]
00401615 |. 8D35 87254000 lea esi, [402587]
0040161B |. B9 03000000 mov ecx, 3
00401620 |. F3:A5 rep movs dword ptr es:[edi]>
00401622 |. FF75 08 push dword ptr [ebp+8]
00401625 |. E8 DFFEFFFF call 00401509 ; 关键函数
0040162A |. 59 pop ecx
0040162B |. 09C0 or eax, eax
0040162D |. 74 53 je short 00401682 ; 关键点
0040162F |. 6A 40 push 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00401631 |. 8D45 C7 lea eax, [ebp-39] ; |
00401634 |. 50 push eax ; |Title
00401635 |. 8D45 D3 lea eax, [ebp-2D] ; |
00401638 |. 50 push eax ; |Text
00401639 |. FF75 08 push dword ptr [ebp+8] ; |hOwner
0040163C |. E8 1B010000 call 0040175C ; \MessageBoxA
00401641 |. EB 3D jmp short 00401680
00401643 |> 8D05 0E264000 lea eax, [40260E] ; Case 3 of switch 004015F1
00401649 |. 8945 F8 mov [ebp-8], eax
0040164C |. 8D05 93254000 lea eax, [402593]
00401652 |. 8945 F4 mov [ebp-C], eax
00401655 |. 6A 40 push 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00401657 |. FF75 F8 push dword ptr [ebp-8] ; |Title
0040165A |. 50 push eax ; |Text => "You have to make an own working keygen!",LF,"Send the solutions to: t.h.e.b.i.g.m.a.n@gmx.net",LF,"Patching is not allowed!",LF,LF,"Enjoy !"
0040165B |. FF75 08 push dword ptr [ebp+8] ; |hOwner
0040165E |. E8 F9000000 call 0040175C ; \MessageBoxA
00401663 |. EB 1B jmp short 00401680
00401665 |> 6A 01 push 1 ; /Result = 1; Case 2 of switch 004015F1
00401667 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
0040166A |. E8 B1000000 call 00401720 ; \EndDialog
0040166F |. EB 0F jmp short 00401680
00401671 |> 6A 00 push 0 ; /Result = 0; Case 10 (WM_CLOSE) of switch 004015AD
00401673 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
00401676 |. E8 A5000000 call 00401720 ; \EndDialog
0040167B |. 31C0 xor eax, eax
0040167D |. 40 inc eax
0040167E |. EB 02 jmp short 00401682
00401680 |> 31C0 xor eax, eax ; Default case of switch 004015F1
00401682 |> 5F pop edi
00401683 |. 5E pop esi
00401684 |. 5B pop ebx
00401685 |. C9 leave
00401686 \. C2 1000 retn 10
看来上面就是消息处理函数了,OLLY注释的已经很清楚了。我们很轻易的可以找到按钮的消息处理过程,界面上共有3个按钮,WM_COMMAND下共有3个分支。根据跳转的位置我们可以分析出第一分支就是我们要找的Check的处理过程,第2、3分别为 Exit 和 About。我们关心的分支中只有一个自定义Call,紧跟着这个Call就是一个判断跳转,在这个Call上设断点。更改je 为 jne ,F9,我们期待的对话框就出来了。看来我们的那个Call就是关键了。删除其他断点,输入用户名、注册码,check。
第二部分 分析
正如我们期待的那样,程序断在了我们下断的Call上。F7,跟进。
00401509 /$ 55 push ebp ; important Call
0040150A |. 89E5 mov ebp, esp
0040150C |. 81EC 00010000 sub esp, 100
00401512 |. 53 push ebx
00401513 |. 56 push esi
00401514 |. 57 push edi
00401515 |. 8DBD 00FFFFFF lea edi, [ebp-100]
0040151B |. 8D35 5E244000 lea esi, [40245E]
00401521 |. B9 40000000 mov ecx, 40
00401526 |. F3:A5 rep movs dword ptr es:[ed>
00401528 |. 68 00010000 push 100 ; /Count = 100 (256.)
0040152D |. 8D85 00FFFFFF lea eax, [ebp-100] ; |
00401533 |. 50 push eax ; |Buffer
00401534 |. 6A 65 push 65 ; |ControlID = 65 (101.)
00401536 |. FF75 08 push dword ptr [ebp+8] ; |hWnd
00401539 |. E8 FA010000 call 00401738 ; \GetDlgItemTextA
0040153E |. 89C3 mov ebx, eax ; 获取用户名,EAX中为用户名字符数
00401540 |. 09DB or ebx, ebx
00401542 |. 75 04 jnz short 00401548 ; 用户名字符数不为0则跳下计算
00401544 |. 31C0 xor eax, eax
00401546 |. EB 50 jmp short 00401598 ; 用户名为0跳走
00401548 |> BF BC020000 mov edi, 2BC ; 700d
0040154D |. BE 30000000 mov esi, 30 ; 48d
00401552 |. B8 48000000 mov eax, 48 ; 72d
00401557 |. 99 cdq ; Eax符号扩展 符号位扩展到Edx中
00401558 |. F7FB idiv ebx ; eax/ebx 商在eax 中 余数在edx
0040155A |. 29C6 sub esi, eax ; esi=固定数30h-(固定数 48h/用户名字符数)
0040155C |. 8D34B6 lea esi, [esi+esi*4] ; esi=esi+esi*4
0040155F |. 29F7 sub edi, esi ; edi=edi-esi
00401561 |. 6BFF 6B imul edi, edi, 6B ; edi=edi*6bh
00401564 |. 81EF 6CCF0000 sub edi, 0CF6C ; edi=edi-0cf6ch
0040156A |. 81FF 00230000 cmp edi, 2300 ; edi=2300h?(由此可向上反推用户名字符数=3)
00401570 |. 7F 08 jg short 0040157A ; 用户名字符数<3 则跳出
00401572 |. 81FF 90010000 cmp edi, 190 ; 反推用户名字符数为9
00401578 |. 7D 04 jge short 0040157E ; 用户名字符数>9则跳出
0040157A |> 31C0 xor eax, eax
0040157C |. EB 1A jmp short 00401598
0040157E |> 8D85 00FFFFFF lea eax, [ebp-100] ; eax=用户名地址
00401584 |. 50 push eax ; 用户名地址
00401585 |. 53 push ebx ; 用户名字符数
00401586 |. FF75 08 push dword ptr [ebp+8] ; 对话框HWND
00401589 |. E8 77FDFFFF call 00401305 ; 关键 用户名变换与注册码比较函数
0040158E |. 83C4 0C add esp, 0C
00401591 |. 09C0 or eax, eax
00401593 |. 74 03 je short 00401598
00401595 |. 31C0 xor eax, eax
00401597 |. 40 inc eax
00401598 |> 5F pop edi
00401599 |. 5E pop esi
0040159A |. 5B pop ebx
0040159B |. C9 leave
0040159C \. C3 retn
分析此函数可看出,这个函数在读取了用户名后,对用户名长度进行了变换和比较,用户名长度符合作者要求之后,程序进入了下一个关键的Call。这个函数中用户名长度变换比较过程分析整理如下:
设 n=用户名长度,则n必须满足:2300H<{2bcH-[30H-int(48H/n)]*5}*6bH-0cfbcH<190H。
晕,还是一道比较复杂的数学题。还好学过数学,由上式可得符合条件的n:3<=n<=9。
分析完毕,F7,进入下一个Call。
00401305 /$ 55 push ebp
00401306 |. 89E5 mov ebp, esp
00401308 |. 81EC 2C040000 sub esp, 42C
0040130E |. 53 push ebx
0040130F |. 56 push esi
00401310 |. 57 push edi
00401311 |. 8DBD FCFEFFFF lea edi, [ebp-104] ; 目的操作数地址 ebp-104
00401317 |. 8D35 38204000 lea esi, [402038] ; 源操作数地址 [402038]
0040131D |. B9 40000000 mov ecx, 40 ; 循环次数 40h
00401322 |. F3:A5 rep movs dword ptr es:[edi], dwor>; 一次操作大小DWORD
00401324 |. 8DBD E1FBFFFF lea edi, [ebp-41F]
0040132A |. 8D35 38214000 lea esi, [402138] ; 源操作数地址[402138]
00401330 |. B9 40000000 mov ecx, 40 ; 40h
00401335 |. F3:A5 rep movs dword ptr es:[edi], dwor>; DWORD
00401337 |. 8DBD E1FDFFFF lea edi, [ebp-21F] ; 后面要用到
0040133D |. 8D35 38224000 lea esi, [402238] ; [402238]
00401343 |. B9 40000000 mov ecx, 40 ; 40h
00401348 |. F3:A5 rep movs dword ptr es:[edi], dwor>; DWORD
0040134A |. 8DBD E1FCFFFF lea edi, [ebp-31F]
00401350 |. 8D35 38234000 lea esi, [402338] ; [402338]
00401356 |. B9 40000000 mov ecx, 40 ; 40h
0040135B |. F3:A5 rep movs dword ptr es:[edi], dwor>; DWORD
0040135D |. 8DBD DCFBFFFF lea edi, [ebp-424]
00401363 |. 8D35 38244000 lea esi, [402438] ; [402438] 内容为“%c%s”
00401369 |. B9 05000000 mov ecx, 5 ; 5h
0040136E |. F3:A4 rep movs byte ptr es:[edi], byte >; Byte
00401370 |. 8DBD D6FBFFFF lea edi, [ebp-42A]
00401376 |. 8D35 3D244000 lea esi, [40243D] ; [40243D] 内容为“%s-%d”
0040137C |. B9 03000000 mov ecx, 3 ; 3h
00401381 |. F3:66:A5 rep movs word ptr es:[edi], word >; Word
00401384 |. 8DBD E1FEFFFF lea edi, [ebp-11F]
0040138A |. 8D35 43244000 lea esi, [402443] ; [402443] 内容为“A……Z”
00401390 |. B9 1B000000 mov ecx, 1B ; 1bh
00401395 |. F3:A4 rep movs byte ptr es:[edi], byte >; Byte
00401397 |. C745 FC 00000>mov dword ptr [ebp-4], 0 ; [ebp-4]=0 以备后用
0040139E |. 68 00010000 push 100 ; /Count = 100 (256.)
004013A3 |. 8D85 E1FCFFFF lea eax, [ebp-31F] ; |
004013A9 |. 50 push eax ; |Buffer
004013AA |. 6A 66 push 66 ; |ControlID = 66 (102.)
004013AC |. FF75 08 push dword ptr [ebp+8] ; |hWnd
004013AF |. E8 84030000 call 00401738 ; \GetDlgItemTextA
004013B4 |. 09C0 or eax, eax ; 获取注册码 eax中为注册码字符数
004013B6 |. 0F84 48010000 je 00401504 ; 注册码字符数=0则跳走
004013BC |. B8 CF110000 mov eax, 11CF ; eax=11cfh
004013C1 |. 0FB68D E1FCFF>movzx ecx, byte ptr [ebp-31F] ; ecx=注册码第一位
004013C8 |. 99 cdq
004013C9 |. F7F9 idiv ecx
004013CB |. 83FA 17 cmp edx, 17
004013CE |. 74 07 je short 004013D7 ; 11cfh/s0 余数不等17则跳走
004013D0 |. 31C0 xor eax, eax
004013D2 |. E9 2D010000 jmp 00401504
004013D7 |> 31DB xor ebx, ebx ; ebx=0 准备循环
004013D9 |. EB 0B jmp short 004013E6
004013DB |> 8B45 10 /mov eax, [ebp+10] ; 这个循环为计算
004013DE |. 0FBE0418 |movsx eax, byte ptr [eax+ebx] ; 用户名所有字符ASCII码相加之和
004013E2 |. 0145 FC |add [ebp-4], eax ; 将这个计算值保存于[EBP-4]处
004013E5 |. 43 |inc ebx ; 以备后用
004013E6 |> 3B5D 0C cmp ebx, [ebp+C]
004013E9 |.^ 7C F0 \jl short 004013DB ; 循环结束
004013EB |. 31DB xor ebx, ebx ; ebx=0 准备下一个循环
004013ED |. E9 83000000 jmp 00401475
004013F2 |> 8B55 10 /mov edx, [ebp+10] ; edx = addr 用户名
004013F5 |. 0FBE3C1A |movsx edi, byte ptr [edx+ebx] ; edi=取用户名中的一位
004013F9 |. 8B75 FC |mov esi, [ebp-4] ; esi=上面计算的和值
004013FC |. 89D9 |mov ecx, ebx ; ecx=ebx(循环数)
004013FE |. C1E1 02 |shl ecx, 2 ; ecx 逻辑左移2位
00401401 |. 89DA |mov edx, ebx ; edx=ebx(循环数)
00401403 |. 42 |inc edx ; edx=edx+1
00401404 |. 29D1 |sub ecx, edx ; ecx=ecx-edx(第一次循环ecx=-1)
00401406 |. 0FB68C0D E1FE>|movzx ecx, byte ptr [ebp+ecx-11F] ; ecx= 表中值(位置和Ecx相关)
0040140E |. 89FA |mov edx, edi ; edx=用户名中的一位
00401410 |. 31CA |xor edx, ecx ; edx=edx Xor ecx
00401412 |. 89F1 |mov ecx, esi ; ecx=esi(和值)
00401414 |. 0FAFCB |imul ecx, ebx ; ecx=ecx(和值) * ebx (循环数)
00401417 |. 29F1 |sub ecx, esi ; ecx=ecx - esi (和值)
00401419 |. 89CE |mov esi, ecx ; esi=ecx(和值*循环数-和值)
0040141B |. 83F6 FF |xor esi, FFFFFFFF ; esi=esi Xor FFFFFFFF
0040141E |. 8DB432 4D0100>|lea esi, [edx+esi+14D] ; esi = edx+esi+14dh(得到值1)
00401425 |. 8B4D 0C |mov ecx, [ebp+C] ; ecx=用户名字符数
00401428 |. 89DA |mov edx, ebx ; edx=循环数
0040142A |. 83C2 03 |add edx, 3 ; edx=edx+3
0040142D |. 0FAFCA |imul ecx, edx ; ecx=ecx(用户名字符数)*edx(循环数+3)
00401430 |. 0FAFCF |imul ecx, edi ; ecx=ecx*edi(用户名中的第循环数+1位)值2
00401433 |. 89F0 |mov eax, esi
00401435 |. 01C8 |add eax, ecx ; eax=值1+值2(得到值3)
00401437 |. B9 0A000000 |mov ecx, 0A ; ecx=0ah
0040143C |. 31D2 |xor edx, edx ; edx=0
0040143E |. F7F1 |div ecx ; eax=eax/ecx
00401440 |. 83C2 30 |add edx, 30 ; edx=值3%0ah+30h
00401443 |. 88941D FCFEFF>|mov [ebp+ebx-104], dl
0040144A |. 0FB6BC1D FCFE>|movzx edi, byte ptr [ebp+ebx-104] ; edi=dl
00401452 |. 81F7 ACAD0000 |xor edi, 0ADAC ; edi=edi Xor 0adach(得到值4)
00401458 |. 89DE |mov esi, ebx ; esi=循环数
0040145A |. 83C6 02 |add esi, 2 ; esi =循环数+2
0040145D |. 89F8 |mov eax, edi
0040145F |. 0FAFC6 |imul eax, esi ; eax=值4 * (循环数+2)
00401462 |. B9 0A000000 |mov ecx, 0A ; ecx=0ah
00401467 |. 99 |cdq
00401468 |. F7F9 |idiv ecx ; eax=eax/ecx
0040146A |. 83C2 30 |add edx, 30 ; edx=eax%ecx+30
0040146D |. 88941D FCFEFF>|mov [ebp+ebx-104], dl ; dl 就是变换码 之一
00401474 |. 43 |inc ebx
00401475 |> 3B5D 0C cmp ebx, [ebp+C]
00401478 |.^ 0F8C 74FFFFFF \jl 004013F2
0040147E |. 8D85 FCFEFFFF lea eax, [ebp-104] ; eax=addr 所有变换码 n位
00401484 |. 50 push eax
00401485 |. 6A 54 push 54 ; 字符 T
00401487 |. 8D85 DCFBFFFF lea eax, [ebp-424]
0040148D |. 50 push eax ; |Format "%c%s"
0040148E |. 8D85 E1FBFFFF lea eax, [ebp-41F] ; |
00401494 |. 50 push eax ; |s
00401495 |. E8 CE020000 call 00401768 ; \wsprintfA 变换成TXXXXX的格式 n个X
0040149A |. 8B7D 0C mov edi, [ebp+C] ; edi=用户名字符数
0040149D |. 89F8 mov eax, edi
0040149F |. 0FAF45 FC imul eax, [ebp-4] ; eax=字符数*和值
004014A3 |. B9 64000000 mov ecx, 64 ; ecx=64h
004014A8 |. 99 cdq
004014A9 |. F7F9 idiv ecx ; eax=eax/ecx
004014AB |. 89D7 mov edi, edx ; edi=eax%ecx
004014AD |. 83C7 30 add edi, 30 ; edi= (字符数 * 和值)%64h +30h
004014B0 |. 57 push edi
004014B1 |. 8DBD E1FBFFFF lea edi, [ebp-41F]
004014B7 |. 57 push edi
004014B8 |. 8DBD D6FBFFFF lea edi, [ebp-42A]
004014BE |. 57 push edi ; |Format "%s-%d"
004014BF |. 8DBD E1FDFFFF lea edi, [ebp-21F] ; |
004014C5 |. 57 push edi ; |s
004014C6 |. E8 9D020000 call 00401768 ; \wsprintfA 变换成 TXXXXX-XXX的形式
004014CB |. 83C4 20 add esp, 20
004014CE |. 8D8D E1FDFFFF lea ecx, [ebp-21F] ; ecx=addr 用户名变换码
004014D4 |. 83C8 FF or eax, FFFFFFFF
004014D7 |> 40 /inc eax
004014D8 |. 803C01 00 |cmp byte ptr [ecx+eax], 0
004014DC |.^ 75 F9 \jnz short 004014D7 ; 循环得到变换码长度
004014DE |. 50 push eax ; /Arg3 变换码长度
004014DF |. 8D85 E1FCFFFF lea eax, [ebp-31F] ; |
004014E5 |. 50 push eax ; |Arg2 addr 输入的注册码
004014E6 |. 8D85 E1FDFFFF lea eax, [ebp-21F] ; |
004014EC |. 50 push eax ; |Arg1 addr 变换码
004014ED |. E8 D0FDFFFF call 004012C2 ; \crackme6.004012C2
004014F2 |. 83C4 0C add esp, 0C
004014F5 |. 83F8 00 cmp eax, 0
004014F8 |. 75 07 jnz short 00401501
004014FA |. B8 00000000 mov eax, 0
004014FF |. EB 03 jmp short 00401504
00401501 |> 31C0 xor eax, eax
00401503 |. 40 inc eax
00401504 |> 5F pop edi
00401505 |. 5E pop esi
00401506 |. 5B pop ebx
00401507 |. C9 leave
00401508 \. C3 retn
好长,分析下来还真的需要点耐心。这个函数首先为程序作了一些准备,读入了一些固定值作为表,以备后面查询;然后读入我们输入的注册码,取出第一位进行变换比较,符合条件后继续;利用一个循环获得用户名ASCII码和值;利用另一个循环获得变换码第一部分,并将其格式化为TXXXXX的形式,其中X位数同用户名位数;计算变化码第二部分,并整合第一部分成TXXXXX-XXX形式;将变化码长度,假注册码及变换码入栈作为参数,进入下一个关键Call。
注册码第一位变化比较算法:设 n = 注册码第一位的ASCII值,则n必须符合 11cfH%n=17H,这个不难,我们可以穷举。
变换码第一部分算法比较复杂(对于我来说,:)),用我极其不熟练的C+ASM语言进行表达如下:
设:n=用户名长度,U[i]=用户名ASCII数组,Hez=用户名ASCII和值,T[i]=表A....Z,S[i]=第一部分变换码数组
for(i=0;i++;i<n)
{
值1=T[3*i-1] Xor U[i] + Hez*(i-1) Xor FFFFFFFF + 14dH
值2=n*(i+3)*U[i]
值3=值1+值2
edx=值3%0aH+30H
值4=dl Xor 0adacH
edx=值4*(i+2)%0aH+30H
S[i]=dl
}
希望上面的表达大家能理解。这里值得注意的是当i=0时,将访问T[-1],查看函数的准备部分,我们可以得到T[-1]=00H。
变换码第二部分算法比较简单:借用上面假设,取 (n*Hez)%64H+30H 的十进制字符串形式,如 结果=7bH,则第二部分变换码为 123。
好了,总算分析完毕。我们进入下一个Call。
004012C2 /$ 55 push ebp
004012C3 |. 89E5 mov ebp, esp
004012C5 |. 53 push ebx
004012C6 |. 56 push esi
004012C7 |. 57 push edi
004012C8 |. 8B5D 10 mov ebx, [ebp+10] ; ebx=变换码长度
004012CB |. 31F6 xor esi, esi
004012CD |. 46 inc esi ; esi 作为循环变量 次数 为变换码长度
004012CE |. EB 29 jmp short 004012F9
004012D0 |> 8B55 08 /mov edx, [ebp+8] ; edx=addr 变换码
004012D3 |. 0FBE3C32 |movsx edi, byte ptr [edx+esi] ; edi=变换码第n+1位
004012D7 |. 89F8 |mov eax, edi ; eax=edi
004012D9 |. 83F0 20 |xor eax, 20 ; eax=eax Xor 20h
004012DC |. B9 0A000000 |mov ecx, 0A ; ecx=0ah
004012E1 |. 99 |cdq
004012E2 |. F7F9 |idiv ecx
004012E4 |. 89D7 |mov edi, edx ; edi=eax%ecx
004012E6 |. 83C7 30 |add edi, 30 ; edi=edi+30h
004012E9 |. 8B55 0C |mov edx, [ebp+C]
004012EC |. 0FBE1432 |movsx edx, byte ptr [edx+esi] ; edx=注册码第n+1位
004012F0 |. 39D7 |cmp edi, edx
004012F2 74 04 je short 004012F8 ; 等则比较下一位
004012F4 |. 31C0 |xor eax, eax
004012F6 |. EB 08 |jmp short 00401300
004012F8 |> 46 |inc esi
004012F9 |> 39DE cmp esi, ebx
004012FB |.^ 7C D3 \jl short 004012D0
004012FD |. 31C0 xor eax, eax
004012FF |. 40 inc eax
00401300 |> 5F pop edi
00401301 |. 5E pop esi
00401302 |. 5B pop ebx
00401303 |. 5D pop ebp
00401304 \. C3 retn
终于看到了曙光,这里再也没有下一个call了。这个函数过程比较明了,将得到的变换码进行再次变换,然后逐位与我们输入的注册码进行比较,这里舍弃了第一位,其余各位均等则函数设置Eax=1 后返回。变换比较算法如下:
设Z[i]=我们输入的注册码数组 , S[i]=变换码数组 , n=变换码长度
则必须满足: Z[i]= (S[i] Xor 20H)%0aH+30H (其中 i From 1 to n)
至此,整个算法分析完毕。 第三部分,编写注册机
我正在学习汇编语言,就用汇编写个注册机吧。这样我还可以把大部分代码搬到我的程序里。序列号产生程序如下:
GenKey proc uses edi, lpUserName:DWORD,nU:DWORD
;生成序列号函数,参数分别为:指向用户名的指针,用户名字符数,返回值为 注册码 指针
local lpBuffer[128]:BYTE
local szBuffer[128]:BYTE
local hezhi:DWORD
local zcm1[32]:Byte
.if (nU>2) && (nU<10)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;计算注册码第一位字符
mov ecx,20h
xor edi,edi
.while ecx<129
mov eax,11cfh
cdq
idiv ecx
.if edx==17h
mov byte ptr [edi+lpBuffer],cl
inc edi
.endif
inc ecx
.endw
mov byte ptr [edi+lpBuffer],0
invoke wsprintf,addr szBuffer,addr szFormat1,addr lpBuffer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;计算用户名和值 save hezhi
mov eax,0
mov hezhi,eax
xor ebx,ebx
.while ebx<nU
mov eax,lpUserName
movsx edx,byte ptr [eax+ebx]
add hezhi,edx
inc ebx
.endw
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 计算第一部分中间码 save szBuffer
lea eax,zcm1
mov byte ptr [eax-1],0
xor ebx,ebx
.while ebx<nU
mov edx,lpUserName
movsx edi,byte ptr [edx+ebx]
mov esi,hezhi
mov ecx,ebx
shl ecx,2h
mov edx,ebx
inc edx
sub ecx,edx
movzx ecx,byte ptr[ecx+offset table]
mov edx,edi
xor edx,ecx
mov ecx,esi
imul ecx,ebx
sub ecx,esi
mov esi,ecx
xor esi,0ffffffffh
lea esi,[edx+esi+14dh]
mov ecx,nU
mov edx,ebx
add edx,3h
imul ecx,edx
imul ecx,edi
mov eax,esi
add eax,ecx
mov ecx,0ah
xor edx,edx
div ecx
add edx,30h
lea eax,zcm1
mov [ebx+eax],dl
movzx edi,byte ptr [ebx+eax]
xor edi,0adach
mov esi,ebx
add esi,2h
mov eax,edi
imul eax,esi
mov ecx,0ah
cdq
idiv ecx
add edx,30h
lea eax,zcm1
mov [ebx+eax],dl
inc ebx
.endw
lea eax,zcm1
mov byte ptr [ebx+eax],0
invoke wsprintf,addr szBuffer ,addr szFormat2,addr zcm1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;计算中间码2 save edi
mov edi,nU
mov eax,edi
imul eax,hezhi
mov ecx,64h
cdq
idiv ecx
mov edi,edx
add edi,30h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;计算完整中间码 save szlpBuffer
invoke wsprintf,addr lpBuffer,addr szFormat3,addr szBuffer,edi
mov ebx,eax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;计算注册码 save szBuffer
xor esi,esi
inc esi
.while esi<ebx
lea edx,lpBuffer
movsx edi,byte ptr [edx+esi]
mov eax,edi
xor eax,20h
mov ecx,0ah
cdq
idiv ecx
add edx,30h
lea eax,szBuffer
mov byte ptr[eax+esi],dl
inc esi
.endw
lea eax,szBuffer
mov byte ptr[eax],38h
mov byte ptr[eax+ebx],0
invoke StrCpyNW,addr TestString,addr szBuffer,ebx
ret
.else
invoke MessageBox,NULL,offset szErrUsername,offset szCaption,MB_OK or MB_SYSTEMMODAL
mov eax,0
ret
.endif
GenKey endp
感谢您一直等我啰嗦到这里,等我一切作完,我看了4nil的过程。我在重新载入这个CrackMe时,就发现我下断的地方数据变了,我怀疑加了壳,可不但OLLYDBG没提示我有壳,就是我请出PEID,也没有查出什么。带壳分析完了整个程序,上述问题仍然迷惑不解。4nil分析的简单明了,因为我是第一次如此分析CrackMe,所以把整个过程都罗嗦了上来,再次感谢您读到这里。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年12月03日 23:20:18
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
上传的附件: