KeyMe2 是在crackmes.de上找到的一个CrackMe,没有加过壳。这个CrackMe的破解难度不大,但各种判断比较多,有些语句末尾以“//”起

始的说明文字,是我添加的注释。

用OllyICE打开,F9运行,却自动退出,包括OllyICE也被关闭了。只好一步步进行,首先运行的是下面这段语句(1):

00401000 >/$/EB 0F      jmp short 00401011
00401002 |. |4C 65 74 27 7> ascii "Let's start :)",0
00401011 |> \E8 02080000   call <jmp.&kernel32.GetCurrentProcess>; [GetCurrentProcessId
00401016 |. A3 D0324000   mov dword ptr [4032D0], eax
0040101B |. 6A 00      push 0                 ; /ProcessID = 0
0040101D |. 6A 02      push 2                 ; |Flags = TH32CS_SNAPPROCESS
0040101F |. E8 E8070000   call <jmp.&kernel32.CreateToolhelp32S>; \CreateToolhelp32Snapshot
00401024 |. 83F8 00     cmp eax, 0
00401027 |. 75 07      jnz short 00401030
00401029 |. 6A 00      push 0                 ; /ExitCode = 0
0040102B |. E8 E2070000   call <jmp.&kernel32.ExitProcess>    ; \ExitProcess
00401030 |> A3 D4324000   mov dword ptr [4032D4], eax
00401035 |. C705 D8324000> mov dword ptr [4032D8], 200
0040103F |. 68 D8324000   push 004032D8              ; /pProcessentry = 复件_Key.004032D8
00401044 |. FF35 D4324000  push dword ptr [4032D4]         ; |hSnapshot = NULL
0040104A |. E8 FF070000   call <jmp.&kernel32.Process32First>  ; \Process32First
0040104F |. 83F8 00     cmp eax, 0
00401052 |. 75 07      jnz short 0040105B
00401054 |. 6A 00      push 0                 ; /ExitCode = 0
00401056 |. E8 B7070000   call <jmp.&kernel32.ExitProcess>    ; \ExitProcess
0040105B |> A1 E0324000   mov eax, dword ptr [4032E0]
00401060 |. 3B05 D0324000  cmp eax, dword ptr [4032D0]
00401066 |. 74 35      je short 0040109D
00401068 |> 68 D8324000   /push 004032D8             ; /pProcessentry = 复件_Key.004032D8
0040106D |. FF35 D4324000  |push dword ptr [4032D4]        ; |hSnapshot = NULL
00401073 |. E8 DC070000   |call <jmp.&kernel32.Process32Next>   ; \Process32Next
00401078 |. 83F8 00     |cmp eax, 0
0040107B |. 75 11      |jnz short 0040108E
0040107D |. E8 A2070000   |call <jmp.&kernel32.GetLastError>   ; [GetLastError
00401082 |. 83F8 0C     |cmp eax, 0C
00401085 |. 74 07      |je short 0040108E
00401087 |. 6A 00      |push 0                 ; /ExitCode = 0
00401089 |. E8 84070000   |call <jmp.&kernel32.ExitProcess>    ; \ExitProcess
0040108E |> A1 E0324000   |mov eax, dword ptr [4032E0]
00401093 |. 3B05 D0324000  |cmp eax, dword ptr [4032D0]
00401099 |. 74 02      |je short 0040109D
0040109B |.^ EB CB      \jmp short 00401068
0040109D |> FF35 D4324000  push dword ptr [4032D4]         ; /hObject = NULL
004010A3 |. E8 58070000   call <jmp.&kernel32.CloseHandle>    ; \CloseHandle
004010A8 |. A1 F0324000   mov eax, dword ptr [4032F0]
004010AD |. A3 E0344000   mov dword ptr [4034E0], eax
004010B2 |. 6A 00      push 0                 ; /ProcessID = 0
004010B4 |. 6A 02      push 2                 ; |Flags = TH32CS_SNAPPROCESS
004010B6 |. E8 51070000   call <jmp.&kernel32.CreateToolhelp32S> ; \CreateToolhelp32Snapshot
004010BB |. 83F8 00     cmp eax, 0
004010BE |. 75 07      jnz short 004010C7
004010C0 |. 6A 00 push 0                      ; /ExitCode = 0
004010C2 |. E8 4B070000   call <jmp.&kernel32.ExitProcess>    ; \ExitProcess
004010C7 |> A3 D4324000   mov dword ptr [4032D4], eax
004010CC |. C705 D8324000> mov dword ptr [4032D8], 200
004010D6 |. 68 D8324000   push 004032D8              ; /pProcessentry = 复件_Key.004032D8
004010DB |. FF35 D4324000  push dword ptr [4032D4]         ; |hSnapshot = NULL
004010E1 |. E8 68070000   call <jmp.&kernel32.Process32First>  ; \Process32First
004010E6 |. 83F8 00     cmp eax, 0
004010E9 |. 75 07      jnz short 004010F2
004010EB |. 6A 00      push 0                 ; /ExitCode = 0
004010ED |. E8 20070000   call <jmp.&kernel32.ExitProcess>    ; \ExitProcess
004010F2 |> A1 E0324000   mov eax, dword ptr [4032E0]
004010F7 |. 3B05 E0344000  cmp eax, dword ptr [4034E0]
004010FD |. 74 35      je short 00401134
004010FF |> 68 D8324000   /push 004032D8             ; /pProcessentry = 复件_Key.004032D8
00401104 |. FF35 D4324000  |push dword ptr [4032D4]        ; |hSnapshot = NULL
0040110A |. E8 45070000   |call <jmp.&kernel32.Process32Next>   ; \Process32Next
0040110F |. 83F8 00     |cmp eax, 0
00401112 |. 75 11      |jnz short 00401125
00401114 |. E8 0B070000   |call <jmp.&kernel32.GetLastError>   ; [GetLastError
00401119 |. 83F8 0C     |cmp eax, 0C
0040111C |. 74 07      |je short 00401125
0040111E |. 6A 00      |push 0                 ; /ExitCode = 0
00401120 |. E8 ED060000   |call <jmp.&kernel32.ExitProcess>    ; \ExitProcess
00401125 |> A1 E0324000   |mov eax, dword ptr [4032E0]
0040112A |. 3B05 E0344000  |cmp eax, dword ptr [4034E0]
00401130 |. 74 02      |je short 00401134
00401132 |.^ EB CB      \jmp short 004010FF
00401134 |> FF35 D4324000  push dword ptr [4032D4]         ; /hObject = NULL
0040113A |. E8 C1060000   call <jmp.&kernel32.CloseHandle>    ; \CloseHandle
0040113F |. 8D05 FC324000  lea eax, dword ptr [4032FC]
00401145 |. 50        push eax                ; /StringOrChar => ""
00401146 |. E8 79060000   call <jmp.&user32.CharLowerA>      ; \CharLowerA
0040114B |. 813D FC324000> cmp dword ptr [4032FC], 6C707865
00401155 |. 75 23      jnz short 0040117A
00401157 |. 813D 00334000> cmp dword ptr [403300], 7265726F
00401161 |. 75 17      jnz short 0040117A
00401163 |. 813D 04334000> cmp dword ptr [403304], 6578652E
0040116D |. 75 0B      jnz short 0040117A
0040116F |. 803D 08334000> cmp byte ptr [403308], 0
00401176 |. 75 02      jnz short 0040117A
00401178 |. EB 1A      jmp short 00401194
0040117A |> FF35 E0344000  push dword ptr [4034E0]         ; /ProcessId = 0
00401180 |. 6A 01      push 1                 ; |Inheritable = TRUE
00401182 |. 68 FF0F1F00   push 1F0FFF               ; |Access = PROCESS_ALL_ACCESS
00401187 |. E8 BC060000   call <jmp.&kernel32.OpenProcess>    ; \OpenProcess
0040118C |. 6A 00      push 0                 ; /ExitCode = 0
0040118E |. 50        push eax                ; |hProcess
0040118F |. E8 CC060000   call <jmp.&kernel32.TerminateProcess> ; \TerminateProcess
00401194 |> 6A 00      push 0                 ; /pModule = NULL
00401196 |. E8 95060000   call <jmp.&kernel32.GetModuleHandleA> ; \GetModuleHandleA
0040119B |. A3 4C324000   mov dword ptr [40324C], eax
004011A0 |. 6A 00      push 0                 ; /lParam = NULL
004011A2 |. 68 BF114000   push 004011BF             ; |DlgProc = 复件_Key.004011BF
004011A7 |. 6A 00      push 0                 ; |hOwner = NULL
004011A9 |. 68 E9030000   push 3E9                ; |pTemplate = 3E9
004011AE |. FF35 4C324000  push dword ptr [40324C]        ; |hInst = NULL
004011B4 |. E8 11060000   call <jmp.&user32.DialogBoxParamA>   ; \DialogBoxParamA
004011B9 |. 50       push eax                ; /ExitCode
004011BA \. E8 53060000   call <jmp.&kernel32.ExitProcess>    ; \ExitProcess

单步跟踪后,会发现执行到下面这段语句,就会关闭OllyICE:

0040118C |. 6A 00      push 0                 ; /ExitCode = 0
0040118E |. 50        push eax                ; |hProcess
0040118F |. E8 CC060000   call <jmp.&kernel32.TerminateProcess> ; \TerminateProcess // 到这段语句就关闭OllyICE

而且是从下面这里跳转的:

0040114B |. 813D FC324000> cmp dword ptr [4032FC], 6C707865
00401155 |. 75 23      jnz short 0040117A
00401157 |. 813D 00334000> cmp dword ptr [403300], 7265726F
00401161 |. 75 17      jnz short 0040117A
00401163 |. 813D 04334000> cmp dword ptr [403304], 6578652E
0040116D |. 75 0B      jnz short 0040117A
0040116F |. 803D 08334000> cmp byte ptr [403308], 0
00401176 |. 75 02      jnz short 0040117A // 本段各比较中,有一个不匹配,即跳转至关闭语句,退出OllyICE

其实,“6C707865”,“7265726F”及“6578652E”每个字节值转换成对应的字符,并各自逆序排列联接起来,就是“explorer.exe”。由

于我对上述大段语句(1)中调用的API函数不是十分了解,只简单推断了一下,语句段(1)中判断调用KeyMe2的进程是不是“explorer.exe”,

如果不是,则终止相应进程。因此,上述最后一段进行比较语句当中,所有“jnz short 0040117A”都改为用“NOP”填充,OllyICE就不会

再被关闭了。接下来就可以进行调试。

随便输入serial(下面为了叙述及说明方便,把serial看作字符串,则serial[0]表示所输入serial的第一位,serial[X]为所输入serial的

第X+1位。),点击“Check serial”后,出来的错误提示大多是“Serial format is incorrect! Try again.”,再查找所有参考文本字符

串,发现有很多同样的提示,首先可以看到的是下面这段语句(2):

004012F7 /$ 83F8 00     cmp eax, 0
004012FA |. 75 01      jnz short 004012FD
004012FC |. C3       retn
004012FD |> 33C0      xor eax, eax
004012FF |. 33DB      xor ebx, ebx
00401301 |. 33C9      xor ecx, ecx
00401303 |. B8 04000000   mov eax, 4
00401308 |> 81B8 50324000> /cmp dword ptr [eax+403250], 3D6F6E2> // serial存放在以[403250]为起始地址的地方
00401312 |. 75 03      |jnz short 00401317 // “3D6F6E2”各字节值对应字符并逆序排列就是“.no=”
00401314 |. 43       |inc ebx
00401315 |. 8BC8      |mov ecx, eax // ecx储存的是serial中“.no=”起始的位置,设为X
00401317 |> 40       |inc eax
00401318 |. 83F8 09     |cmp eax, 9 // 4 <= eax < 9
0040131B |.^ 75 EB     \jnz short 00401308
0040131D |. 83FB 01     cmp ebx, 1 // 本段判断,以serial[4]-[8]为起始位,serial中包含且仅包含一个“.no=”
00401320 |. 74 14      je short 00401336
00401322 |. 6A 30      push 30                ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
00401324 |. 68 DE314000   push 004031DE             ; |Title = "Input error"
00401329 |. 68 EA314000   push 004031EA             ; |Text = "Serial format is incorrect! Try again."
0040132E |. 6A 00      push 0                 ; |hOwner = NULL
00401330 |. E8 B9040000   call <jmp.&user32.MessageBoxA>     ; \MessageBoxA
00401335 |. C3       retn
00401336 |> E8 24000000   call 0040135F // 从这里起,调用若干函数来操作数据或判断serial的正确性
0040133B |. E8 74000000   call 004013B4
00401340 |. E8 D7000000   call 0040141C
00401345 |. E8 1D010000   call 00401467
0040134A |. E8 AD010000   call 004014FC
0040134F |. E8 E5010000   call 00401539
00401354 |. E8 F9010000   call 00401552
00401359 |. E8 45020000   call 004015A3
0040135E \. C3       retn

接下来看语句段(2)中所调用函数中的第一个,语句段(3):

0040135F /$ C781 50324000> mov dword ptr [ecx+403250], 0 // 操作类型是dword,所以输入的serial[X]-[X+3],即“.no=”
00401369 |. 33DB      xor ebx, ebx         // 对应的位置被置空,''
0040136B |. EB 24      jmp short 00401391
0040136D |. 47 6F 20 61 7> ascii "Go away!",0
00401376 |. 49 27 6D 20 6> ascii "I'm just",0
0040137F |. 63 68 61 6E 6> ascii "changing",0
00401388 |. 62 75 66 66 6> ascii "buffers.",0
00401391 |> 8B1D 50324000  mov ebx, dword ptr [403250]
00401397 |. 891D 80324000  mov dword ptr [403280], ebx
0040139D |. 8B1D 54324000  mov ebx, dword ptr [403254] // 四个mov语句,将serial[0]-[7]
004013A3 |. 891D 84324000  mov dword ptr [403284], ebx // 移入[403280]为起始地址的地方
004013A9 |. C781 80324000> mov dword ptr [ecx+403280], 0 // 将[403280+X]起始的地方置空,即[403280]为起始地址的地方
004013B3 \. C3       retn             // 保存的是serial[0]-[X-1]

下面看语句段(4):

004013B4 /$ 80B9 5C324000> cmp byte ptr [ecx+40325C], 3A // 判断serial[12+X]是否为“:”(3A),不是则出错
004013BB |. 74 17      je short 004013D4
004013BD |. 6A 30      push 30                ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013BF |. 68 DE314000   push 004031DE             ; |Title = "Input error"
004013C4 |. 68 EA314000   push 004031EA             ; |Text = "Serial format is incorrect! Try again."
004013C9 |. 6A 00      push 0                 ; |hOwner = NULL
004013CB |. E8 1E040000   call <jmp.&user32.MessageBoxA>     ; \MessageBoxA
004013D0 |. 83C4 04     add esp, 4
004013D3 |. C3       retn
004013D4 |> 80B9 65324000> cmp byte ptr [ecx+403265], 3A // 判断serial[21+X]是否为“:”(3A),不是则出错
004013DB |. 74 17      je short 004013F4
004013DD |. 6A 30      push 30                ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013DF |. 68 DE314000   push 004031DE             ; |Title = "Input error"
004013E4 |. 68 EA314000   push 004031EA             ; |Text = "Serial format is incorrect! Try again."
004013E9 |. 6A 00      push 0                 ; |hOwner = NULL
004013EB |. E8 FE030000   call <jmp.&user32.MessageBoxA>     ; \MessageBoxA
004013F0 |. 83C4 04     add esp, 4
004013F3 |. C3       retn
004013F4 |> 80B9 6E324000> cmp byte ptr [ecx+40326E], 0 // 判断serial[30+X]是否为空(0),不是则出错
004013FB |. 74 17      je short 00401414
004013FD |. 6A 30      push 30                ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013FF |. 68 DE314000   push 004031DE             ; |Title = "Input error"
00401404 |. 68 EA314000   push 004031EA             ; |Text = "Serial format is incorrect! Try again."
00401409 |. 6A 00      push 0                 ; |hOwner = NULL
0040140B |. E8 DE030000   call <jmp.&user32.MessageBoxA>     ; \MessageBoxA
00401410 |. 83C4 04     add esp, 4
00401413 |. C3       retn
00401414 |> C681 6E324000> mov byte ptr [ecx+40326E], 0
0040141B \. C3       retn

语句段(4)说明了serial对格式的一些要求,以及其长度最长为X+30,即在第二个“:”后的长度最多为8位(serial[22+X]-[29+X])。通过

后面的语句还可以看到,其实应该是正好8位。注意,serial[0]-[X-1]即“.no=”之前的部分,serial[X+4]-[X+11],是在“.no=”和

“:”之间的部分,serial[X+13]-[X+20]为两个“:”之间的部分,而serial[22+X]-[29+X]则是最后的部分。

下面是语句段(5),没有什么特别的,主要是注意到serial[X+4]-[X+11]及serial[X+13]-[X+20]被移入了以[403290]为起始地址的地方,而

serial[X+22]-[X+29]被移入了以[4032A0]为起始地址的地方。

0040141C /$ 33DB      xor ebx, ebx
0040141E |. 8B99 54324000  mov ebx, dword ptr [ecx+403254]
00401424 |. 891D 90324000  mov dword ptr [403290], ebx
0040142A |. 8B99 58324000  mov ebx, dword ptr [ecx+403258]
00401430 |. 891D 94324000  mov dword ptr [403294], ebx
00401436 |. 8B99 5D324000  mov ebx, dword ptr [ecx+40325D]
0040143C |. 891D 98324000  mov dword ptr [403298], ebx
00401442 |. 8B99 61324000  mov ebx, dword ptr [ecx+403261]
00401448 |. 891D 9C324000  mov dword ptr [40329C], ebx
0040144E |. 8B99 66324000  mov ebx, dword ptr [ecx+403266]
00401454 |. 891D A0324000  mov dword ptr [4032A0], ebx
0040145A |. 8B99 6A324000  mov ebx, dword ptr [ecx+40326A]
00401460 |. 891D A4324000  mov dword ptr [4032A4], ebx
00401466 \. C3       retn

语句段(6) ,就是将原来储存输入的serial的地方(以[403250]为起始地址)全部清空。

00401467 /$ EB 1A      jmp short 00401483
00401469 |. 4E 6F 74 68 6> ascii "Nothing",0
00401471 |. 69 6E 74 65 7> ascii "interesting",0
0040147D |. 68 65 72 65 2> ascii "here.",0
00401483 |> C705 50324000> mov dword ptr [403250], 0
0040148D |. C705 54324000> mov dword ptr [403254], 0
00401497 |. C705 58324000> mov dword ptr [403258], 0
004014A1 |. C705 5C324000> mov dword ptr [40325C], 0
004014AB |. C705 60324000> mov dword ptr [403260], 0
004014B5 |. C705 64324000> mov dword ptr [403264], 0
004014BF |. C705 68324000> mov dword ptr [403268], 0
004014C9 |. C705 6C324000> mov dword ptr [40326C], 0
004014D3 |. C705 70324000> mov dword ptr [403270], 0
004014DD |. C705 74324000> mov dword ptr [403274], 0
004014E7 |. C705 78324000> mov dword ptr [403278], 0
004014F1 |. C705 7C324000> mov dword ptr [40327C], 0
004014FB \. C3       retn

下面是语句段(7),在语句段(3)中已经看到,以[00403280]起始,储存的就是serial[0]-[X-1]这个字符串,lstrlenA就是获得字符串的长度

X,然后的IsCharAlphaA循环进行判断,是否serial[0]-[X-1]每个都是字母,如果不是,则出错。

004014FC /$ 68 80324000   push 00403280             ; /String = ""
00401501 |. E8 6C030000   call <jmp.&kernel32.lstrlenA>     ; \lstrlenA
00401506 |. 33DB      xor ebx, ebx
00401508 |. 33C9      xor ecx, ecx
0040150A |. 8BD8      mov ebx, eax
0040150C |> 0FB68B 7F3240> /movzx ecx, byte ptr [ebx+40327F]
00401513 |. 51       |push ecx               ; /Char
00401514 |. E8 C9020000   |call <jmp.&user32.IsCharAlphaA>    ; \IsCharAlphaA
00401519 |. 83F8 00     |cmp eax, 0
0040151C |. 75 17      |jnz short 00401535
0040151E |. 6A 30      |push 30                ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
00401520 |. 68 DE314000   |push 004031DE             ; |Title = "Input error"
00401525 |. 68 EA314000   |push 004031EA             ; |Text = "Serial format is incorrect! Try again."
0040152A |. 6A 00      |push 0                ; |hOwner = NULL
0040152C |. E8 BD020000   |call <jmp.&user32.MessageBoxA>    ; \MessageBoxA
00401531 |. 83C4 04     |add esp, 4
00401534 |. C3       |retn
00401535 |> 4B       |dec ebx
00401536 |.^ 75 D4     \jnz short 0040150C
00401538 \. C3       retn

语句段(8),没有什么特别的,就是调用GetLogicalDrives和GetVersion,返回值分别保存在以[4032B0]和[4032B4]起始的地方。

00401539 /$ E8 EC020000   call <jmp.&kernel32.GetLogicalDrives> ; [GetLogicalDrives
0040153E |. A3 B0324000   mov dword ptr [4032B0], eax
00401543 |. 33C0      xor eax, eax
00401545 |. E8 F2020000   call <jmp.&kernel32.GetVersion>
0040154A |. A3 B4324000   mov dword ptr [4032B4], eax
0040154F |. 33C0      xor eax, eax
00401551 \. C3       retn

语句段(9),两个嵌套循环,分别将GetLogicalDrives和GetVersion的值与serial[0]-[X-1]及serial[X+4]-[X+11]、serial[X+13]-[X+20]进

行异或和移位运算,并存储在以[4032B0]开始的地址中。

00401552 /$ 33C0      xor eax, eax
00401554 |. 33DB      xor ebx, ebx
00401556 |. 33C9      xor ecx, ecx
00401558 |> 0FB683 803240> /movzx eax, byte ptr [ebx+403280]
0040155F |. 83F8 00     |cmp eax, 0
00401562 |. 74 17      |je short 0040157B
00401564 |> 3081 B0324000  |/xor byte ptr [ecx+4032B0], al
0040156A |. 41       ||inc ecx
0040156B |. D289 B0324000  ||ror byte ptr [ecx+4032B0], cl
00401571 |. 83F9 08     ||cmp ecx, 8
00401574 |.^ 75 EE     |\jnz short 00401564
00401576 |. 33C9      |xor ecx, ecx
00401578 |. 43       |inc ebx
00401579 |.^ EB DD     \jmp short 00401558
0040157B |> 33C0      xor eax, eax
0040157D |. 33DB      xor ebx, ebx
0040157F |. 33C9      xor ecx, ecx
00401581 |> 0FB683 903240> /movzx eax, byte ptr [ebx+403290]
00401588 |> 3081 B0324000  |xor byte ptr [ecx+4032B0], al
0040158E |. 41       |inc ecx
0040158F |. D289 B0324000  |ror byte ptr [ecx+4032B0], cl
00401595 |. 83F9 08     |cmp ecx, 8
00401598 |.^ 75 EE     |jnz short 00401588
0040159A |. 33C9      |xor ecx, ecx
0040159C |. 43       |inc ebx
0040159D |. 83FB 10     |cmp ebx, 10
004015A0 |.^ 75 DF     \jnz short 00401581
004015A2 \. C3       retn

语句段(10),最后的判断。将(9)中运算的值,再分为两个dword值相加后,存入eax。调用msvcrt.dll中的sprintf函数,将eax中的值以十六

进制形式转换为字符串(比如dword值3CDED7CD即转为字符串"3CDED7CD"),存入[004032C0]。将这个字符串与serial在第二个“:”后面的

部分对比,即与serial[X+22]-[X+29]比较,如果相等,则判断serial为正确。

004015A3 /$ A1 B0324000   mov eax, dword ptr [4032B0]
004015A8 |. 0305 B4324000  add eax, dword ptr [4032B4]
004015AE |. 50       push eax
004015AF |. 68 22324000   push 00403222             ; /ProcNameOrOrdinal = "sprintf"
004015B4 |. 68 17324000   push 00403217             ; |/FileName = "msvcrt.dll"
004015B9 |. E8 84020000   call <jmp.&kernel32.LoadLibraryA>   ; |\LoadLibraryA
004015BE |. 50       push eax                ; |hModule
004015BF |. E8 72020000   call <jmp.&kernel32.GetProcAddress>  ; \GetProcAddress
004015C4 |. 68 11324000   push 00403211             ; ASCII "%.8X"
004015C9 |. 68 C0324000   push 004032C0
004015CE |. FFD0      call eax
004015D0 |. 83C4 0C     add esp, 0C
004015D3 |. 8D05 A0324000  lea eax, dword ptr [4032A0] // serial最后部分,即serial[X+22]-[X+29]
004015D9 |. 50       push eax                ; /String2 => "" // serial[X+22]-[X+29]
004015DA |. 68 C0324000   push 004032C0             ; |String1 = "" // serial[X+22]-[X+29]的正确值
004015DF |. E8 88020000   call <jmp.&kernel32.lstrcmpA>     ; \lstrcmpA
004015E4 |. 83F8 00     cmp eax, 0
004015E7 |. 74 14      je short 004015FD
004015E9 |. 6A 10      push 10                ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
004015EB |. 68 A0314000   push 004031A0             ; |Title = "Serial not valid"
004015F0 |. 68 B1314000   push 004031B1             ; |Text = "Serial number is not valid for this machine!"
004015F5 |. 6A 00      push 0                 ; |hOwner = NULL
004015F7 |. E8 F2010000   call <jmp.&user32.MessageBoxA>     ; \MessageBoxA
004015FC |. C3       retn
004015FD |> 6A 40      push 40                ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004015FF |. 68 5C314000   push 0040315C             ; |Title = "Registered!"
00401604 |. 68 68314000   push 00403168             ; |Text = "Serial number is accepted! Now write small tutorial :)!"
00401609 |. 6A 00      push 0                 ; |hOwner = NULL
0040160B |. E8 DE010000   call <jmp.&user32.MessageBoxA>     ; \MessageBoxA
00401610 \. C3       retn

本来应该写出注册机,但是因为X值是不固定的,而且serial[0]-[X]、serial[X+4]-[X+11]和serial[X+13]-[X+20]都不确定,要根据给出的

值来计算最后的serial[X+22]-[X+29],实在有点儿麻烦,就不写了。另外,这篇破文好长,恐怕难免有我没有考虑周到或说清楚的地方,请

大家见谅并指正。

谢谢!

(附:KeyMe2

 

曹华 2007年8月22日

 

补充:

在查找所有参考文本字串时,可以看到“ollydbg.exe”、“idag.exe”和“w32dsm.exe”等,其实在语句段(1)中,除了本文开始提到的判

断之外,还进行了其它的检查,当发现是“ollydbg.exe”、“idag.exe”或“w32dsm.exe”时,也关闭当前进程。我因为用的是OllyICE,

所以没有受这个影响,只是当时看到了相关字串,但在写破文的时候,忘了提及,现在想起,补充说明一下。