OJOsoft系列软件的注册算法分析

软件是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


显然是通过专用的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      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
#include
#include
using namespace std;

int main()
{
  char name[32]={0};
  cout<<"Please Enter SoftName:()\n";
  cin.getline(name,31);
  //cout<  
  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      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[i];
      if((tmp %0x1A)==n5)
        break;
    }
    
    code[i]=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<  
  cout<<"Please Enter a Key to Exit…………\n";
  getchar();
  getchar();
  
  return 0;
}

“OJOsoft系列软件的注册算法分析”的一个回复

发表评论