绿鹰PC万能精灵V3.92注册算法分析(同样适用V3.95)

  先说点废话。^_^ 就是这个软件带我进入Cracker世界,感谢~ 当时由于中了卑鄙小人的木马,病急乱

投医,找到这个软件,结果要注册,呵呵,当时很想搞掉它,可惜功力不够。最近闲来无事,下载了个最新版V

3.92再研究研究,也算是了却我一个心愿吧~ 谁知道噩梦开始了,该软件虽没有用密码学算法(还不如用呢,还

好定位和分析),可是却层层嵌套,费尽心思隐藏代码生成过程,真是不堪回首,不过如果只是想要自己的注

册码的话就不用这么麻烦了,可以利用软件帮我们自己算,如果分析... 以下我只贴出主要代码,细了太长,

我也不想再回头看这段可怕的路了,祝大家好运~ 

[Cracker]    : prince

[时间]       : 2005.07.16

[声明]       : 只做技术交流,不做商业用途,如果你手头宽裕并喜欢这个软件的话请支持正版软件。

[E-mail]     : Cracker_prince@163.com

[软件信息]

[软件说明]   :

  功能判断,可杀未来木马。可实时查杀:各种网络游戏盗号木马(传奇、天堂等)、邮件木马、聊天软

件盗密木马(如OICQ、MSN等)、股票大盗...

[保护方式]   : 序列号 + 次数限制 + 功能限制(只提示,不杀毒,并且不管有没有病毒都提示有可疑文件

) + 反调试 + 自校验

[外壳保护]   : PECompact 2.x -> Jeremy Collake

[编译器/语言]: Microsoft Visual C++ 6.0  / C/C++

[下载地址]   : http://www.onlinedown.net/soft/6396.htm (3.95版)
 
[分析过程]   :
  

  

文件脱壳、自校验和反调试我前几天已经写过,这里不再赘述,可以参考这里:http://bbs.pediy.com/showth

read.php?s=&threadid=14860
  
  软件使用SendMessage获取用户输入的注册码,下断bpx SendMessageA,将我们没有点击注册按钮之前

的所有断点,添好假码8765432133333333(格式后面分析),点确定,断下来:

------------------------------------------------------------------
004248AC   >call dword ptr ds:[<&user32.SendMessageA>] ; \SendMessageA
004248B2   >test eax,eax
004248B4   >mov eax,dword ptr ds:[47B0F0]
004248B9   >jnz UN2_++自.00424AA8
004248BF   >mov dword ptr ss:[esp+94],eax
004248C6   >mov dword ptr ss:[esp+1364],0
004248D1   >mov dword ptr ss:[esp+10],eax
004248D5   >mov dword ptr ss:[esp+11C],eax
004248DC   >lea ecx,dword ptr ss:[esp+94]
004248E3   >mov byte ptr ss:[esp+1364],2
004248EB   >push ecx
004248EC   >push 456
004248F1   >mov ecx,esi
004248F3   >call UN2_++自.0044BE75                      ;  EAX=0012F7C0 “tDF”
004248F8   >mov ecx,eax
004248FA   >call UN2_++自.0044A113                      ;  取用户输入的假码送ECX,EAX=假码个数
004248FF   >mov edx,dword ptr ss:[esp+94]               ;  假码写入内存00130608
00424906   >cmp dword ptr ds:[edx-8],10                 ;  假码个数同0x10比较
0042490A   >je short UN2_++自.0042494F                  ;  不跳就死
0042490C   >mov ecx,esi


------------------------------------------------------------------
主要判断代码:

