目标软件:OJOsoft系列
语言:VC
工具:OD

软件是VC写的,用PEID显示为:Microsoft Visual C++ 7.0 Method2 [调试]
二话不说,直接到OD载入,然后输入假的激活码,很快就能找到关键点:

代码:
00401D6D     .  C74424 1C 00000>mov dword ptr ss:[esp+1C],0
00401D75     .  E8 96E80100     call <jmp.&MFC71.#3761>
00401D7A     .  51              push ecx
00401D7B     .  8D5424 08       lea edx,dword ptr ss:[esp+8]
00401D7F     .  8BCC            mov ecx,esp
00401D81     .  896424 10       mov dword ptr ss:[esp+10],esp
00401D85     .  52              push edx
00401D86     .  FF15 E4514200   call dword ptr ds:[<&MFC71.#297>]         ;  MFC71.7C14E575
00401D8C     .  8B8E C8000000   mov ecx,dword ptr ds:[esi+C8]
00401D92     .  FF15 30504200   call dword ptr ds:[<&Control.AVProxy::Reg>;  Control.AVProxy::RegisteProduct
[separator]
显然是通过专用的DLL来验证注册的。
跟进后,来到这里:
代码:
003A6C49      C64424 4C 01      mov byte ptr ss:[esp+4C],1
003A6C4E      FF15 94803B00     call dword ptr ds:[<&MFC71.#297>]         ; MFC71.7C14E575
003A6C54      8D4C24 0C         lea ecx,dword ptr ss:[esp+C]
003A6C58      E8 03DB0000       call Control.003B4760                   //这里同样为关键CALL,跟进继续分析
003A6C5D      8BF0              mov esi,eax
003A6C5F      85F6              test esi,esi
003A6C61      75 76             jnz short Control.003A6CD9
003A6C63      8D4C24 10         lea ecx,dword ptr ss:[esp+10]
继续跟进
代码:
003B485D      8BCE              mov ecx,esi
003B485F      885C24 3C         mov byte ptr ss:[esp+3C],bl
003B4863      E8 38F5FFFF       call Control.003B3DA0
003B4868      0FB6C0            movzx eax,al
003B486B      85C0              test eax,eax
003B486D      74 69             je short Control.003B48D8
继续跟进
代码:
003B3DE7      8BCE              mov ecx,esi
003B3DE9      E8 12FAFFFF       call Control.003B3800
003B3DEE      84C0              test al,al
003B3DF0      0F84 49010000     je Control.003B3F3F
跟进CALL后,发现这里是对激活码的格式的一段验证:
代码:
003B3837      2BC2              sub eax,edx
003B3839      83F8 18           cmp eax,18             //比较激活码的长度是否为0x18,不是则失败
…………………………
003B3850     >  56              push esi
003B3851     . |8D4C24 20       lea ecx,dword ptr ss:[esp+20]
003B3855     . |FF15 20813B00   call dword ptr ds:[<&MFC71.#865>]              ;  MFC71.7C1894E7
003B385B     . |0FBEC0          movsx eax,al
003B385E     . |50              push eax
003B385F     . |FFD3            call ebx
003B3861     . |83C4 04         add esp,4
003B3864     . |83FF 04         cmp edi,4
003B3867     . |75 08           jnz short Control.003B3871
003B3869     . |3C 2D           cmp al,2D
003B386B     . |75 33           jnz short Control.003B38A0
003B386D     . |33FF            xor edi,edi
003B386F     . |EB 09           jmp short Control.003B387A
003B3871     > |3C 41           cmp al,41
003B3873     . |7C 2B           jl short Control.003B38A0
003B3875     . |3C 5A           cmp al,5A
003B3877     . |7F 27           jg short Control.003B38A0
003B3879     . |47              inc edi
003B387A     > |46              inc esi
003B387B     . |83FE 18         cmp esi,18
003B387E     .^\7C D0           jl short Control.003B3850
难过上面一段,我们知道了激活的格式应该为:XXXX-XXXX-XXXX-XXXX-XXXX
而且应该是全为字母,不能为数字!
好了,我们重新输入来过,路跳过上面的分析,来到:
代码:
003B3960    |> /8D4424 14       /lea eax,dword ptr ss:[esp+14]
003B3964    |. |33ED            |xor ebp,ebp
003B3966    |. |8D50 01         |lea edx,dword ptr ds:[eax+1]
003B3969    |. |8DA424 00000000 |lea esp,dword ptr ss:[esp]
003B3970    |> |8A08            |/mov cl,byte ptr ds:[eax]
003B3972    |. |40              ||inc eax
003B3973    |. |84C9            ||test cl,cl
003B3975    |.^|75 F9           |\jnz short Control.003B3970
003B3977    |. |2BC2            |sub eax,edx                       ;  取软件名长度-EBX
003B3979    |. |8BF8            |mov edi,eax
003B397B    |. |33C9            |xor ecx,ecx
003B397D    |. |85FF            |test edi,edi
003B397F    |. |7E 18           |jle short Control.003B3999
003B3981    |> |0FBE740C 14     |/movsx esi,byte ptr ss:[esp+ecx+1>
003B3986    |. |8BC3            ||mov eax,ebx
003B3988    |. |0FAFC6          ||imul eax,esi
003B398B    |. |99              ||cdq
003B398C    |. |2BC2            ||sub eax,edx
003B398E    |. |D1F8            ||sar eax,1
003B3990    |. |03C6            ||add eax,esi
003B3992    |. |03E8            ||add ebp,eax
003B3994    |. |41              ||inc ecx
003B3995    |. |3BCF            ||cmp ecx,edi
003B3997    |.^|7C E8           |\jl short Control.003B3981
003B3999    |> |8BC5            |mov eax,ebp
003B399B    |. |99              |cdq
003B399C    |. |B9 1A000000     |mov ecx,1A
003B39A1    |. |F7F9            |idiv ecx
003B39A3    |. |8B4424 10       |mov eax,dword ptr ss:[esp+10]     ;  除以常量0x1A
003B39A7    |. |83C3 06         |add ebx,6                         ;  EB+=6
003B39AA    |. |83C0 04         |add eax,4
003B39AD    |. |83FB 19         |cmp ebx,19                        ;  与0x19比较,大于则结束循环
003B39B0    |. |894424 10       |mov dword ptr ss:[esp+10],eax
003B39B4    |. |8950 FC         |mov dword ptr ds:[eax-4],edx      ;  将上面的相除所得余数保存到下来
003B39B7    |.^\7C A7           \jl short Control.003B3960
这段代码很重要,主要是针对程序名进行处理,得到4个整数,保存下来后,将参与注册验证!
这里将之记为modNum[4],代码的流程用C++转述如下(注:name记为软件名):
代码:
int modNum[4]={0};
        int k=0;
        for(int i=1;i<0x19;i+=6){
                int len=strlen(name);
                int sum=0;
                for(int j=0;j<len;j++){
                        int tmpSum=name[j]*i;
                        tmpSum=(tmpSum>>1);
                        sum=sum+tmpSum+name[j];
                }
                modNum[k]=sum % 0x1A;
                k++;
        }
好了,我们继续单步,来到这里:
代码:
003B3E02    |> /83FF 04         /cmp edi,4
003B3E05    |. |75 04           |jnz short Control.003B3E0B
003B3E07    |. |33FF            |xor edi,edi
003B3E09    |. |EB 23           |jmp short Control.003B3E2E
003B3E0B    |> |53              |push ebx
003B3E0C    |. |8D8C24 E0000000 |lea ecx,dword ptr ss:[esp+E0]
003B3E13    |. |FF15 20813B00   |call dword ptr ds:[<&MFC71.#865>] ;  MFC71.7C1894E7
003B3E19    |. |0FBEC8          |movsx ecx,al
003B3E1C    |. |51              |push ecx
003B3E1D    |. |FFD5            |call ebp
003B3E1F    |. |0FBED0          |movsx edx,al
003B3E22    |. |83EA 41         |sub edx,41
003B3E25    |. |83C4 04         |add esp,4
003B3E28    |. |8916            |mov dword ptr ds:[esi],edx
003B3E2A    |. |47              |inc edi
003B3E2B    |. |83C6 04         |add esi,4
003B3E2E    |> |43              |inc ebx
003B3E2F    |. |83FB 18         |cmp ebx,18
003B3E32    |.^\7C CE           \jl short Control.003B3E02
上面是对用户输入的假码进行处理,处理的方式是对激活码按位送去0x41(跳过"-")!
然后我们就来到了关键的比较点了:
代码:
003B3F01    |> /8B6C0C 18       /mov ebp,dword ptr ss:[esp+ecx+18]    ;  按位取第1组
003B3F05    |. |8B840C B8000000 |mov eax,dword ptr ss:[esp+ecx+B8]    ;  按位取上面所得余数
003B3F0C    |. |8B7C0C 28       |mov edi,dword ptr ss:[esp+ecx+28]    ;  按位取第2组
003B3F10    |. |8B740C 48       |mov esi,dword ptr ss:[esp+ecx+48]    ;  按位取第4组
003B3F14    |. |8B540C 38       |mov edx,dword ptr ss:[esp+ecx+38]    ;  按位取第3组
003B3F18    |. |03C5            |add eax,ebp
003B3F1A    |. |03C7            |add eax,edi
003B3F1C    |. |03C6            |add eax,esi
003B3F1E    |. |03C2            |add eax,edx
003B3F20    |. |99              |cdq
003B3F21    |. |BE 1A000000     |mov esi,1A
003B3F26    |. |F7FE            |idiv esi
003B3F28    |. |3B940C A8000000 |cmp edx,dword ptr ss:[esp+ecx+A8]    ;  按位与第5组相比
003B3F2F    |. |89540C 58       |mov dword ptr ss:[esp+ecx+58],edx
003B3F33    |. |75 0A           |jnz short Control.003B3F3F
003B3F35    |. |83C1 04         |add ecx,4
003B3F38    |. |83F9 10         |cmp ecx,10
003B3F3B    |.^\7C C4           \jl short Control.003B3F01
003B3F3D    |.  EB 02           jmp short Control.003B3F41
003B3F3F    |>  32DB            xor bl,bl
003B3F41    |>  8D8C24 D8000000 lea ecx,dword ptr ss:[esp+D8]
分析好了,我们开始写KeyGen了,完整的代码如下:
代码:
#include<stdio.h>
#include<time.h>
#include<iostream>
using namespace std;

int main()
{
        char name[32]={0};
        cout<<"lease Enter SoftName)\n";
        cin.getline(name,31);
        //cout<<name<<endl;
        
        int modNum[4]={0};
        int k=0;
        for(int i=1;i<0x19;i+=6){
                int len=strlen(name);
                int sum=0;
                for(int j=0;j<len;j++){
                        int tmpSum=name[j]*i;
                        tmpSum=(tmpSum>>1);
                        sum=sum+tmpSum+name[j];
                }
                modNum[k]=sum % 0x1A;
                k++;
        }
        
        srand(time(NULL));
        char code[32]={0};
        for(int i=0;i<4;i++){
                int n1,n2,n3,n4,n5;
                while(1){
                        n1=((double)rand()/(double)RAND_MAX)*0x19+0;
                        n2=((double)rand()/(double)RAND_MAX)*0x19+0;
                        n3=((double)rand()/(double)RAND_MAX)*0x19+0;
                        n4=((double)rand()/(double)RAND_MAX)*0x19+0;
                        n5=((double)rand()/(double)RAND_MAX)*0x19+0;
                        int tmp=n1+n2+n3+n4+modNum;
                        if((tmp %0x1A)==n5)
                                break;
                }
                
                code=0x41+n1;
                code[i+5]=0x41+n2;
                code[i+5*2]=0x41+n3;
                code[i+5*3]=0x41+n4;
                code[i+5*4]=0x41+n5;
        }
        code[4]='-';
        code[9]='-';
        code[14]='-';
        code[19]='-';

        cout<<"Your RegCode is: ";
        cout<<code<<endl;;
        
        cout<<"lease Enter a Key to Exit…………\n";
        getchar();
        getchar();
        
        return 0;
}