【文章标题】: znkkeygenme#1简单分析
【文章作者】: ikki
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
这个keygenMe在crackme.de上已经有solution
http://www.crackmes.de/users/znycuk/znycuks_1_crackme/solutions/red477/browse/solution_ZNKKeygenme%5E1_red477.txt
先试着运行看看,随便输入一个注册码"1234",点那个serial me,keygenMe下面状态栏显示"Bad entry, sorry :-(",失败了?看来运气不是很好呀,呵呵:)
那么用w32dasm看看有没有什么可以利用的信息。本人是菜鸟一只,当然是最喜欢看字符串参考了,如果有个
"Bad entry, sorry :-(",
"God job"
什么的,那就最好了.
字符串参考信息:
--------------
""
""
"button"
"edit"
"Key"
"Menu"
"MessageBoxA"
"Serial me"
"user32.dll"
"U??uj"
"WinClass"
"ZNY"
"Znycuk's #1 KeyGenmE"---------------------keygenMe的标题
"??????临?芮谒?鳞?萘论谇晾??
"煜?死谲??淋茏??"
"? @"
"?, @"
"?P @"
------------
就知道一个keygenMe的标题,下面都是乱码,收获不大,提示字符串应该是做了变换的吧.
那再看看Imported functions吧:
Imported functions:
---------
advapi32.GetUserNameA
comctl32.CreateStatusWindowA
comctl32.InitCommonControls
kernel32.ExitProcess
kernel32.GetCommandLineA
kernel32.GetComputerNameA
kernel32.GetModuleHandleA
kernel32.GetProcAddress
user32.CreateWindowExA
user32.DefWindowProcA
user32.DestroyWindow
user32.DispatchMessageA
user32.GetMessageA
user32.GetWindowTextA
user32.LoadCursorA
user32.LoadIconA
user32.PostQuitMessage
user32.RegisterClassExA
user32.SendMessageA
user32.SetFocus
user32.SetWindowTextA
user32.ShowWindow
user32.TranslateMessage
user32.UpdateWindow
---------------
重点盯上了这个:
user32.GetWindowTextA
--------------
GetWindowTextA引用的地方只有下面一处:
* Reference To: user32.GetMessageA, Ord:0119h
|
:004016CE FF2558204000 Jmp dword ptr [00402058]
* Reference To: user32.GetWindowTextA, Ord:014Ah
|
:004016D4 FF2550204000 Jmp dword ptr [00402050]
上不着村下不着店的,有点奇怪.
Olldbg载入,
bp GetWindowTextA
输入测试注册码"1234",点击Serial Me按钮之后就会断下来,ALT + F9返回用户程序之后来到这里
:
004012BF 68 00020000 push 200
004012C4 68 D8304000 push ZNKKeyge.004030D8 ; ASCII "1234"
004012C9 FF35 CC304000 push dword ptr ds:[4030CC]
004012CF 68 DA124000 push ZNKKeyge.004012DA
004012D4 68 D4164000 push
004012D9 C3 retn
004012DA 8BD8 mov ebx,eax--------------------------------停在这里(EAX=4)
004012DC 81FB FFFF0000 cmp ebx,0FFFF------------------------------没有输入注册码?
004012E2 74 07 je short ZNKKeyge.004012EB
004012E4 BB EC124000 mov ebx,ZNKKeyge.004012EC
004012E9 FFE3 jmp ebx
004012EB |DBE8 fucomi st,st
004012ED 8803 mov byte ptr ds:[ebx],al
004012EF 0000 add byte ptr ds:[eax],al
004012F1 84E4 test ah,ah
004012F3 75 76 jnz short ZNKKeyge.0040136B
004012F5 E8 F1010000 call ZNKKeyge.004014EB
004012FA 83F8 04 cmp eax,4
------------
keygenMe中很多对API函数的调用都是这样处理的:
push param A
push param B
.....
push
retn
xxxxx
其实就是
push param A
push param B
.....
jmp.&user32.GetWindowTextA
xxxx
---------------
结合上面那个ASCII字符串,可以知道,这里是在用GetWindowsTextA读取输入的注册码.
运行到
004012E9 FFE3 jmp ebx
004012EB |DBE8 fucomi st,st
时
EBX=004012EC
而接下来的指令地址却是
004012EB
应该是花指令吧,在
004012EB |DBE8 fucomi st,st
这一行点击右键->汇编,输入汇编指令NOP(注意不要选上汇编对话框中的"使用NOP填充"),调试过程中还会遇到几次这样的情况,处理方法是一样的.
004012E9 /FFE3 jmp ebx ; ZNKKeyge.004012EC
004012EB |90 nop
004012EC E8 88030000 call ZNKKeyge.00401679 ; 注册码格式检查:xxx-xxx-xxx-xxx
004012F1 84E4 test ah,ah
004012F3 /75 76 jnz short ZNKKeyge.0040136B
004012F5 |E8 F1010000 call ZNKKeyge.004014EB ; /用rdtsc指令读取处理器计数器
004012FA |83F8 04 cmp eax,4 ; 通过一定的计算得到一个小于4的数字
004012FD |7C 02 jl short ZNKKeyge.00401301 ; 返回到EAX中用来做后面计算的参数
004012FF ^|EB F4 jmp short ZNKKeyge.004012F5 ; \循环直到得到符合条件的值(<4)
00401301 8BD8 mov ebx,eax ; /
00401303 B1 04 mov cl,4
00401305 F6E1 mul cl ; 1
00401307 03C3 add eax,ebx
00401309 8BB0 D8304000 mov esi,dword ptr ds:[eax+4030D8] ; \
0040130F 53 push ebx
00401310 E8 D8020000 call ZNKKeyge.004015ED ; *重点*
00401315 3BF2 cmp esi,edx ; 第1次结果比较(这次比较是随机选择4段数字之一)
00401317 75 52 jnz short ZNKKeyge.0040136B
00401319 33C9 xor ecx,ecx
0040131B 83F9 04 cmp ecx,4 ; /
0040131E 74 1D je short ZNKKeyge.0040133D
00401320 8BC1 mov eax,ecx
00401322 8BD8 mov ebx,eax
00401324 B2 04 mov dl,4 ; 循环依次比较输入
00401326 F6E2 mul dl
00401328 03C3 add eax,ebx
0040132A 8BB0 D8304000 mov esi,dword ptr ds:[eax+4030D8]
00401330 53 push ebx
00401310 E8 D8020000 call ZNKKeyge.004015ED ; *重点*
00401336 3BF2 cmp esi,edx ; 的注册码的4段数字
00401338 75 31 jnz short ZNKKeyge.0040136B
0040133A 41 inc ecx
0040133B ^ EB DE jmp short ZNKKeyge.0040131B ; \
1处:
此处代码的作用就是根据前面循环得到的随机数(<4),使ESI指向
输入注册码
xxxx-xxxx-xxxx-xxxx
0/5/10/15这4个位置中的某个.
004015ED 51 push ecx
004015EE 56 push esi
004015EF 55 push ebp
004015F0 8BEC mov ebp,esp
004015F2 83C4 F8 add esp,-8 ; 下面的是keygenMe显示的key
004015F5 BB 1D344000 mov ebx,ZNKKeyge.0040341D ; ASCII "1IHW"
004015FA 833B FF cmp dword ptr ds:[ebx],-1
004015FD 74 07 je short ZNKKeyge.00401606
004015FF B8 07164000 mov eax,ZNKKeyge.00401607
00401604 FFE0 jmp eax
00401606 90 nop
00401607 33C0 xor eax,eax
00401609 8B75 10 mov esi,dword ptr ss:[ebp+10] ; 用前面得到的随机数字,取4位KEY中
0040160C 8A041E mov al,byte ptr ds:[esi+ebx] ; 某一位的ASCII码
0040160F 33DB xor ebx,ebx
00401611 50 push eax
00401612 E8 A8FEFFFF call ZNKKeyge.004014BF ; 对A进行计算
00401617 895D FC mov dword ptr ss:[ebp-4],ebx ; 设结果为A1
0040161A E8 B6FEFFFF call ZNKKeyge.004014D5 ; 对A进行计算
0040161F 895D F8 mov dword ptr ss:[ebp-8],ebx ; 设结果为A2
00401622 6A 00 push 0
00401624 68 25344000 push ZNKKeyge.00403425
00401629 FF75 FC push dword ptr ss:[ebp-4]
0040162C FF75 F8 push dword ptr ss:[ebp-8]
0040162F E8 2DFFFFFF call ZNKKeyge.00401561
00401634 A1 25344000 mov eax,dword ptr ds:[403425] ; EAX查表得到的结果
00401639 C1C8 08 ror eax,8
0040163C 83F8 05 cmp eax,5
0040163F 74 08 je short ZNKKeyge.00401649
00401641 50 push eax
00401642 B8 4A164000 mov eax,ZNKKeyge.0040164A
00401647 FFE0 jmp eax
00401649 90 nop
0040164A 58 pop eax
0040164B 3265 F4 xor ah,byte ptr ss:[ebp-C] ; xor ah, A
0040164E 3245 F4 xor al,byte ptr ss:[ebp-C] ; xor al, A
00401651 33D2 xor edx,edx
00401653 66:8BD8 mov bx,ax
00401656 B9 04000000 mov ecx,4
0040165B 24 0F and al,0F ; /
0040165D 3C 09 cmp al,9 ; 把EBX中的16进制数字转换成字符串
0040165F 7E 02 jle short ZNKKeyge.00401663
00401661 04 07 add al,7
00401663 04 30 add al,30
00401665 8AD0 mov dl,al
00401667 C1CA 08 ror edx,8
0040166A 66:C1EB 04 shr bx,4
0040166E 8AC3 mov al,bl
00401670 ^ E2 E9 loopd short ZNKKeyge.0040165B ; \
00401672 83C4 1C add esp,1C
00401675 5D pop ebp
00401676 5E pop esi
00401677 59 pop ecx
00401678 C3 retn
--
004014BF 55 push ebp
004014C0 8BEC mov ebp,esp
004014C2 8A5D 08 mov bl,byte ptr ss:[ebp+8]
004014C5 80E3 F0 and bl,0F0
004014C8 C0C3 04 rol bl,4
004014CB 80FB 03 cmp bl,3
004014CE 74 03 je short ZNKKeyge.004014D3
004014D0 80EB 04 sub bl,4
004014D3 5D pop ebp
004014D4 C3 retn
--
--
004014D5 55 push ebp
004014D6 8BEC mov ebp,esp
004014D8 8A5D 08 mov bl,byte ptr ss:[ebp+8]
004014DB 80E3 0F and bl,0F
004014DE 80FB 07 cmp bl,7
004014E1 77 04 ja short ZNKKeyge.004014E7
004014E3 32DB xor bl,bl
004014E5 EB 02 jmp short ZNKKeyge.004014E9
004014E7 B3 01 mov bl,1
004014E9 5D pop ebp
004014EA C3 retn
--
----
00401561 55 push ebp
00401562 8BEC mov ebp,esp
00401564 B8 02000000 mov eax,2
00401569 F665 08 mul byte ptr ss:[ebp+8] ; MUL A2
0040156C 0245 0C add al,byte ptr ss:[ebp+C] ; ADD A1
0040156F 8B5D 10 mov ebx,dword ptr ss:[ebp+10]
00401572 8A55 14 mov dl,byte ptr ss:[ebp+14]
00401575 84D2 test dl,dl
00401577 74 09 je short ZNKKeyge.00401582
00401579 891C85 FD334000 mov dword ptr ds:[eax*4+4033FD],ebx
00401580 EB 09 jmp short ZNKKeyge.0040158B
00401582 8B3485 FD334000 mov esi,dword ptr ds:[eax*4+4033FD] ; 用EAX查表
00401589 8933 mov dword ptr ds:[ebx],esi
0040158B 5D pop ebp
0040158C C3 retn
---
004033FD C3 D4 DE F1 FF E8 8D FF FF 2B 21 0E FF 17 72 00 迷揆???!?r.
0040340D 08 58 09 71 08 B8 90 03 00 AC 84 B8 00 5C C8 81 X.q?.??\?
注册算法大概就是这样了,最后的一个问题是:这个表里面的数据是从哪里来的?
对04033FD下硬件写入断点,重新运行程序
00401561 55 push ebp
00401562 8BEC mov ebp,esp
00401564 B8 02000000 mov eax,2
00401569 F665 08 mul byte ptr ss:[ebp+8]
0040156C 0245 0C add al,byte ptr ss:[ebp+C]
0040156F 8B5D 10 mov ebx,dword ptr ss:[ebp+10]
00401572 8A55 14 mov dl,byte ptr ss:[ebp+14]
00401575 84D2 test dl,dl
00401577 74 09 je short ZNKKeyge.00401582
00401579 891C85 FD334000 mov dword ptr ds:[eax*4+4033FD],ebx ; 断在这里
00401580 EB 09 jmp short ZNKKeyge.0040158B
00401582 8B3485 FD334000 mov esi,dword ptr ds:[eax*4+4033FD]
00401589 8933 mov dword ptr ds:[ebx],esi
0040158B 5D pop ebp
0040158C C3 retn
一段熟悉的代码,呵呵,执行到返回:
0040158D 55 push ebp
0040158E 8BEC mov ebp,esp
00401590 33C9 xor ecx,ecx
00401592 66:BE 0000 mov si,0
00401596 80F9 02 cmp cl,2 ; /
00401599 74 50 je short ZNKKeyge.004015EB
0040159B 84C9 test cl,cl
0040159D 74 05 je short ZNKKeyge.004015A4
0040159F 8B55 0C mov edx,dword ptr ss:[ebp+C] ; (奇数时EDX的初始值)
004015A2 EB 03 jmp short ZNKKeyge.004015A7
004015A4 8B55 08 mov edx,dword ptr ss:[ebp+8] ; (偶数时EDX的初始值)
004015A7 66:83FE 04 cmp si,4
004015AB 74 37 je short ZNKKeyge.004015E4
004015AD 66:85F6 test si,si
004015B0 74 10 je short ZNKKeyge.004015C2 ; 以si为计算器循环
004015B2 66:83FE 01 cmp si,1
004015B6 74 12 je short ZNKKeyge.004015CA ; 用变换后的EDX的值
004015B8 66:83FE 02 cmp si,2
004015BC 74 10 je short ZNKKeyge.004015CE ; 填充DWORD表格
004015BE D1CA ror edx,1
004015C0 EB 11 jmp short ZNKKeyge.004015D3 ; 先填充偶数位:0/2/4/6
004015C2 81F2 ABADDEAB xor edx,ABDEADAB
004015C8 EB 09 jmp short ZNKKeyge.004015D3 ; 再填充奇数位:1/3/5/7
004015CA F7DA neg edx
004015CC EB 05 jmp short ZNKKeyge.004015D3
004015CE C1E2 03 shl edx,3
004015D1 EB 00 jmp short ZNKKeyge.004015D3
004015D3 6A 01 push 1
004015D5 52 push edx
004015D6 51 push ecx
004015D7 56 push esi
004015D8 E8 84FFFFFF call ZNKKeyge.00401561 ; 填充表格
004015DD 66:46 inc si
004015DF 83C4 10 add esp,10
004015E2 ^ EB C3 jmp short ZNKKeyge.004015A7 ; \
看看这两个初始值怎么来的,执行到返回:
0040122C FF35 E9334000 push dword ptr ds:[4033E9] ; 初始值压栈
00401232 FF35 E8324000 push dword ptr ds:[4032E8] ; 初始值压栈
00401238 E8 50030000 call ZNKKeyge.0040158D
0040123D 6A 01 push 1
0040123F FF75 08 push dword ptr ss:[ebp+8]
对04033E9下内存写入断点,重新运行程序.
断下后返回:
0040141C 68 A2304000 push ZNKKeyge.004030A2
00401421 68 E9334000 push ZNKKeyge.004033E9 ; ASCII "TEST"
00401426 68 31144000 push ZNKKeyge.00401431
0040142B 68 22174000 push ; 这个很明显吧GetComputerName
00401430 C3 retn
00401431 833D A2304000 04 cmp dword ptr ds:[4030A2],4 ; 返回这里
00401438 73 1D jnb short ZNKKeyge.00401457 ; /
0040143A B9 04000000 mov ecx,4 ; 如果ComputerName长度不足4位
0040143F 2B0D A2304000 sub ecx,dword ptr ds:[4030A2] ; 后面依次填充"ZNY",使长度为4位
00401445 BE AA304000 mov esi,ZNKKeyge.004030AA ; ASCII "ZNY"
0040144A BF E9334000 mov edi,ZNKKeyge.004033E9 ; ASCII "TEST"
0040144F 033D A2304000 add edi,dword ptr ds:[4030A2]
00401455 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:>; \
00401457 C3 retn
对04032E8下内存写入断点,重新运行程序.
断下后返回:
004013DF 68 A6304000 push ZNKKeyge.004030A6
004013E4 68 E8324000 push ZNKKeyge.004032E8 ; ASCII "hy"
004013E9 68 F4134000 push ZNKKeyge.004013F4
004013EE 68 34174000 push ; 先GetUserName
004013F3 C3 retn
004013F4 833D A6304000 04 cmp dword ptr ds:[4030A6],4 ; 返回这里
004013FB /73 1E jnb short ZNKKeyge.0040141B ; 如果GetUserName返回的size(包括一个NULL字符)
004013FD |B9 04000000 mov ecx,4 ; 不足4位
00401402 2B0D A6304000 sub ecx,dword ptr ds:[4030A6] ; 后面依次填充"ZNY",使长度为4位
00401408 8D35 AA304000 lea esi,dword ptr ds:[4030AA]
0040140E BF E8324000 mov edi,ZNKKeyge.004032E8 ; ASCII "hy"
00401413 033D A6304000 add edi,dword ptr ds:[4030A6]
00401419 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:>
0040141B C3 retn
整个算法的过程大概就是这样了,说的不清楚的地方请参考注册机代码(c语言).
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年08月07日 20:14:31
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)