004100A0  /$  6A>push -1
004100A2  |.  68>push UN2_++自.004591AC                      ;  SE 句柄安装
004100A7  |.  64>mov eax,dword ptr fs:[0]
004100AD  |.  50 push eax
004100AE  |.  64>mov dword ptr fs:[0],esp
004100B5  |.  81>sub esp,814
004100BB  |.  53 push ebx
004100BC  |.  55 push ebp
004100BD  |.  56 push esi
004100BE  |.  57 push edi
004100BF  |.  8B>mov edi,ecx
004100C1  |.  8B>mov eax,dword ptr ss:[esp+834]             ;  取假码的前8位“87654321”送EAX
004100C8  |.  8B>mov ecx,dword ptr ss:[esp+838]             ;  取假码的后8位“33333333”送ECX
004100CF  |.  C7>mov dword ptr ss:[esp+82C],3               ;  3写入[0012DB10]
004100DA  |.  8B>mov edx,dword ptr ds:[eax-8]               ;  EDX=8
004100DD  |.  8B>mov eax,dword ptr ds:[ecx-8]               ;  EAX=8
004100E0  |.  3B>cmp edx,eax
004100E2  |.  0F>jnz UN2_++自.0041033E
004100E8  |.  85>test eax,eax
004100EA  |.  0F>je UN2_++自.0041033E
004100F0  |.  33>xor esi,esi                                ;  ESI清零
004100F2  |.  85>test eax,eax
004100F4  |.  7E>jle short UN2_++自.0041011E
004100F6  |>  8A>/mov cl,byte ptr ds:[esi+ecx]              ;  循环取假码后8位的单个字符
004100F9  |.  88>|mov byte ptr ss:[esp+10],cl               
004100FD  |.  8B>|mov edx,dword ptr ss:[esp+10]             ;  送EDX
00410101  |.  52 |push edx                                  ;  压栈
00410102  |.  8B>|mov ecx,edi
00410104  |.  E8>|call UN2_++自.00410620
00410109  |.  85>|test eax,eax
0041010B  |.  0F>|jl UN2_++自.0041033E
00410111  |.  8B>|mov ecx,dword ptr ss:[esp+838]
00410118  |.  46 |inc esi
00410119  |.  3B>|cmp esi,dword ptr ds:[ecx-8]
0041011C  |.^ 7C>\jl short UN2_++自.004100F6
0041011E  |>  8B>mov eax,dword ptr ss:[esp+834]             ;  取假码的前8位送EAX
00410125  |.  33>xor esi,esi                                ;  ESI清零
00410127  |.  8B>mov ecx,dword ptr ds:[eax-8]               ;  ECX=8
0041012A  |.  85>test ecx,ecx
0041012C  |.  0F>jle UN2_++自.004101F2
00410132  |.  B3>mov bl,4                                   
00410134  |>  8B>/mov ecx,dword ptr ds:[47B0F0]             ;  UN2_++自.0047B104
0041013A  |.  89>|mov dword ptr ss:[esp+14],ecx
0041013E  |.  56 |push esi                                  ;  ESI==0
0041013F  |.  56 |push esi
00410140  |.  8D>|lea ecx,dword ptr ss:[esp+83C]            
00410147  |.  88>|mov byte ptr ss:[esp+834],bl
0041014E  |.  E8>|call UN2_++自.004103B0                    ;  取假码的前8位单个字符
00410153  |.  50 |push eax                                  ;  假码的第1位压栈
00410154  |.  51 |push ecx                                  ;  ECX==0
00410155  |.  8D>|lea edx,dword ptr ss:[esp+848]
0041015C  |.  8B>|mov ecx,esp
0041015E  |.  89>|mov dword ptr ss:[esp+2C],esp
00410162  |.  52 |push edx
00410163  |.  E8>|call UN2_++自.0044C6BF
00410168  |.  8D>|lea eax,dword ptr ss:[esp+1C]             ; |
0041016C  |.  8B>|mov ecx,edi                               ; |
0041016E  |.  50 |push eax                                  ; |Arg1
0041016F  |.  E8>|call UN2_++自.004103C0                    ; \关键,确定后8位字符,跟进
00410174  |.  50 |push eax                                 
00410175  |.  8D>|lea ecx,dword ptr ss:[esp+18]             
00410179  |.  C6>|mov byte ptr ss:[esp+830],5               
00410181  |.  E8>|call UN2_++自.0044CA83
00410186  |.  8D>|lea ecx,dword ptr ss:[esp+10]
0041018A  |.  88>|mov byte ptr ss:[esp+82C],bl
00410191  |.  E8>|call UN2_++自.0044C94A
00410196  |.  8B>|mov ecx,dword ptr ss:[esp+14]
0041019A  |.  8B>|mov eax,dword ptr ss:[esp+838]            ;  取假码的后8位
004101A1  |.  8A>|mov dl,byte ptr ds:[ecx]                  ;  查表
004101A3  |.  8A>|mov cl,byte ptr ds:[esi+eax]              ;  取假码后8位单个字符
004101A6  |.  88>|mov byte ptr ss:[esp+1C],cl               
004101AA  |.  88>|mov byte ptr ss:[esp+18],dl               ;  查表结果写入内存
004101AE  |.  8B>|mov edx,dword ptr ss:[esp+1C]             ;  
004101B2  |.  8B>|mov ecx,edi
004101B4  |.  52 |push edx
004101B5  |.  E8>|call UN2_++自.00410620
004101BA  |.  8B>|mov ebp,eax                               
004101BC  |.  8B>|mov eax,dword ptr ss:[esp+18]             
004101C0  |.  50 |push eax
004101C1  |.  8B>|mov ecx,edi                               
004101C3  |.  E8>|call UN2_++自.00410620
004101C8  |.  3B>|cmp ebp,eax                               ;  判断查表结果和计算结果是否相同
004101CA  |.  C6>|mov byte ptr ss:[esp+82C],3
004101D2  |.  8D>|lea ecx,dword ptr ss:[esp+14]
004101D6  |.  0F>|jnz UN2_++自.00410339                     ;  跳走即失败!!!
004101DC  |.  E8>|call UN2_++自.0044C94A
004101E1  |.  8B>|mov ecx,dword ptr ss:[esp+834]            ;  取假码前8个字符准备下次循环
004101E8  |.  46 |inc esi
004101E9  |.  3B>|cmp esi,dword ptr ds:[ecx-8]
004101EC  |.^ 0F>\jl UN2_++自.00410134
004101F2  |>  8B>mov eax,dword ptr ss:[esp+844]
004101F9  |.  85>test eax,eax
004101FB  |.  0F>jnz UN2_++自.004102DF
00410201  |.  A1>mov eax,dword ptr ds:[47E420]
00410206  |.  8D>lea edx,dword ptr ss:[esp+24]
0041020A  |.  68>push 800                                   ; /Count = 800 (2048.)


