【文章标题】: 不问年少cm魔术揭秘
【文章作者】: patapon
【下载地址】: http://bbs.pediy.com/showthread.php?t=108836
【使用工具】: PEID OD
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
帖子之所以取这个题目是因为之前在upk论坛看到偶像hexer大侠的一篇帖子。为了表达对偶像的崇拜之情,于是这次自己写
破文就山寨一回。。。 - -|||||
拿到这个cm,首先PEID查壳,显示为Microsoft Visual C++ 6.0,没有加壳。由于这个cm的验证过程是在explorer进程中进
行的。所以我们先要找到它往进程中写入验证代码的部分。
00401190 /$ 55 push ebp
00401191 |. 8BEC mov ebp, esp
00401193 |. 81EC 3C010000 sub esp, 13C
00401199 |. 53 push ebx
0040119A |. 56 push esi
0040119B |. 57 push edi
0040119C |. 8BD9 mov ebx, ecx
0040119E |. E8 DD030000 call 00401580
004011A3 |. BE 80154000 mov esi, 00401580 ; 入口地址
004011A8 |. 81EE D0134000 sub esi, 004013D0
004011AE |. 56 push esi
004011AF |. 8975 F0 mov dword ptr [ebp-10], esi
004011B2 |. E8 AB090000 call <jmp.&MFC42.#823_operator new>
004011B7 |. 83C4 04 add esp, 4
004011BA |. 8945 F4 mov dword ptr [ebp-C], eax
004011BD |. 89B3 C4000000 mov dword ptr [ebx+C4], esi
004011C3 |. 8B4D F0 mov ecx, dword ptr [ebp-10]
004011C6 |. BE D0134000 mov esi, 004013D0
004011CB |. 8B7D F4 mov edi, dword ptr [ebp-C]
004011CE |. FC cld
004011CF |. F3:A4 rep movs byte ptr es:[edi], byte ptr >
004011D1 |. 6A 00 push 0 ; /ProcessID = 0
004011D3 |. 6A 02 push 2 ; |Flags = TH32CS_SNAPPROCESS
004011D5 |. C785 C4FEFFFF>mov dword ptr [ebp-13C], 128 ; |
004011DF |. E8 6A0C0000 call <jmp.&KERNEL32.CreateToolhelp32Sn>; \CreateToolhelp32Snapshot
004011E4 |. 8D8D C4FEFFFF lea ecx, dword ptr [ebp-13C] ; 创建进程快照
004011EA |. 8945 EC mov dword ptr [ebp-14], eax
004011ED |. 51 push ecx ; /lppe
004011EE |. 50 push eax ; |hSnapshot
004011EF |. E8 540C0000 call <jmp.&KERNEL32.Process32First> ; \Process32First
004011F4 |. 85C0 test eax, eax
004011F6 |. 0F84 9E000000 je 0040129A
004011FC |. 8D95 E8FEFFFF lea edx, dword ptr [ebp-118]
00401202 |. BF 28304000 mov edi, 00403028 ; explorer.exe
00401207 |. 52 push edx ; /StringOrChar
00401208 |. FF15 38224000 call dword ptr [<&USER32.CharUpperA>] ; \CharUpperA
0040120E |. 8BF0 mov esi, eax
00401210 |> 8A0E /mov cl, byte ptr [esi]
00401212 |. 8A17 |mov dl, byte ptr [edi]
00401214 |. 8AC1 |mov al, cl
00401216 |. 3ACA |cmp cl, dl
00401218 |. 75 1E |jnz short 00401238
0040121A |. 84C0 |test al, al
0040121C |. 74 16 |je short 00401234
0040121E |. 8A56 01 |mov dl, byte ptr [esi+1]
00401221 |. 8A4F 01 |mov cl, byte ptr [edi+1]
00401224 |. 8AC2 |mov al, dl
00401226 |. 3AD1 |cmp dl, cl
00401228 |. 75 0E |jnz short 00401238
0040122A |. 83C6 02 |add esi, 2
0040122D |. 83C7 02 |add edi, 2
00401230 |. 84C0 |test al, al
00401232 |.^ 75 DC \jnz short 00401210
00401234 |> 33C0 xor eax, eax
00401236 |. EB 05 jmp short 0040123D
00401238 |> 1BC0 sbb eax, eax
0040123A |. 83D8 FF sbb eax, -1
0040123D |> 85C0 test eax, eax
0040123F |. 74 59 je short 0040129A
00401241 |> 8B4D EC /mov ecx, dword ptr [ebp-14]
00401244 |. 8D85 C4FEFFFF |lea eax, dword ptr [ebp-13C]
0040124A |. 50 |push eax ; /lppe
0040124B |. 51 |push ecx ; |hSnapshot
0040124C |. E8 F10B0000 |call <jmp.&KERNEL32.Process32Next> ; \Process32Next
00401251 |. 85C0 |test eax, eax ; 继续查找进程
00401253 |. 74 45 |je short 0040129A
00401255 |. 8D95 E8FEFFFF |lea edx, dword ptr [ebp-118]
0040125B |. BF 28304000 |mov edi, 00403028 ; explorer.exe
00401260 |. 52 |push edx ; /StringOrChar
00401261 |. FF15 38224000 |call dword ptr [<&USER32.CharUpperA>] ; \CharUpperA
00401267 |. 8BF0 |mov esi, eax
00401269 |> 8A0E |/mov cl, byte ptr [esi]
0040126B |. 8A17 ||mov dl, byte ptr [edi]
0040126D |. 8AC1 ||mov al, cl
0040126F |. 3ACA ||cmp cl, dl
00401271 |. 75 1E ||jnz short 00401291
00401273 |. 84C0 ||test al, al
00401275 |. 74 16 ||je short 0040128D
00401277 |. 8A56 01 ||mov dl, byte ptr [esi+1]
0040127A |. 8A4F 01 ||mov cl, byte ptr [edi+1]
0040127D |. 8AC2 ||mov al, dl
0040127F |. 3AD1 ||cmp dl, cl
00401281 |. 75 0E ||jnz short 00401291
00401283 |. 83C6 02 ||add esi, 2
00401286 |. 83C7 02 ||add edi, 2
00401289 |. 84C0 ||test al, al
0040128B |.^ 75 DC |\jnz short 00401269
0040128D |> 33C0 |xor eax, eax
0040128F |. EB 05 |jmp short 00401296
00401291 |> 1BC0 |sbb eax, eax
00401293 |. 83D8 FF |sbb eax, -1
00401296 |> 85C0 |test eax, eax
00401298 |.^ 75 A7 \jnz short 00401241
0040129A |> 8B45 EC mov eax, dword ptr [ebp-14] ; 上面的循环查找explorer.exe这个进程
0040129D |. 50 push eax ; /hObject
0040129E |. FF15 1C204000 call dword ptr [<&KERNEL32.CloseHandle>; \CloseHandle
004012A4 |. 8B8D CCFEFFFF mov ecx, dword ptr [ebp-134]
004012AA |. 51 push ecx ; /ProcessId
004012AB |. 6A 00 push 0 ; |Inheritable = FALSE
004012AD |. 68 FF0F1F00 push 1F0FFF ; |Access = PROCESS_ALL_ACCESS
004012B2 |. FF15 18204000 call dword ptr [<&KERNEL32.OpenProcess>; \OpenProcess
004012B8 |. 8B7D F0 mov edi, dword ptr [ebp-10] ; 打开进程
004012BB |. 8B35 54204000 mov esi, dword ptr [<&KERNEL32.Virtua>; KERNEL32.VirtualAllocEx
004012C1 |. 6A 40 push 40 ; /flProtect = 40 (64.)
004012C3 |. 68 00100000 push 1000 ; |flAllocationType = 1000 (4096.)
004012C8 |. 57 push edi ; |dwSize
004012C9 |. 6A 00 push 0 ; |lpAddress = NULL
004012CB |. 50 push eax ; |hProcess
004012CC |. 8983 DC000000 mov dword ptr [ebx+DC], eax ; |
004012D2 |. FFD6 call esi ; \VirtualAllocEx
004012D4 |. 8B93 DC000000 mov edx, dword ptr [ebx+DC]
004012DA |. 6A 00 push 0 ; /pBytesWritten = NULL
004012DC |. 57 push edi ; |BytesToWrite
004012DD |. 8B7D F4 mov edi, dword ptr [ebp-C] ; |
004012E0 |. 57 push edi ; |Buffer
004012E1 |. 50 push eax ; |Address
004012E2 |. 52 push edx ; |hProcess
004012E3 |. 8983 CC000000 mov dword ptr [ebx+CC], eax ; |
004012E9 |. FF15 58204000 call dword ptr [<&KERNEL32.WriteProces>; \WriteProcessMemory
走到4012E9这里,我们看看堆栈的内容如下:
0012FD28 00000044 |hProcess = 00000044 (window)
0012FD2C 01700000 |Address = 1700000
0012FD30 003539A0 |Buffer = 003539A0
0012FD34 000001B0 |BytesToWrite = 1B0 (432.)
0012FD38 00000000 \pBytesWritten = NULL
D 003539A0过去看看
003539A0 55 8B EC 83 EC 18 53 56 57 C7 45 FC 00 00 00 00 U嬱冹SVW荅?...
003539B0 C7 45 F8 00 00 00 00 C7 45 F4 00 00 00 00 C7 45 荅?...荅?...荅
003539C0 F0 00 00 00 00 C7 45 E8 00 00 00 00 8B 45 08 89 ?...荅?...婨
003539D0 45 EC 8B 4D EC C7 41 04 00 00 00 00 8B 55 EC 8B E鞁M烨A....婾鞁
003539E0 02 50 6A 00 6A 04 8B 4D EC FF 51 0C 89 45 FC 6A Pj.j婱?Q.塃黬
003539F0 32 6A 00 6A 00 6A 04 8B 55 FC 52 8B 45 EC FF 50 2j.j.j婾黂婨?P
00353A00 10 89 45 E8 8B 4D E8 51 8B 55 EC FF 52 20 89 45 塃鑻M鑁婾?R 塃
00353A10 F0 83 7D F0 09 74 1D 6A 00 6A 00 68 82 04 00 00 饍}?tj.j.h?..
00353A20 8B 45 EC 8B 48 08 51 8B 55 EC FF 52 18 33 C0 E9 婨鞁HQ婾?R3篱
这块就是写入的验证代码,55就是push ebp啦。那我们把它改成CC(int 3),人工造成一个异常。然后设置OD为系统调试器
就可以断下来分析验证代码了。这里修改完以后,让cm跑起来,输入假码123456789,点击play。此时你是不是看到又跳出来
一个OD呢?很神奇啊~~ - -|||||||
算法部分分析如下:
01DC0000 55 push ebp
01DC0001 8BEC mov ebp, esp
01DC0003 83EC 18 sub esp, 18
01DC0006 53 push ebx
01DC0007 56 push esi
01DC0008 57 push edi
01DC0009 C745 FC 0000000>mov dword ptr [ebp-4], 0
01DC0010 C745 F8 0000000>mov dword ptr [ebp-8], 0
01DC0017 C745 F4 0000000>mov dword ptr [ebp-C], 0
01DC001E C745 F0 0000000>mov dword ptr [ebp-10], 0
01DC0025 C745 E8 0000000>mov dword ptr [ebp-18], 0
01DC002C 8B45 08 mov eax, dword ptr [ebp+8]
01DC002F 8945 EC mov dword ptr [ebp-14], eax
01DC0032 8B4D EC mov ecx, dword ptr [ebp-14]
01DC0035 C741 04 0000000>mov dword ptr [ecx+4], 0
01DC003C 8B55 EC mov edx, dword ptr [ebp-14]
01DC003F 8B02 mov eax, dword ptr [edx]
01DC0041 50 push eax
01DC0042 6A 00 push 0
01DC0044 6A 04 push 4
01DC0046 8B4D EC mov ecx, dword ptr [ebp-14]
01DC0049 FF51 0C call dword ptr [ecx+C]
01DC004C 8945 FC mov dword ptr [ebp-4], eax
01DC004F 6A 32 push 32
01DC0051 6A 00 push 0
01DC0053 6A 00 push 0
01DC0055 6A 04 push 4
01DC0057 8B55 FC mov edx, dword ptr [ebp-4]
01DC005A 52 push edx
01DC005B 8B45 EC mov eax, dword ptr [ebp-14]
01DC005E FF50 10 call dword ptr [eax+10]
01DC0061 8945 E8 mov dword ptr [ebp-18], eax
01DC0064 8B4D E8 mov ecx, dword ptr [ebp-18]
01DC0067 51 push ecx
01DC0068 8B55 EC mov edx, dword ptr [ebp-14]
01DC006B FF52 20 call dword ptr [edx+20]
01DC006E 8945 F0 mov dword ptr [ebp-10], eax
01DC0071 837D F0 09 cmp dword ptr [ebp-10], 9 ; 注册码长度要有9位
01DC0075 74 1D je short 01DC0094
01DC0077 6A 00 push 0
01DC0079 6A 00 push 0
01DC007B 68 82040000 push 482
01DC0080 8B45 EC mov eax, dword ptr [ebp-14]
01DC0083 8B48 08 mov ecx, dword ptr [eax+8]
01DC0086 51 push ecx
01DC0087 8B55 EC mov edx, dword ptr [ebp-14]
01DC008A FF52 18 call dword ptr [edx+18]
01DC008D 33C0 xor eax, eax
01DC008F E9 0A010000 jmp 01DC019E
01DC0094 C745 F8 0000000>mov dword ptr [ebp-8], 0
01DC009B EB 09 jmp short 01DC00A6
01DC009D 8B45 F8 mov eax, dword ptr [ebp-8]
01DC00A0 83C0 01 add eax, 1
01DC00A3 8945 F8 mov dword ptr [ebp-8], eax
01DC00A6 8B4D F0 mov ecx, dword ptr [ebp-10]
01DC00A9 83E9 01 sub ecx, 1
01DC00AC 394D F8 cmp dword ptr [ebp-8], ecx
01DC00AF 0F83 85000000 jnb 01DC013A
01DC00B5 8B55 E8 mov edx, dword ptr [ebp-18]
01DC00B8 0355 F8 add edx, dword ptr [ebp-8]
01DC00BB 33C0 xor eax, eax
01DC00BD 8A02 mov al, byte ptr [edx]
01DC00BF 83E8 30 sub eax, 30
01DC00C2 85C0 test eax, eax
01DC00C4 75 1D jnz short 01DC00E3 ; 第一位不能为0
01DC00C6 6A 00 push 0
01DC00C8 6A 00 push 0
01DC00CA 68 82040000 push 482
01DC00CF 8B4D EC mov ecx, dword ptr [ebp-14]
01DC00D2 8B51 08 mov edx, dword ptr [ecx+8]
01DC00D5 52 push edx
01DC00D6 8B45 EC mov eax, dword ptr [ebp-14]
01DC00D9 FF50 18 call dword ptr [eax+18]
01DC00DC 33C0 xor eax, eax
01DC00DE E9 BB000000 jmp 01DC019E
01DC00E3 8B4D F8 mov ecx, dword ptr [ebp-8]
01DC00E6 83C1 01 add ecx, 1
01DC00E9 894D F4 mov dword ptr [ebp-C], ecx
01DC00EC EB 09 jmp short 01DC00F7
01DC00EE 8B55 F4 mov edx, dword ptr [ebp-C]
01DC00F1 83C2 01 add edx, 1
01DC00F4 8955 F4 mov dword ptr [ebp-C], edx
01DC00F7 8B45 F4 mov eax, dword ptr [ebp-C]
01DC00FA 3B45 F0 cmp eax, dword ptr [ebp-10]
01DC00FD 73 36 jnb short 01DC0135
01DC00FF 8B4D E8 mov ecx, dword ptr [ebp-18]
01DC0102 034D F8 add ecx, dword ptr [ebp-8]
01DC0105 33D2 xor edx, edx
01DC0107 8A11 mov dl, byte ptr [ecx]
01DC0109 8B45 E8 mov eax, dword ptr [ebp-18]
01DC010C 0345 F4 add eax, dword ptr [ebp-C]
01DC010F 33C9 xor ecx, ecx
01DC0111 8A08 mov cl, byte ptr [eax]
01DC0113 33D1 xor edx, ecx
01DC0115 85D2 test edx, edx ; 这里检查各位数字是否有重复
01DC0117 75 1A jnz short 01DC0133
01DC0119 6A 00 push 0
01DC011B 6A 00 push 0
01DC011D 68 82040000 push 482
01DC0122 8B55 EC mov edx, dword ptr [ebp-14]
01DC0125 8B42 08 mov eax, dword ptr [edx+8]
01DC0128 50 push eax
01DC0129 8B4D EC mov ecx, dword ptr [ebp-14]
01DC012C FF51 18 call dword ptr [ecx+18]
01DC012F 33C0 xor eax, eax
01DC0131 EB 6B jmp short 01DC019E
01DC0133 ^ EB B9 jmp short 01DC00EE
01DC0135 ^ E9 63FFFFFF jmp 01DC009D
01DC013A 60 pushad
01DC013B 33FF xor edi, edi
01DC013D 33F6 xor esi, esi
01DC013F 8B4D F0 mov ecx, dword ptr [ebp-10]
01DC0142 8B6D E8 mov ebp, dword ptr [ebp-18]
01DC0145 0FB65D 00 movzx ebx, byte ptr [ebp]
01DC0149 8D5B D0 lea ebx, dword ptr [ebx-30] ; 这里取各位数字
01DC014C 03FB add edi, ebx ; ebx初始为1,edi初始为0
01DC014E 85F6 test esi, esi ; 是否是第一位参加运算
01DC0150 75 0A jnz short 01DC015C
01DC0152 6BFF 0A imul edi, edi, 0A ; 满足条件就乘以10
01DC0155 46 inc esi
01DC0156 45 inc ebp
01DC0157 ^ E2 EC loopd short 01DC0145
01DC0159 61 popad
01DC015A EB 0F jmp short 01DC016B
01DC015C 33D2 xor edx, edx ; 不是第一位跳到这里运算
01DC015E 8BC7 mov eax, edi
01DC0160 8D5E 01 lea ebx, dword ptr [esi+1] ; 除数为数字的长度
01DC0163 F7FB idiv ebx
01DC0165 85D2 test edx, edx ; 检查余数是否为0
01DC0167 75 1C jnz short 01DC0185
01DC0169 ^ EB E7 jmp short 01DC0152
01DC016B 6A 00 push 0
01DC016D 6A 00 push 0
01DC016F 68 78040000 push 478
01DC0174 8B55 EC mov edx, dword ptr [ebp-14]
01DC0177 8B42 08 mov eax, dword ptr [edx+8]
01DC017A 50 push eax
01DC017B 8B4D EC mov ecx, dword ptr [ebp-14]
01DC017E FF51 18 call dword ptr [ecx+18]
01DC0181 33C0 xor eax, eax
01DC0183 EB 19 jmp short 01DC019E
01DC0185 61 popad
01DC0186 6A 00 push 0
01DC0188 6A 00 push 0
01DC018A 68 82040000 push 482
01DC018F 8B55 EC mov edx, dword ptr [ebp-14]
01DC0192 8B42 08 mov eax, dword ptr [edx+8]
01DC0195 50 push eax
01DC0196 8B4D EC mov ecx, dword ptr [ebp-14]
01DC0199 FF51 18 call dword ptr [ecx+18]
01DC019C 33C0 xor eax, eax
01DC019E 5F pop edi
01DC019F 5E pop esi
01DC01A0 5B pop ebx
01DC01A1 8BE5 mov esp, ebp
01DC01A3 5D pop ebp
01DC01A4 C2 0400 retn 4
算法分析下来不是特别难。我们来总结下好了
1.注册码要求是数字,而且是一个9位数
2.数字的各位不能重复
3.取出的位数除以它的长度余数要为0,否则视为不符合条件。比如123456789,取出12,除以长度2,余数为0。1234除以4
余数不为0,则判定验证不通过。。。
最后感谢冬祭大侠给我的一个穷举的代码:
# include <iostream>
# include <algorithm>
# include <string>
using namespace std;
int test (char s[])
{
int i, n = atoi(s) ;
for ( i = 9 ; i > 0 ; i--)
{
if (n % i != 0)
return 0 ;
n /= 10 ;
}
return 1 ;
}
void main ()
{
string a = "123456789" ;
do
{
if (test(a.begin()))
cout << a << endl ;
} while (next_permutation(a.begin(), a.end()));
}
算出来的解为381654729
--------------------------------------------------------------------------------
【经验总结】
总的来说这个cm算法不是很难,主要是验证的方式可能大家之前没有接触过,调试起来会有一点小麻烦。。。 也感谢不问
年少大侠提供这个cm,强烈希望他能够放出这个cm的源码给我们彩笔学习学习。。。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2010年03月16日 11:04:26
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)