【破文标题】WinImage V 8.0.8000 序列号算法分析
【破文作者】Fahrenheit
【作者邮箱】fahrenheit871116@163.com
【破解工具】PEiD,,OllyDebugger
【破解平台】Windows XP 32-BIT EDTION
【软件名称】WinImage V 8.0.8000
【保护方式】注册码
【破解声明】请各位指教!!!@_@
-------------------------------------------------------------------
目标是 WinImage V 8.0.8000 (English Version)。本打算用爆破法解除软件的时间限制,因为他只有30天的使用期限。
查壳 无壳。Microsoft Visual C++ 7.0编写。ollyDebugger下打开WinImage,直接在GetSystemTime() 和 GetLocalTime()两个函数下断点,中断后,找到获取日期的地方,但比来比去,想想还是解除软件的序列号比较好,完全破解总比暴破来的安全,那就先破解序列号!
输入序列号有出错提示信息,“Wrong Registration Key !”,在参考文本串中搜寻,没有结果,看来问题有点棘手。搜寻数次仍然没有结果,看来老方法是行不通了。突然间想到,这是一个镜像制作软件,大多地方,都是选择输入,很少有文本输入的地方,这样就可以从程序中的文本输入的函数入手。在Windows的API函数表里一查,有GetDlgItemInt()『获得指定输入框整数值』和GetDlgItemText()『获得指定输入框输入字符串』和GetDlgItemTextA()『获得指定输入框输入字符串』 。前者不会有太大用处,所以直接在后两个函数出下了断点。再次输入序列号,果然,拦截成功!输入序列号的信息窗口函数如下:
0044549D /. 55 push ebp
0044549E |. 8BEC mov ebp,esp
004454A0 |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
004454A3 |. 2D 10010000 sub eax,110 ; Switch (cases 110..111)
004454A8 |. 56 push esi
004454A9 |. 0F84 13010000 je winimage.004455C2
004454AF |. 48 dec eax
004454B0 |. 75 2C jnz short winimage.004454DE
004454B2 |. 0FB745 10 movzx eax,word ptr ss:[ebp+10] ; Case 111 of switch 004454A3
004454B6 |. 48 dec eax ; Switch (cases 1..819)
004454B7 |. 74 3C je short winimage.004454F5
004454B9 |. 48 dec eax
004454BA |. 74 29 je short winimage.004454E5
004454BC |. 2D 17080000 sub eax,817
004454C1 |. 75 1B jnz short winimage.004454DE
004454C3 |. 68 54D74700 push winimage.0047D754 ; ASCII "::/registration.html"; Case 819 of switch 004454B6
004454C8 |. 6A 20 push 20
004454CA |. 6A 01 push 1
004454CC |. FF75 08 push dword ptr ss:[ebp+8] ; /hWnd
004454CF |. FF15 10354700 call dword ptr ds:[<&USER32.GetParent>>; \GetParent
004454D5 |. 50 push eax
004454D6 |. E8 189B0000 call winimage.0044EFF3
004454DB |. 83C4 10 add esp,10
004454DE |> 33C0 xor eax,eax ; Default case of switch 004454B6
004454E0 |. E9 16010000 jmp winimage.004455FB
004454E5 |> 6A 00 push 0 ; /Result = 0; Case 2 of switch 004454B6
004454E7 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004454EA |. FF15 38354700 call dword ptr ds:[<&USER32.EndDialog>>; \EndDialog
004454F0 |. E9 03010000 jmp winimage.004455F8
004454F5 |> 8B35 6C354700 mov esi,dword ptr ds:[<&USER32.GetDlgI>; USER32.GetDlgItemTextA; Case 1 of switch 004454B6
004454FB |. 53 push ebx
004454FC |. 57 push edi ; ---> load name and sn
004454FD 1. -----> |. 68 01010000 push 101 ; /Count = 101 (257.)
00445502 |. BF B8DE4800 mov edi,winimage.0048DEB8 ; |ASCII "sadf"
00445507 |. 57 push edi ; |Buffer => winimage.0048DEB8
00445508 |. 68 16080000 push 816 ; |ControlID = 816 (2070.)
0044550D |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
00445510 |. FFD6 call esi ; \GetDlgItemTextA
00445512 2. -----> |. 6A 7F push 7F ; /Count = 7F (127.)
00445514 |. BB 38E24800 mov ebx,winimage.0048E238 ; |ASCII "dffdf"
00445519 |. 53 push ebx ; |Buffer => winimage.0048E238
0044551A |. 68 17080000 push 817 ; |ControlID = 817 (2071.)
0044551F |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
00445522 |. FFD6 call esi ; \GetDlgItemTextA
00445524 |. 57 push edi
00445525 |. BF 70DB4800 mov edi,winimage.0048DB70
0044552A |. 8BC3 mov eax,ebx
0044552C 3. -----> |. E8 2E720000 call winimage.0044C75F ; VERY SIMILAR
00445531 |. 59 pop ecx
00445532 |. 8B0D 70DB4800 mov ecx,dword ptr ds:[48DB70]
00445538 |. 33D2 xor edx,edx
0044553A |. 3BC2 cmp eax,edx
0044553C |. 5F pop edi
0044553D |. A3 F8E54800 mov dword ptr ds:[48E5F8],eax
00445542 |. 5B pop ebx
00445543 |. 74 06 je short winimage.0044554B
00445545 |. 890D D4D74800 mov dword ptr ds:[48D7D4],ecx
0044554B |> 3915 D4D74800 cmp dword ptr ds:[48D7D4],edx
00445551 |. 890D 90DE4800 mov dword ptr ds:[48DE90],ecx
00445557 |. 75 05 jnz short winimage.0044555E
00445559 |. A3 90DE4800 mov dword ptr ds:[48DE90],eax
0044555E |> 33F6 xor esi,esi
00445560 |. 46 inc esi
00445561 |. 3BC2 cmp eax,edx
00445563 4. -----> |. 75 29 jnz short winimage.0044558E ------| ; -----> If Right, Then Jump
00445565 |. 68 10200000 push 2010 |
0044556A |. 68 2D040000 push 42D |
0044556F 5. -----> |. 8935 F0E24800 mov dword ptr ds:[48E2F0],esi |
00445575 |. 8935 88DE4800 mov dword ptr ds:[48DE88],esi |
0044557B |. 8815 38E24800 mov byte ptr ds:[48E238],dl |
00445581 |. 8815 B8DE4800 mov byte ptr ds:[48DEB8],dl |
00445587 |. 68 2B040000 push 42B |
0044558C |. EB 1B jmp short winimage.004455A9 |
0044558E |> 68 40200000 push 2040 <------|
00445593 |. 68 2D040000 push 42D
00445598 |. 8915 F0E24800 mov dword ptr ds:[48E2F0],edx
0044559E |. 8915 88DE4800 mov dword ptr ds:[48DE88],edx
004455A4 |. 68 2A040000 push 42A
004455A9 |> FF75 08 push dword ptr ss:[ebp+8]
004455AC 6. -----> |. E8 6587FEFF call winimage.0042DD16 ; -----> show dialogue
004455B1 |. 83C4 10 add esp,10
004455B4 |. 56 push esi ; /Result
004455B5 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004455B8 |. FF15 38354700 call dword ptr ds:[<&USER32.EndDialog>>; \EndDialog
004455BE |. 8BC6 mov eax,esi
004455C0 |. EB 39 jmp short winimage.004455FB
004455C2 |> FF75 08 push dword ptr ss:[ebp+8] ; Case 110 of switch 004454A3
004455C5 |. E8 4882FEFF call winimage.0042D812
004455CA |. 833D F8E54800 00 cmp dword ptr ds:[48E5F8],0
004455D1 |. 59 pop ecx
004455D2 |. 74 24 je short winimage.004455F8
004455D4 |. 8B35 70354700 mov esi,dword ptr ds:[<&USER32.SetDlgI>; USER32.SetDlgItemTextA
004455DA |. 68 B8DE4800 push winimage.0048DEB8 ; /Text = "sadf"
004455DF |. 68 16080000 push 816 ; |ControlID = 816 (2070.)
004455E4 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004455E7 |. FFD6 call esi ; \SetDlgItemTextA
004455E9 |. 68 38E24800 push winimage.0048E238 ; /Text = "dffdf"
004455EE |. 68 17080000 push 817 ; |ControlID = 817 (2071.)
004455F3 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004455F6 |. FFD6 call esi ; \SetDlgItemTextA
004455F8 |> 33C0 xor eax,eax
004455FA |. 40 inc eax
004455FB |> 5E pop esi
004455FC |. 5D pop ebp
004455FD \. C2 1000 retn 10
这段子程序中有六个值得注意的地方,分别以箭头1-6标注。箭头1和箭头2都是调用GetDlgItemTextA(),分别是读取用户名(name)和注册号(Registration Code)。反复调试之后,发现箭头3处非常可疑,不出预料应该是判断序列号的地方(ye!^_^)。箭头6是一个函数调用,经证明,是信息对话框的显示。箭头4是一个跳转,他出现在信息对话框的显示的前面不远处,应该是决定信息框的内容的,即注册成功或失败。经过我的反复多次调试,我的猜测是正确的。而箭头5就是注入注册失败的信息,“Wrong Registration Key!”。找到大方向之后就要在细节上有所发现了。
进入箭头3的函数里,先是两个函数,测量用户名和注册号的长度,再把用户名和注册号字母转换大写,因此程序是对字母大小写不敏感的!。接着用用户名来生成注册号,这应该是比较传统的序列号生成方法。生成方法如下:
0044C6D7 |> /8BC1 /mov eax,ecx
0044C6D9 |. |6A 0E |push 0E
0044C6DB |. |99 |cdq
0044C6DC |. |5B |pop ebx
0044C6DD |. |F7FB |idiv ebx
0044C6DF |. |85D2 |test edx,edx
0044C6E1 |. |75 03 |jnz short winimage.0044C6E6
0044C6E3 |. |6A 27 |push 27
0044C6E5 |. |5E |pop esi
0044C6E6 |> |8D41 03 |lea eax,dword ptr ds:[ecx+3]
0044C6E9 1.----> |. |0FB61407 |movzx edx,byte ptr ds:[edi+eax] ; MOVE CHARACTER
0044C6ED |. |0FAFD6 |imul edx,esi
0044C6F0 2.----> |. |0155 FC |add dword ptr ss:[ebp-4],edx
0044C6F3 |. |6A 0E |push 0E
0044C6F5 |. |99 |cdq
0044C6F6 |. |5B |pop ebx
0044C6F7 |. |F7FB |idiv ebx
0044C6F9 |. |85D2 |test edx,edx
0044C6FB |. |74 05 |je short winimage.0044C702
0044C6FD |. |8D3476 |lea esi,dword ptr ds:[esi+esi*2]
0044C700 |. |EB 03 |jmp short winimage.0044C705
0044C702 |> |6BF6 07 |imul esi,esi,7
0044C705 |> |41 |inc ecx
0044C706 |. |3B4D F8 |cmp ecx,dword ptr ss:[ebp-8]
0044C709 |.^\7C CC \jl short winimage.0044C6D7
关键在于箭头1和箭头2的地方,箭头1处是把用户名中的每一个字母移动到寄存器,和ESI的值做带符号乘法,再把结果和内存中的一个值相加,经查,该处内存在
Stack ss:[0012E9E4]处,其值为0x0047694C,而且这个值是固定的,是常量!如此循环,就在该内存处,得到了由用户名生成的第一个注册号,我叫他原始序列号。所以这是注册的初始阶段。
注册号生成以后,他只是保存在内存中的十六进制数,程序又把他转化为字符串,今妙之处就在这里,转换过程中,把0x0B和0x08的值对换了一下,即原来为0x0B的地方换成了0x08,原来为0x08的地方换成了0x0B,这可能是程序作者刻意的吧。这难不倒我,在写注册机的时候,我精心写了一个函数,就完成这项工作。主要部分如下:
for ( i=0; i<8; i++, copy>>=4, clear<<=4 )
{
simple = (char)(copy&0x000f);
if ( simple==0x0B )
{
flag = 0x88888888 & clear;
clear ^= 0xffffffff;
SerialNumber &= clear;
SerialNumber |= flag;
clear ^= 0xffffffff;
}
else if ( simple==0x08 )
{
flag = 0xBBBBBBBB & clear;
clear ^= 0xffffffff;
SerialNumber &= clear;
SerialNumber |= flag;
clear ^= 0xffffffff;
}
else;
}
用位操作,完成了数值的互换,得意一下!^_^而原来地程序是这样的:
0044C713 /$ 55 push ebp
0044C714 |. 8BEC mov ebp,esp
0044C716 |. 83EC 10 sub esp,10
0044C719 |. 56 push esi ; | THE RESULT JUST NOW
0044C71A |. FF75 0C push dword ptr ss:[ebp+C] ; /<%lX>
0044C71D |. 8B75 08 mov esi,dword ptr ss:[ebp+8] ; |
0044C720 |. 8D45 F0 lea eax,dword ptr ss:[ebp-10] ; |
0044C723 |. 68 14DB4700 push winimage.0047DB14 ; |Format = "%lX"
0044C728 |. 50 push eax ; |s
0044C729 |. FF15 14354700 call dword ptr ds:[<&USER32.wsprintfA>>; \wsprintfA
0044C72F |. 8A45 F0 mov al,byte ptr ss:[ebp-10]
0044C732 |. 83C4 0C add esp,0C
0044C735 |. 84C0 test al,al
0044C737 |. 74 1D je short winimage.0044C756
0044C739 |. 8D4D F0 lea ecx,dword ptr ss:[ebp-10]
0044C73C |. 2BCE sub ecx,esi
0044C73E |> 3C 38 /cmp al,38
0044C740 |. 75 04 |jnz short winimage.0044C746
0044C742 |. 04 0A |add al,0A
0044C744 |. EB 06 |jmp short winimage.0044C74C
0044C746 |> 3C 42 |cmp al,42
0044C748 |. 75 02 |jnz short winimage.0044C74C
0044C74A |. 04 F6 |add al,0F6
0044C74C |> 8806 |mov byte ptr ds:[esi],al
0044C74E |. 46 |inc esi
0044C74F |. 8A0431 |mov al,byte ptr ds:[ecx+esi]
0044C752 |. 84C0 |test al,al
0044C754 |.^ 75 E8 \jnz short winimage.0044C73E
0044C756 |> 8B45 08 mov eax,dword ptr ss:[ebp+8]
0044C759 |. C606 00 mov byte ptr ds:[esi],0
0044C75C |. 5E pop esi
0044C75D |. C9 leave
0044C75E \. C3 retn
然后就开始序列号的比对,具体代码如下:
00462270 /$ 8B5424 04 mov edx,dword ptr ss:[esp+4]
00462274 |. 8B4C24 08 mov ecx,dword ptr ss:[esp+8]
00462278 |. F7C2 03000000 test edx,3
0046227E |. 75 3C jnz short winimage.004622BC
00462280 |> 8B02 /mov eax,dword ptr ds:[edx]
00462282 |. 3A01 |cmp al,byte ptr ds:[ecx]
00462284 |. 75 2E |jnz short winimage.004622B4
00462286 |. 0AC0 |or al,al
00462288 |. 74 26 |je short winimage.004622B0
0046228A |. 3A61 01 |cmp ah,byte ptr ds:[ecx+1]
0046228D |. 75 25 |jnz short winimage.004622B4
0046228F |. 0AE4 |or ah,ah
00462291 |. 74 1D |je short winimage.004622B0
00462293 |. C1E8 10 |shr eax,10
00462296 |. 3A41 02 |cmp al,byte ptr ds:[ecx+2]
00462299 |. 75 19 |jnz short winimage.004622B4
0046229B |. 0AC0 |or al,al
0046229D |. 74 11 |je short winimage.004622B0
0046229F |. 3A61 03 |cmp ah,byte ptr ds:[ecx+3]
004622A2 |. 75 10 |jnz short winimage.004622B4
004622A4 |. 83C1 04 |add ecx,4
004622A7 |. 83C2 04 |add edx,4
004622AA |. 0AE4 |or ah,ah
004622AC |.^ 75 D2 \jnz short winimage.00462280
004622AE |. 8BFF mov edi,edi
004622B0 |> 33C0 xor eax,eax
004622B2 |. C3 retn
004622B3 | 90 nop
004622B4 |> 1BC0 sbb eax,eax
004622B6 |. D1E0 shl eax,1
004622B8 |. 83C0 01 add eax,1
004622BB |. C3 retn
004622BC |> F7C2 01000000 test edx,1
004622C2 |. 74 18 je short winimage.004622DC
004622C4 |. 8A02 mov al,byte ptr ds:[edx]
004622C6 |. 83C2 01 add edx,1
004622C9 |. 3A01 cmp al,byte ptr ds:[ecx]
004622CB |.^ 75 E7 jnz short winimage.004622B4
004622CD |. 83C1 01 add ecx,1
004622D0 |. 0AC0 or al,al
004622D2 |.^ 74 DC je short winimage.004622B0
004622D4 |. F7C2 02000000 test edx,2
004622DA |.^ 74 A4 je short winimage.00462280
004622DC |> 66:8B02 mov ax,word ptr ds:[edx]
004622DF |. 83C2 02 add edx,2
004622E2 |. 3A01 cmp al,byte ptr ds:[ecx]
004622E4 |.^ 75 CE jnz short winimage.004622B4
004622E6 |. 0AC0 or al,al
004622E8 |.^ 74 C6 je short winimage.004622B0
004622EA |. 3A61 01 cmp ah,byte ptr ds:[ecx+1]
004622ED |.^ 75 C5 jnz short winimage.004622B4
004622EF |. 0AE4 or ah,ah
004622F1 |.^ 74 BD je short winimage.004622B0
004622F3 |. 83C1 02 add ecx,2
004622F6 \.^ EB 88 jmp short winimage.00462280
比对成功自然进入刚才的跳转语句。不成功,下面进行的一步就有点匪夷所思了。程序由原始序列号又生成了一系列的序列号,并重复刚才的转换成字符串和比对的工作,总共生成了12个序列号,进行了12次比对。生成算法是固定的,都是由原始序列号与某一个数相加得到。这十一个数是 0x14051948,0x17061954,0x10051981,0x04011995,0x02061997,0x12091999,0x16062004,0x21042002,0x13062004,0x09112005和0x24112005,这就是程序员的安排了,我不知该称这是精妙还是漏洞,因为只要有这十二个序列号的任何一个,就可以注册成功。
破解完了,技术上到是不难,但给我留下最深印象的,是改变一下方法,就可以到达问题的本质。试想,如果我不安那样的方法做,可能想在还在爆破之中。有的时候换个想法,会好很多!^_^
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课