------------------------------------------------------------------
  我就只贴出这些了,剩下的所有都可以从这里跟下去,很长,很复杂,而且利用多次指针转移,不容

易跟踪,大家如果有兴趣分析,可以顺藤摸瓜,逆着往回查,可以省点工夫。
  我贴出注册机源码,过程中的浮点操作我不知道如何用高级语言描述,于是大多采用了程序中的汇编

源码,大家可以对照源码查看,基本过程在我的注册机里很清楚。
  另外,我是边跟踪边写注册机,所以变量使用混乱,代码繁杂,也不想再修正了,大家凑合看吧,请

勿深究变量使用及设计方面的问题。
-------------------------------------------------------------------
  以下代码在VC6+2K SP3下测试通过:

/***************************************************
  File name  : Keygen for 绿鹰PC万能精灵3.92(3.95)
  Author    : prince
  Date    : 2005.07.06
****************************************************/

#include <stdafx.h>
#include <stdio.h>
#include <Math.h>
#include <memory.h>
#include <time.h>
#include <stdlib.h>

char  number = 0;
char  chNumber1 = 0;
double  dNumber2 = 0;
double  dNumber3 = 0;
char  chString[] = "EGhost";
char  chChart[5] = {'3', 'b', 'y', 't', 'p'};
char  chValue1[11] = {0};
double  dValue2 = 0;
__int64 nValue3 = 0x40C6248000000000;
double  dValue4 = 0;
double  dCount = 0;
__int64 dSaveFcw = 0;
__int64 dTemp7 = 0;
int    nFlag = 0;

