【破文标题】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,这就是程序员的安排了,我不知该称这是精妙还是漏洞,因为只要有这十二个序列号的任何一个,就可以注册成功。
      破解完了,技术上到是不难,但给我留下最深印象的,是改变一下方法,就可以到达问题的本质。试想,如果我不安那样的方法做,可能想在还在爆破之中。有的时候换个想法,会好很多!^_^