拿到cm.exe后要沐浴、斋戒、祭天、做法、看风水、祭出PEiD观察之类的的活动就略过不说了~~~~
直接OD载入,看入口处代码,有msvcrt.__set_app_type之类的API,看来是MFC程序了,直接查找当前模块中的名称,找名称MFC42.#3092_CWnd::GetDlgItem,搜到2个,就是这二个函数获取User name和Key了。下断,输入试炼码:lelfei 14141414后确定,断下了!
代码:
00401CD3 push 3E9 00401CD8 mov ecx, dword ptr [ebp-4] 00401CDB call <jmp.&MFC42.#3092_CWnd::GetDlgItem> 00401CE0 mov ecx, eax 00401CE2 call <jmp.&MFC42.#3874_CWnd::GetWindowTextA> ; SN 00401CE7 mov ecx, dword ptr [ebp-4] 00401CEA add ecx, 60 00401CED call 00401E20 00401CF2 push eax 00401CF3 mov ecx, dword ptr [ebp-4] 00401CF6 add ecx, 60 00401CF9 call <jmp.&MFC42.#2915_CString::GetBuffer> 00401CFE mov dword ptr [ebp-8], eax 00401D01 mov ecx, dword ptr [ebp-4] 00401D04 add ecx, 64 00401D07 push ecx 00401D08 push 3EA 00401D0D mov ecx, dword ptr [ebp-4] 00401D10 call <jmp.&MFC42.#3092_CWnd::GetDlgItem> 00401D15 mov ecx, eax 00401D17 call <jmp.&MFC42.#3874_CWnd::GetWindowTextA> ; UN 00401D39 push edx ; /s 00401D3A call <jmp.&MSVCRT.strlen> ; \len(UN) 00401D3F add esp, 4 00401D42 test eax, eax 00401D44 jnz short 00401D5C ; len(UN)=0? 00401D46 push 0 00401D48 push 0 00401D4A push 004035D0 00401D4F mov ecx, dword ptr [ebp-4] 00401D52 call <jmp.&MFC42.#4224_CWnd::MessageBoxA> 00401D57 jmp 00401E0F 00401D5C mov eax, dword ptr [ebp-8] 00401D5F push eax ; /s 00401D60 call <jmp.&MSVCRT.strlen> ; \len(SN) 00401D65 add esp, 4 00401D68 test eax, eax 00401D6A jnz short 00401D82 ; len(SN)=0? 00401D6C push 0 00401D6E push 0 00401D70 push 004035C0 00401D75 mov ecx, dword ptr [ebp-4] 00401D78 call <jmp.&MFC42.#4224_CWnd::MessageBoxA> 00401D7D jmp 00401E0F 00401D82 mov dword ptr [ebp-10], 0 ; i=0 00401D89 /mov ecx, dword ptr [ebp-C] ; UN 00401D8C |add ecx, dword ptr [ebp-10] 00401D8F |movsx edx, byte ptr [ecx] ; UN(i) 00401D92 |test edx, edx ; UN(i)=0? 00401D94 |je short 00401DBD ; 跳出 00401D96 |mov eax, dword ptr [ebp-C] ; UN 00401D99 |add eax, dword ptr [ebp-10] 00401D9C |movsx ecx, byte ptr [eax] ; UN(i) 00401D9F |cmp ecx, 61 ; "a" 00401DA2 |jle short 00401DBD ; UN(i)>"a" 00401DA4 |mov edx, dword ptr [ebp-C] ; UN 00401DA7 |add edx, dword ptr [ebp-10] 00401DAA |movsx eax, byte ptr [edx] ; UN(i) 00401DAD |cmp eax, 7A ; "z" 00401DB0 |jge short 00401DBD ; UN(i)<"z" 00401DB2 |mov ecx, dword ptr [ebp-10] 00401DB5 |add ecx, 1 00401DB8 |mov dword ptr [ebp-10], ecx 00401DBB \jmp short 00401D89 00401DBD cmp dword ptr [ebp-10], 6 ; 用户名为6位小写字母 00401DFB mov ecx, dword ptr [ebp-8] ; SN 00401DFE push ecx 00401DFF mov edx, dword ptr [ebp-C] ; UN 00401E02 push edx 00401E03 mov eax, dword ptr [ebp-14] 00401E06 push eax 00401E07 call 00401BB0 ; 关键,跟入
用户名必须为6位,我的名字刚好是6位,继续跟踪:
代码:
00401BB0 push ebp 00401BB1 mov ebp, esp 00401BB3 push -1 00401BB5 push 004026F0 ; SE 处理程序安装 00401BBA mov eax, dword ptr fs:[0] 00401BC0 push eax 00401BC1 mov dword ptr fs:[0], esp 00401BC8 push ecx 00401BC9 sub esp, 50 00401BCC push ebx 00401BCD push esi 00401BCE push edi 00401BCF mov dword ptr [ebp-10], esp 00401BD2 mov eax, dword ptr [ebp+8] 00401BD5 sub eax, 10 00401BD8 mov dword ptr [ebp-14], eax 00401BDB mov ecx, dword ptr [ebp-14] 00401BDE mov dword ptr [ecx], 004017F0 ; 设置返回地址入口 00401BE4 mov dword ptr [ebp-18], 0 ; i=0 00401BEB mov dword ptr [ebp-1C], 0 ; j=0 ……花指令 00401BFE cmp dword ptr [ebp-18], 6 ; i>6? 00401C02 jge short 00401C71 ……花指令 00401C10 mov edx, dword ptr [ebp+C] ; UN 00401C13 add edx, dword ptr [ebp-18] 00401C16 movsx eax, byte ptr [edx] ; UN(i) 00401C19 mov ecx, dword ptr [ebp+10] ; SN 00401C1C add ecx, dword ptr [ebp-1C] 00401C1F movsx edx, byte ptr [ecx] ; SN(j) 00401C22 add edx, 1B ; SN(j)+1B 00401C25 cmp eax, edx ; SN(j)+1B=UN(i)? 00401C27 je short 00401C2E ; 不跳则出错 00401C29 call 004017B0 ; MsgBox("继续努力") ……花指令 00401C3A mov eax, dword ptr [ebp+C] ; UN 00401C3D add eax, dword ptr [ebp-18] 00401C40 movsx ecx, byte ptr [eax] ; UN(i) 00401C43 mov edx, dword ptr [ebp+10] ; SN 00401C46 add edx, dword ptr [ebp-1C] 00401C49 movsx eax, byte ptr [edx+1] ; SN(j+1) 00401C4D add eax, 20 ; SN(j+1)+20 00401C50 cmp ecx, eax ; SN(j+1)+20=UN(i)? 00401C52 jle short 00401C5D ; 00401C54 mov ecx, dword ptr [ebp-14] 00401C57 mov dword ptr [ecx], 004017B0 ; 修改返回入口,跳过后面的注册码检测过程 00401C5D mov edx, dword ptr [ebp-1C] 00401C60 add edx, 2 ; j=j+2 00401C63 mov dword ptr [ebp-1C], edx 00401C66 mov eax, dword ptr [ebp-18] 00401C69 add eax, 1 ; i=i+1 00401C6C mov dword ptr [ebp-18], eax 00401C6F jmp short 00401BFE 00401C71 mov dword ptr [ebp-4], 0 ……花指令 00401C84 mov dword ptr [ebp-20], 29C 00401C8B mov ecx, dword ptr [ebp-20] 00401C8E mov byte ptr [ecx], 6 ; 内存访问异常 00401C91 jmp short 00401C9E ; 异常后返回这里 00401C93 call 004019F0 ; 异常后会检测父进程 00401C98 mov eax, 00401C9E 00401C9D retn 00401C9E mov dword ptr [ebp-4], -1 00401CA5 mov ecx, dword ptr [ebp-C] 00401CA8 mov dword ptr fs:[0], ecx 00401CAF pop edi 00401CB0 pop esi 00401CB1 pop ebx 00401CB2 mov esp, ebp 00401CB4 pop ebp 00401CB5 retn ; 返回到前面设置的入口4017F0
执行到这里可以看到注册码与用户名的关系为:
Username每一位ASC值分别减1B和20排列组成Key
整理代码为:
代码:
For i = 1 To Len(UN) j = Asc(Mid$(UN, i, 1)) SN = SN & Chr(j - &H1B) & Chr(j - &H20) Next
代码:
004017F0 push ebp 004017F1 mov ebp, esp 004017F3 sub esp, 80 004017F9 push ebx 004017FA push esi 004017FB push edi 004017FC mov dword ptr [ebp-4], 00403588 ; STR="ABCDEFGHIJKLMNOPQRSTUVWXY" 00401812 mov eax, dword ptr [ebp-4] ; STR 00401815 mov dword ptr [ebp-8], eax ; j=0 00401818 mov dword ptr [ebp-28], 0 ; i=0 0040181F cmp dword ptr [ebp-28], 18 ; do until i=0x18 00401823 je 004018BB ……花指令 00401835 movsx ecx, byte ptr [40416C] ; UN(0) 0040183C mov edx, dword ptr [ebp-4] 0040183F movsx eax, byte ptr [edx] ; STR(j) 00401842 add eax, 20 ; STR(j)+20转换为小写 00401845 cmp ecx, eax 00401847 je short 0040186A ; if STR(j)+20<>UN(0) then---- 00401849 push 0040416C ; /s = "lelfep" 0040184E call <jmp.&MSVCRT.strlen> ; \len(UN) 00401853 add esp, 4 00401856 movsx ecx, byte ptr [eax+40416B] ; UN(len(UN)-1)=UN(5)最后一位 0040185D mov edx, dword ptr [ebp-4] 00401860 movsx eax, byte ptr [edx] ; STR(j) 00401863 add eax, 20 ; STR(j)+20转换为小写 00401866 cmp ecx, eax 00401868 jnz short 00401873 ; if STR(j)+20=UN(5) then---- 0040186A mov ecx, dword ptr [ebp-4] ; 合并成:if STR(j)+20=UN(1) or STR(j)+20=UN(6) then 0040186D add ecx, 1 ; j=j+1 00401870 mov dword ptr [ebp-4], ecx ; end if---- 00401873 mov edx, dword ptr [ebp-28] ; i 00401876 mov eax, dword ptr [ebp-4] 00401879 mov cl, byte ptr [eax] ; STR(j) 0040187B mov byte ptr [ebp+edx-24], cl ; NEWSTR(i)=STR(j) 0040187F mov edx, dword ptr [ebp-28] 00401882 add edx, 1 ; i=i+1 00401885 mov dword ptr [ebp-28], edx 00401888 mov eax, dword ptr [ebp-4] 0040188B add eax, 2 ; j=j+2 0040188E mov dword ptr [ebp-4], eax ……花指令 0040189D mov ecx, dword ptr [ebp-4] 004018A0 movsx edx, byte ptr [ecx] ; STR(j) 004018A3 test edx, edx 004018A5 jnz short 004018B6 ; if STR(j)=0 004018A7 cmp dword ptr [ebp-28], 18 004018AB jge short 004018B6 ; and i<0x18 then 004018AD mov eax, dword ptr [ebp-8] 004018B0 add eax, 1 004018B3 mov dword ptr [ebp-4], eax ; j=1 004018B6 jmp 0040181F ; loop 004018BB push 0 ; /Style = MB_OK|MB_APPLMODAL 004018BD push 00403560 ; |Title = "错了" 004018C2 push 00403554 ; |Text = "继续努力!" 004018C7 movsx ecx, byte ptr [4040F6] ; |SN(A) 004018CE sub ecx, 55 ; |SN(A)-55 004018D1 neg ecx ; | 004018D3 sbb ecx, ecx ; | 004018D5 inc ecx ; | 004018D6 push ecx ; |hOwner,只有在SN(A)=55时才为1,其余时候为0 004018D7 call dword ptr [<&USER32.MessageBoxA>] ; \当hOwner=1时不会弹出错误窗口 004018DD mov dword ptr [ebp-28], eax ; 如果有弹出窗口,这里会有返回值 004018E0 cmp dword ptr [ebp-28], 0 ; 有返回值时: 004018E4 je short 004018FB ; 终止程序 004018E6 call dword ptr [<&KERNEL32.GetCurrentProcess>] ; [GetCurrentProcess 004018EC mov dword ptr [ebp-2C], eax 004018EF push 0 ; /ExitCode = 0 004018F1 mov edx, dword ptr [ebp-2C] ; | 004018F4 push edx ; |hProcess 004018F5 call dword ptr [<&KERNEL32.TerminateProcess>] ; \TerminateProcess
只有当SN(A)=55时,hOwner=1,MessageBoxA就不会显示。即可得出KEY最后一位必须为“p”。
总结这一部分代码为:
代码:
STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" i = 0 j = 0 Do Until i = &H18 k = Asc(Mid$(STR, j + 1, 1)) + &H20 If k = Asc(Mid$(UN, 1, 1)) Or k = Asc(Mid$(UN, 6, 1)) Then j = j + 1 NEWSTR = NEWSTR & Mid$(STR, j + 1, 1) i = i + 1 j = j + 2 If j >= Len(STR) And i < &H18 Then j = 1 Loop
代码:
004018FB mov dword ptr [ebp-28], 0 ; i=0 00401902 mov dword ptr [ebp-30], 5 ; k=5 00401909 mov dword ptr [ebp-34], 0 ; n=0 00401910 cmp dword ptr [ebp-28], 0C ; do until (i=0xC)----大循环开始 00401914 je 004019D5 0040191A mov eax, dword ptr [ebp-30] ; k 0040191D lea ecx, dword ptr [eax*4-4] ; k*4-4 00401924 mov dword ptr [ebp-38], ecx ; m=k*4-4 00401927 mov edx, dword ptr [ebp-28] ; do----小循环开始 0040192A movsx eax, byte ptr [edx+4040EC] ; SN(i) 00401931 mov ecx, dword ptr [ebp-38] ; m 00401934 movsx edx, byte ptr [ebp+ecx-24] ; NEWSTR(m) 00401939 mov ecx, dword ptr [ebp-38] 0040193C add ecx, 1 ; m=m+1 0040193F mov dword ptr [ebp-38], ecx 00401942 cmp eax, edx ; if SN(i)=NEWSTR(m) then 00401944 je short 00401959 ; exit do 00401946 mov edx, dword ptr [ebp-34] 00401949 add edx, 1 ; n=n+1 0040194C mov dword ptr [ebp-34], edx 0040194F cmp dword ptr [ebp-34], 4 00401953 jle short 00401957 ; if n>4 then 00401955 jmp short 00401959 ; exit do 00401957 jmp short 00401927 ; loop----继续小循环 00401959 cmp dword ptr [ebp-34], 5 0040195D jnz short 0040199E ; if n=5 then---- 0040195F mov dword ptr [ebp-3C], 00403578 ; ASCII "ABCDEFGHIJKLMN" 00401966 mov dword ptr [ebp-40], 00403568 ; ASCII "OPQRSTUVWXYZ" 0040196D push eax 0040196E lea eax, dword ptr [ebp-3C] 00401974 push eax 00401975 push dword ptr [ebp-40] 00401978 pop eax 00401979 pop eax 0040197A pop eax 0040197B mov dword ptr [ebp-34], eax 0040197E cmp dword ptr [ebp-34], 0 00401982 jle short 0040198B 00401984 call 004017B0 ; MsgBox("继续努力") 00401989 jmp short 00401999 0040198B push eax 0040198C lea eax, dword ptr [ebp-3C] 00401992 push eax 00401993 push dword ptr [ebp-40] 00401996 pop eax 00401997 pop eax 00401998 pop eax 00401999 call 004017B0 ; MsgBox("继续努力") 0040199E mov eax, dword ptr [ebp-28] ; end if n=5---- 004019A1 add eax, 2 ; i=i+2 004019A4 mov dword ptr [ebp-28], eax 004019A7 mov ecx, dword ptr [ebp-30] 004019AA sub ecx, 1 ; k=k-1 004019AD mov dword ptr [ebp-30], ecx ……花指令 004019BC cmp dword ptr [ebp-30], 0 004019C0 jnz short 004019C9 ; if k=0 then 004019C2 mov dword ptr [ebp-30], 6 ; k=6 004019C9 mov dword ptr [ebp-34], 0 ; n=0 004019D0 jmp 00401910 ; loop====继续大循环 004019D5 call 00401770 ; MsgBox("过关") 004019DA pop edi 004019DB pop esi 004019DC pop ebx 004019DD mov esp, ebp 004019DF pop ebp 004019E0 retn
代码:
i = 0 k = 5 n = 0 Do Until i = 12 m = k * 4 - 4 Do If Mid$(SN, i + 1, 1) = Mid$(NEWSTR, m + 1, 1) Then Exit Do m = m + 1 n = n + 1 If n > 4 Then Exit Do Loop If n = 5 Then MsgBox ("wrong...") Exit Sub End If i = i + 2 k = k - 1 If k = 0 Then k = 6 n = 0 Loop MsgBox ("right!")
代码:
Private Sub txtText1_Change() Dim i As Long, j As Long, k As Long, m As Long, n As Long Dim UN As String, SN As String, STR As String, NEWSTR As String UN = txtText1.Text If Len(UN) <> 6 Then Exit Sub For i = 1 To 6 If Not (Mid$(UN, i, 1) > "a" And Mid$(UN, i, 1) < "z") Then Exit Sub Next If Mid$(UN, 6, 1) <> "p" Then Exit Sub For i = 1 To Len(UN) j = Asc(Mid$(UN, i, 1)) SN = SN & Chr(j - &H1B) & Chr(j - &H20) Next txtText2.Text = SN STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" i = 0 j = 0 Do Until i = &H18 k = Asc(Mid$(STR, j + 1, 1)) + &H20 If k = Asc(Mid$(UN, 1, 1)) Or k = Asc(Mid$(UN, 6, 1)) Then j = j + 1 NEWSTR = NEWSTR & Mid$(STR, j + 1, 1) i = i + 1 j = j + 2 If j >= Len(STR) And i < &H18 Then j = 1 Loop txtText3.Text = NEWSTR Do Until i = 12 m = k * 4 - 4 Do If Mid$(SN, i + 1, 1) = Mid$(NEWSTR, m + 1, 1) Then Exit Do m = m + 1 n = n + 1 If n > 4 Then Exit Do Loop If n = 5 Then MsgBox ("wrong...") Exit Sub End If i = i + 2 k = k - 1 If k = 0 Then k = 6 n = 0 Loop MsgBox ("Right!") End Sub
继续深入思考,NEWSTR的每一位是有规律的,基本上是隔一位取一个大写字母,只有当字母与注册码指定位相同时才会跳过一个字母,因此每一位字母误差在+1左右,越到后面误差越大。
基本字母范围为:
序号:0123456789ABCDEF01234567
字母:ACEGIKMOQSUWYBDFHJMOQSUW----NEWSTR
用户:---bdfhjlnprt--acehjlnpr
第1位范围限定位是0x10--0x14,即用户名范围为c--l,误差范围为b--n
第2位范围限定位是0x0C--0x10,即用户名范围为t--c,误差范围为t--u,b--e
第3位范围限定位是0x08--0x0C,即用户名范围为l--t,误差范围为l--u
第4位范围限定位是0x04--0x08,即用户名范围为d--l,误差范围为d--m
第5位范围限定位是0x00--0x04,即用户名范围为b--d,误差范围为b--e
第6位为固定位p
其中第4,5位误差范围+1,第1,2,3位误差范围+2。
可以用随机选择范围内的字母组合成用户名,然后测试用户名是否合法的方式做KeyGen。
VB写的KeyGen见附件。
给出一组可用码:
gerecp
LGJEWRJEHCUP