char GeneratePart2Key(char chPart1Key, int nIndex)
{
  nFlag = 0;
  __int64  dTemp1 = 0;
  __int64  dTemp2 = 0;
  __int64  dTemp3 = 0;
  __int64 dTemp4 = 0;
  __int64 dTemp5 = 0;
  __int64 dTemp6 = 0;
  
  dTemp1 = 0x43498ECDC7C69E38;
  dTemp5 = 0x4314723E396BB1C4;
  dTemp6 = 0xC3E8F574F517F684;
  dTemp7 = 0xC0C6248000000000;
  dCount = 0;

  if (0 == chPart1Key)
  {
    printf("damn it! please try again!  :(\n");
    return -1;
  }
  number = chPart1Key;
  __asm
  {
    pushad
    xor eax, eax
    xor ecx, ecx
    mov eax, nIndex
    mov ecx, eax
    shl ecx, 5
    lea eax, dword ptr ss:[ecx + eax + 0xb]
    mov dword ptr ss:[nIndex], eax
    popad
  }
  number ^= nIndex;
  __asm
  {
    push eax
      mov eax, dword ptr ss:[number]
    mov dword ptr ss:[chNumber1], eax
    mov dword ptr ss:[chNumber1 + 7], 0
    pop eax
  }

  __asm
  {
    pushad
    fild qword ptr ds: [chNumber1]
    fsin
    fst st(1)
    fmul qword ptr ds:[dTemp1]
    xor eax, eax
    wait
    fstcw dTemp1
    wait
    mov ax, word ptr ss:[dTemp1]
    or ah, 0xc
    mov word ptr ss:[dTemp2], ax
    fldcw word ptr ss:[dTemp2]
    fistp qword ptr ss:[dTemp3]
    fldcw word ptr ss:[dTemp1]
    mov eax, dword ptr ss:[dTemp3]
    mov edx, dword ptr ss:[dTemp3 + 4]
    test eax, eax
    jl Lable
    fmul qword ptr ds:[dTemp5]
    jmp Lable0
Lable:
    fmul qword ptr ss:[dTemp6]
Lable0:

    fstcw word ptr ss:[dTemp2]
    wait
    mov ax, word ptr ss:[dTemp2]
    or ah, 0xc
    mov word ptr ss:[dTemp3], ax
    fldcw word ptr ss:[dTemp3]
    fistp qword ptr ss:[dTemp4]
    fldcw word ptr ss:[dTemp2]
    mov eax, dword ptr ss:[dTemp4]
    xor edx, edx

    add eax, 0x4fe31c6b
    mov dword ptr ss:[dTemp2], eax
    adc edx, 0x3733e7d0
    mov dword ptr ss:[dTemp4], 0x3733e7d0
    mov dword ptr ss:[dTemp3], 0x12

    xor edi, edi
    mov eax, edx
    mov eax, 0x12
    xor ebx, ebx
    mov ebx, eax
    xor ecx, ecx
    mov ecx, 0x2442ef71
    mov eax, dword ptr ss:[dTemp2]
again1:
    shr ebx, 1
    rcr ecx, 1
    shr edx, 1
    rcr eax, 1
    or ebx, ebx
    jnz again1

    div ecx
    mov ecx, eax
    mul dword ptr ss:[dTemp3]
    xchg eax, ecx
    mov dword ptr ss:[dTemp3], 0x2442ef71
    mul dword ptr ss:[dTemp3]
    add edx, ecx
    cmp edx, dword ptr ss:[dTemp4]
    ja Lable1
    jb Lable2
Lable1:  
    sub eax, 0x2442ef71
    sbb edx, 0x12
Lable2:  
    sub eax, dword ptr ss:[dTemp2]
    sbb edx, 0x3733e7d0
    dec edi
    jns Lable3  
    neg edx
    neg eax
    sbb edx, 0
    mov dword ptr ss:[dNumber2], edx
Lable3:  
    nop
    mov edi, edx
    mov esi, eax
    cmp edi, ebx
    jg Lable5
    jl Lable4
    cmp esi, ebx
    jnb Lable5
Lable4:  
    neg esi
    adc edi, ebx
    neg edi
Lable5:  
    lea ebx, chString
    xor ecx, ecx
    mov ebp, 6
again2:
    movsx eax, byte ptr ds:[ecx + ebx]
    cdq
    add esi, eax
    adc edi, edx
    inc ecx
    cmp ecx, ebp
    jl again2

    xor edi, edi
    mov eax, dword ptr ss:[dNumber2]
    mov eax, 0x12
    mov ebx, eax
    mov ecx, 0x2442ef71
    mov edx, dword ptr ss:[dNumber2]
    mov eax, esi
again3:
    shr ebx, 1
    rcr ecx, 1
    shr edx, 1
    rcr eax, 1
    or ebx, ebx
    jnz again3

    div ecx
    mov ecx, eax
    mov dword ptr ss:[esp - 4], 0x12
    mul dword ptr ss:[esp - 4]
    xchg eax, ecx
    mov dword ptr ss:[esp - 4], 0x2442ef71
    mul dword ptr ss:[esp - 4]
    add edx, ecx
    cmp edx, dword ptr ss:[dNumber2]
    ja Lable6
    jb Lable7
Lable6:
    sub eax, 0x2442ef71
    sbb edx, 0x12
Lable7:
    sub eax, esi
    sbb edx, dword ptr ss:[dNumber2]
    dec edi
    jns Lable8  
    neg edx
    neg eax
    sbb edx, 0
    mov dword ptr ds:[dNumber3], esi  
    xor edx, edx
    cdq
    cmp edx, 0
    je Lable8
    neg eax
    mov dword ptr ds:[dNumber3], eax
    mov dword ptr ds:[nFlag], 1
Lable8:
    nop

    // 开始计算中间字符串
    xor edx, edx
    xor edi, edi
    mov dword ptr ss:[dNumber2], 1
again4:
    mov eax, dword ptr ss:[dNumber2]
    dec dword ptr ss:[dNumber2]
    test eax, eax
    jg Lable9
    mov eax, esi
    or eax, edi
    je EndAgain4
Lable9:
    mov eax, 0xa
    cdq
    // 计算余数
    mov ecx, 0xa
    mov eax, dword ptr ds:[dNumber3]
    div ecx
    mov eax, edx
    xor edx, edx
    mov ebx, eax
    add ebx, 0x30
    // 计算商
    mov eax, dword ptr ds:[dNumber3]
    div ecx
    cmp ebx, 0x39
    mov esi, eax
    mov dword ptr ds:[dNumber3], eax
    mov ecx, dword ptr ds:[dCount]
    mov dword ptr ds:[chValue1 + ecx], ebx
    inc dword ptr ds:[dCount];
    jmp again4

EndAgain4:
    popad
  }

  char chTemp = 0;
  int  i = 0;
  int nStrCount = 0;
  __asm
  {
    pushad
    xor eax, eax
    mov eax, dword ptr ds:[dCount]
    mov dword ptr ss:[nStrCount], eax
    popad
  }
  if (1 == nFlag)
  {
    nStrCount++;
    chValue1[nStrCount - 1] = '-';
  }
  switch (nStrCount)
  {
  case 9:
    {
      for (i = 0; i < 4; i++)
      {
        chTemp = chValue1[i];
        chValue1[i] = chValue1[8 - i];
        chValue1[8 - i] = chTemp;
      }
      break;
    }
  case 10:
    {
      for (i = 0; i < 5; i++)
      {
        chTemp = chValue1[i];
        chValue1[i] = chValue1[9 - i];
        chValue1[9 - i] = chTemp;
      }
      break;
    }
  case 11:
    {
      for (i = 0; i < 11; i++)
      {
        chValue1[10 - i + 1] = chValue1[10 - i];
      }
      chValue1[0] = '-';
      for (i = 1; i < 6; i++)
      {
        chTemp = chValue1[i];
        chValue1[i] = chValue1[11 - i];
        chValue1[11 - i] = chTemp;
      }
      break;
    }
  default:
    {
      break;
    }
  }


  __asm
  {
    pushad
    xor edx, edx
    xor eax, eax
    xor ecx, ecx
    xor esi, esi
    mov esi, dword ptr ss:[nStrCount]
    xor ebp, ebp
    xor ebx, ebx
    lea edx, dword ptr ds:[chValue1]
    mov ebp, 6
    lea ebx, dword ptr ds:[chString]
again5:
    test ecx, ecx
    jnz Lable10
    movsx eax, byte ptr ds:[edx]
    xor eax, 0x16
    jmp Lable11
Lable10:
    movsx ebp, byte ptr ds:[ecx + edx]
    xor ebp, 0x16
    xor eax, ebp
Lable11:
    inc ecx
    cmp ecx, esi
    
    jl again5
    mov dword ptr ds:[dValue2], eax
    fild qword ptr ds:[dValue2]
    fsin
    fld st
    fmul qword ptr ss:[nValue3]
    wait
    fstcw word ptr ds:[dSaveFcw]
    wait
    mov ax, word ptr ds:[dSaveFcw]
    or ah, 0xc
    mov word ptr ds:[dSaveFcw + 8], ax
    fldcw word ptr ds:[dSaveFcw + 8]
    fistp qword ptr ss:[dValue4]
    fldcw word ptr ss:[dSaveFcw]
    mov eax, dword ptr ss:[dValue4]
    mov ecx, eax
    test ecx, ecx
    jl Lable12
    fmul qword ptr ss:[dTemp7]
    wait
    fstcw word ptr ds:[dSaveFcw]
    wait
    mov ax, word ptr ds:[dSaveFcw]
    or ah, 0xc
    mov word ptr ds:[dSaveFcw + 8], ax
    fldcw word ptr ds:[dSaveFcw + 8]
    fistp qword ptr ss:[dValue4]
    fldcw word ptr ss:[dSaveFcw]
    mov eax, dword ptr ss:[dValue4]
    mov ecx, 0xcc7
    sub ecx, eax
    mov eax, ecx
    jmp Lable13
Lable12:
    mov eax, 0xcc7
    fstp st
    sub eax, ecx
Lable13:
    cdq
    mov ecx, 5
    idiv ecx
    mov dword ptr ds:[number], edx

    popad
  }
  return chChart[number];
}

int main(int argc, char* argv[])
{
  int i = 0;
  char chPart2Key[8] = {0};
  char chRandNumber[8] = {0};
  srand( (unsigned)time( NULL ) );

  for (i = 0; i < 8; i++)
  {
    int j = rand() % 3 + 1;
    switch(j)
    {
    case 1:
      {
        chRandNumber[i] = (rand() % 9) + 0x30;
        break;
      }
    case 2:
      {
        chRandNumber[i] = (rand() % 26) + 0x41;
        break;
      }
    case 3:
      {
        chRandNumber[i] = (rand() % 26) + 0x61;
        break;
      }
    default:
      {
        printf("damn it! please try again!  :(\n");
        return -1;
      }
    }
  }

  for (i = 0; i < 8; i++)
  {
    chPart2Key[i] = GeneratePart2Key(chRandNumber[i], i);
  }
  printf("enjoy key: %s\n", chRandNumber/*chTest, chPart2Key*/);
  
  return 0;
}

------------------------------------------------------------------
  就在我写完这篇分析的时候,最新版3.95 released,经过测试,算法没有变,仍然可以使用。
  任何问题可至: Cracker_prince@163.com
  菜鸟写菜文,请大侠们不吝赐教~

                                                                      prince 2005.07.16