启动画面修改工具InnoCustomize注册算法分析

  昨天想换掉Windows的启动画面,找了些资料,发现还要自己做图,又要改boot.ini文件,很是麻烦,没想到找到的这个工具可以轻松的做到这一点,很酷很方便,而且能修改的不止这些...

[Cracker]    : prince

[时间]       : 2005.05.13

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

[E-mail]     : Cracker_prince@163.com

[保护方式]   : 序列号

[限制方式]   : NAG窗口(将近1分钟的NAG,受不了)

[外壳保护]   : ASPack 2.12 -> Alexey Solodovnikov

[编译器/语言]: Borland Delphi 6.0 - 7.0 / Object PASCAL

[下载地址]   : http://www.milsoft.net/main.asp
 
[分析过程]   :

  
  ASPackDie搞定外壳,无须修复,可直接运行,无自校验。省了好多事啊~ 注册情况:有3个输入框,购买人,用户名和注册码,分别填入CZero, prince和87654321,点“确定”按钮,无反应,呵呵,有准备啊!下断点GetWindowTextA试试,还是没反应,没办法,请出DeDe,分析后看到该“确定”按钮的响应代码为0047E1D8,在OD中下断0047E1D8,点“确定”,OK,断下来了:

-------------------------------------------------------------------------------
0047E1D8   /.  55                push ebp                         ;  断在这里
0047E1D9   |.  8BEC              mov ebp,esp
0047E1DB   |.  83C4 F8           add esp,-8
0047E1DE   |.  8955 F8           mov dword ptr ss:[ebp-8],edx
0047E1E1   |.  8945 FC           mov dword ptr ss:[ebp-4],eax
0047E1E4   |.  33D2              xor edx,edx
0047E1E6   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]
0047E1E9   |.  8B80 04030000     mov eax,dword ptr ds:[eax+304]
0047E1EF   |.  8B08              mov ecx,dword ptr ds:[eax]
0047E1F1   |.  FF51 64           call dword ptr ds:[ecx+64]       ;  跟进看看
0047E1F4   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]
0047E1F7   |.  8B80 10030000     mov eax,dword ptr ds:[eax+310]
0047E1FD   |.  C740 0C 01000000  mov dword ptr ds:[eax+C],1
0047E204   |.  59                pop ecx
0047E205   |.  59                pop ecx
0047E206   |.  5D                pop ebp
0047E207   \.  C3                retn

--------------------------------------------------------------------------------
只有0047E1F1这一个CALL,跟进:

--------------------------------------------------------------------------------
00444E0C    .  3A50 58           cmp dl,byte ptr ds:[eax+58]
00444E0F    .  74 11             je short Unpacked.00444E22
00444E11    .  8850 58           mov byte ptr ds:[eax+58],dl
00444E14    .  6A 00             push 0                           ; /Arg1 = 00000000
00444E16    .  33C9              xor ecx,ecx                      ; |
00444E18    .  BA 0CB00000       mov edx,0B00C                    ; |
00444E1D    .  E8 42120000       call Unpacked.00446064           ; \Unpacked.00446064
00444E22    >  C3                retn

--------------------------------------------------------------------------------
00444E1D这个CALL就不要跟进了,进去就是一直在打转,得不到有用信息。看来这个“确定”并没有参与注册码的计算。再试试内存断点,在内存中搜索假码87654321,下内存访问断点,被断下后CTRL+F9执行到返回,OD没有响应,看来可能是在某个线程中,但是点击“确定”按钮却没有被断下,说明在“确定”之前就已经计算比较过了,如果是这样,软件是怎么做到知道用户的输入呢?想到了什么?Timer! 回到DeDe,哈,看到了Timer1Timer和Timer2Timer对应的响应代码:0047E208和0047E2F0,在OD中下这两个断点,这里注意,下完断点之后先将他们关闭,否则你都没有机会回到程序界面去。输入完所有资料之后在OD中开启刚才下的断点,马上就被断下,呵呵Timer开启了。

--------------------------------------------------------------------------------
0047E2F0   /.  55                push ebp
0047E2F1   |.  8BEC              mov ebp,esp
0047E2F3   |.  33C9              xor ecx,ecx
0047E2F5   |.  51                push ecx
0047E2F6   |.  51                push ecx
0047E2F7   |.  51                push ecx
0047E2F8   |.  51                push ecx
0047E2F9   |.  51                push ecx
0047E2FA   |.  51                push ecx
0047E2FB   |.  51                push ecx
0047E2FC   |.  51                push ecx
0047E2FD   |.  8955 F4           mov dword ptr ss:[ebp-C],edx
0047E300   |.  8945 FC           mov dword ptr ss:[ebp-4],eax
0047E303   |.  33C0              xor eax,eax
0047E305   |.  55                push ebp
0047E306   |.  68 EDE34700       push Unpacked.0047E3ED
0047E30B   |.  64:FF30           push dword ptr fs:[eax]
0047E30E   |.  64:8920           mov dword ptr fs:[eax],esp
0047E311   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]
0047E314   |.  8B80 10030000     mov eax,dword ptr ds:[eax+310]
0047E31A   |.  8378 0C 00        cmp dword ptr ds:[eax+C],0
0047E31E   |.  0F84 AE000000     je Unpacked.0047E3D2             ;  这里一定不能跳,手动修改跳转标志
0047E324   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]     ;  下面就是判断比较过程,
0047E327   |.  8B80 10030000     mov eax,dword ptr ds:[eax+310]
0047E32D   |.  33D2              xor edx,edx
0047E32F   |.  8950 0C           mov dword ptr ds:[eax+C],edx
0047E332   |.  8D55 F0           lea edx,dword ptr ss:[ebp-10]
0047E335   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]
0047E338   |.  8B80 34030000     mov eax,dword ptr ds:[eax+334]
0047E33E   |.  E8 6D6BFCFF       call Unpacked.00444EB0           ;  取假码87654321
0047E343   |.  8B45 F0           mov eax,dword ptr ss:[ebp-10]    ;  EAX=假码87654321
0047E346   |.  50                push eax
0047E347   |.  8D55 EC           lea edx,dword ptr ss:[ebp-14]
0047E34A   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]
0047E34D   |.  8B80 00030000     mov eax,dword ptr ds:[eax+300]
0047E353   |.  E8 586BFCFF       call Unpacked.00444EB0           ;  取用户名prince
0047E358   |.  8B45 EC           mov eax,dword ptr ss:[ebp-14]    ;  EAX=用户名prince
0047E35B   |.  5A                pop edx
0047E35C   |.  E8 FF200000       call Unpacked.00480460           ;  关键函数,跟进
0047E361   |.  8845 FB           mov byte ptr ss:[ebp-5],al
0047E364   |.  807D FB 00        cmp byte ptr ss:[ebp-5],0
0047E368   |.  74 47             je short Unpacked.0047E3B1
0047E36A   |.  8D55 E8           lea edx,dword ptr ss:[ebp-18]
0047E36D   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]
0047E370   |.  8B80 34030000     mov eax,dword ptr ds:[eax+334]
0047E376   |.  E8 356BFCFF       call Unpacked.00444EB0
0047E37B   |.  8B45 E8           mov eax,dword ptr ss:[ebp-18]
0047E37E   |.  50                push eax
0047E37F   |.  8D55 E4           lea edx,dword ptr ss:[ebp-1C]
0047E382   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]

--------------------------------------------------------------------------------
0047E35C处跟进:

--------------------------------------------------------------------------------
00480460   /$  55                push ebp
00480461   |.  8BEC              mov ebp,esp
00480463   |.  B9 07000000       mov ecx,7
00480468   |>  6A 00             /push 0
0048046A   |.  6A 00             |push 0
0048046C   |.  49                |dec ecx
0048046D   |.^ 75 F9             \jnz short Unpacked.00480468
0048046F   |.  8955 F8           mov dword ptr ss:[ebp-8],edx     ;  假码写入堆栈
00480472   |.  8945 FC           mov dword ptr ss:[ebp-4],eax     ;  用户名写入堆栈
00480475   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]     ;  EAX=用户名prince
00480478   |.  E8 B347F8FF       call Unpacked.00404C30           ;  检测用户名是否为空
0048047D   |.  8B45 F8           mov eax,dword ptr ss:[ebp-8]     ;  取假码87654321
00480480   |.  E8 AB47F8FF       call Unpacked.00404C30           ;  仍然是上面那个检测是否为空的函数
00480485   |.  33C0              xor eax,eax
00480487   |.  55                push ebp
00480488   |.  68 0E064800       push Unpacked.0048060E
0048048D   |.  64:FF30           push dword ptr fs:[eax]
00480490   |.  64:8920           mov dword ptr fs:[eax],esp
00480493   |.  8D55 EC           lea edx,dword ptr ss:[ebp-14]
00480496   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]     ;  取用户名prince
00480499   |.  E8 AE84F8FF       call Unpacked.0040894C           ;  小写转大写函数,结果PRINCE
0048049E   |.  8D45 E8           lea eax,dword ptr ss:[ebp-18]
004804A1   |.  E8 DA42F8FF       call Unpacked.00404780           ;  无关函数
004804A6   |.  8B45 EC           mov eax,dword ptr ss:[ebp-14]    ;  取大写后的用户名
004804A9   |.  E8 9245F8FF       call Unpacked.00404A40           ;  取用户名个数
004804AE   |.  85C0              test eax,eax
004804B0   |.  7E 38             jle short Unpacked.004804EA
004804B2   |.  8945 E4           mov dword ptr ss:[ebp-1C],eax    ;  用户名个数写入堆栈
004804B5   |.  C745 F0 01000000  mov dword ptr ss:[ebp-10],1
004804BC   |>  8D4D DC           /lea ecx,dword ptr ss:[ebp-24]
004804BF   |.  8B45 EC           |mov eax,dword ptr ss:[ebp-14]   ;  EAX=大写后的用户名PRINCE
004804C2   |.  8B55 F0           |mov edx,dword ptr ss:[ebp-10]
004804C5   |.  0FB64410 FF       |movzx eax,byte ptr ds:[eax+edx->;  循环取单个字符ASC码
004804CA   |.  83C0 0F           |add eax,0F                      ;  EAX=该字符ASC码+0xF
004804CD   |.  BA 02000000       |mov edx,2                       ;  EDX=2
004804D2   |.  E8 418AF8FF       |call Unpacked.00408F18          ;  根据单个字符计算中间码,跟进
004804D7   |.  8B55 DC           |mov edx,dword ptr ss:[ebp-24]   ;  中间码送EDX
004804DA   |.  8D45 E8           |lea eax,dword ptr ss:[ebp-18]
004804DD   |.  E8 6645F8FF       |call Unpacked.00404A48          ;  未知
004804E2   |.  FF45 F0           |inc dword ptr ss:[ebp-10]
004804E5   |.  FF4D E4           |dec dword ptr ss:[ebp-1C]
004804E8   |.^ 75 D2             \jnz short Unpacked.004804BC     ;  是否取完用户名?
004804EA   |>  8B45 E8           mov eax,dword ptr ss:[ebp-18]    ;  上面循环的计算结果“5F61585D5254”作为中间码1送EAX
004804ED   |.  E8 4E45F8FF       call Unpacked.00404A40           ;  中间码1的个数,个数送EAX
004804F2   |.  A8 01             test al,1                        ;  检测该个数低位是否为0
004804F4   |.  74 0D             je short Unpacked.00480503
004804F6   |.  8D45 E8           lea eax,dword ptr ss:[ebp-18]
004804F9   |.  BA 24064800       mov edx,Unpacked.00480624
004804FE   |.  E8 4545F8FF       call Unpacked.00404A48
00480503   |>  8D45 EC           lea eax,dword ptr ss:[ebp-14]
00480506   |.  E8 7542F8FF       call Unpacked.00404780           ;  无关
0048050B   |.  8B45 E8           mov eax,dword ptr ss:[ebp-18]    ;  取中间码1“5F61585D5254”
0048050E   |.  E8 2D45F8FF       call Unpacked.00404A40           ;  取中间码1的个数,送EAX
00480513   |.  85C0              test eax,eax                     ;  检测是否为空
00480515   |.  7E 53             jle short Unpacked.0048056A
00480517   |.  8945 E4           mov dword ptr ss:[ebp-1C],eax
0048051A   |.  C745 F0 01000000  mov dword ptr ss:[ebp-10],1
00480521   |>  F645 F0 01        /test byte ptr ss:[ebp-10],1
00480525   |.  74 1E             |je short Unpacked.00480545      ;  循环的奇偶次数分开计算
00480527   |.  8D45 D8           |lea eax,dword ptr ss:[ebp-28]   ;  奇数次循环的计算:
0048052A   |.  8B55 E8           |mov edx,dword ptr ss:[ebp-18]   ;  取中间码1“5F61585D5254”
0048052D   |.  8B4D F0           |mov ecx,dword ptr ss:[ebp-10]   ;  ECX=i*2-1(i从1开始)
00480530   |.  8A140A            |mov dl,byte ptr ds:[edx+ecx]    ;  取第EDX+ECX个字符
00480533   |.  E8 3044F8FF       |call Unpacked.00404968
00480538   |.  8B55 D8           |mov edx,dword ptr ss:[ebp-28]
0048053B   |.  8D45 EC           |lea eax,dword ptr ss:[ebp-14]
0048053E   |.  E8 0545F8FF       |call Unpacked.00404A48
00480543   |.  EB 1D             |jmp short Unpacked.00480562
00480545   |>  8D45 D4           |lea eax,dword ptr ss:[ebp-2C]   ;  偶次循环的计算:
00480548   |.  8B55 E8           |mov edx,dword ptr ss:[ebp-18]   ;  取中间码1“5F61585D5254”
0048054B   |.  8B4D F0           |mov ecx,dword ptr ss:[ebp-10]   ;  ECX=i*2(i从1开始)
0048054E   |.  8A540A FE         |mov dl,byte ptr ds:[edx+ecx-2]  ;  取第EDX+ECX-2个字符
00480552   |.  E8 1144F8FF       |call Unpacked.00404968
00480557   |.  8B55 D4           |mov edx,dword ptr ss:[ebp-2C]
0048055A   |.  8D45 EC           |lea eax,dword ptr ss:[ebp-14]
0048055D   |.  E8 E644F8FF       |call Unpacked.00404A48
00480562   |>  FF45 F0           |inc dword ptr ss:[ebp-10]
00480565   |.  FF4D E4           |dec dword ptr ss:[ebp-1C]
00480568   |.^ 75 B7             \jnz short Unpacked.00480521     ;  是否循环完成?
0048056A   |>  8B45 F8           mov eax,dword ptr ss:[ebp-8]     ;  假码
0048056D   |.  8B55 EC           mov edx,dword ptr ss:[ebp-14]    ;  根据中间码1计算出的真码
00480570   |.  E8 1746F8FF       call Unpacked.00404B8C           ;  进行比较了,呵呵
00480575   |.  75 0C             jnz short Unpacked.00480583
00480577   |.  8B45 FC           mov eax,dword ptr ss:[ebp-4]

--------------------------------------------------------------------------------
004804D2处为关键计算,跟进:

--------------------------------------------------------------------------------
00408F18   /$  83FA 20        cmp edx,20
00408F1B   |.  76 02          jbe short Unpacked.00408F1F
00408F1D   |.  31D2           xor edx,edx
00408F1F   |>  56             push esi
00408F20   |.  89E6           mov esi,esp
00408F22   |.  83EC 20        sub esp,20
00408F25   |.  51             push ecx
00408F26   |.  B9 10000000    mov ecx,10                       ;  ECX=10
00408F2B   |.  E8 88FEFFFF    call Unpacked.00408DB8           ;  根据单个字符计算中间码
00408F30   |.  89F2           mov edx,esi                      ;  EDX=刚计算出的中间码
00408F32   |.  58             pop eax
00408F33   |.  E8 38B9FFFF    call Unpacked.00404870           ;  该中间码写入内存
00408F38   |.  83C4 20        add esp,20
00408F3B   |.  5E             pop esi
00408F3C   \.  C3             retn

--------------------------------------------------------------------------------
00408F2B处继续跟进:

--------------------------------------------------------------------------------
00408DB8   /$  08C9           or cl,cl                         ;  CL=10or10==10
00408DBA   |.  75 17          jnz short Unpacked.00408DD3
00408DBC   |.  09C0           or eax,eax
00408DBE   |.  79 0E          jns short Unpacked.00408DCE
00408DC0   |.  F7D8           neg eax
00408DC2   |.  E8 07000000    call Unpacked.00408DCE
00408DC7   |.  B0 2D          mov al,2D
00408DC9   |.  41             inc ecx
00408DCA   |.  4E             dec esi
00408DCB   |.  8806           mov byte ptr ds:[esi],al
00408DCD   |.  C3             retn
00408DCE   |$  B9 0A000000    mov ecx,0A
00408DD3   |>  52             push edx
00408DD4   |.  56             push esi
00408DD5   |>  31D2           /xor edx,edx                     ;  EDX清零
00408DD7   |.  F7F1           |div ecx                         ;  EAX / ECX
00408DD9   |.  4E             |dec esi
00408DDA   |.  80C2 30        |add dl,30                       ;  DL=余数F+30
00408DDD   |.  80FA 3A        |cmp dl,3A                       ;  同3A比较
00408DE0   |.  72 03          |jb short Unpacked.00408DE5      ;  小于则跳
00408DE2   |.  80C2 07        |add dl,7                        ;  不跳则DL=DL+7
00408DE5   |>  8816           |mov byte ptr ds:[esi],dl        ;  作为本次计算结果写入内存
00408DE7   |.  09C0           |or eax,eax
00408DE9   |.^ 75 EA          \jnz short Unpacked.00408DD5     ;  EAX是否为0?
00408DEB   |.  59             pop ecx
00408DEC   |.  5A             pop edx
00408DED   |.  29F1           sub ecx,esi
00408DEF   |.  29CA           sub edx,ecx
00408DF1   |.  76 10          jbe short Unpacked.00408E03
00408DF3   |.  01D1           add ecx,edx
00408DF5   |.  B0 30          mov al,30
00408DF7   |.  29D6           sub esi,edx
00408DF9   |.  EB 03          jmp short Unpacked.00408DFE
00408DFB   |>  880432         /mov byte ptr ds:[edx+esi],al
00408DFE   |>  4A              dec edx
00408DFF   |.^ 75 FA          \jnz short Unpacked.00408DFB
00408E01   |.  8806           mov byte ptr ds:[esi],al
00408E03   \>  C3             retn

--------------------------------------------------------------------------------
很清晰的过程 :-) 现在都清楚了:

  将用户名转换为大写,循环取用户名单个字符加上0xF,除以0x10,余数加0x30,同0x3A比较,小于则直接保存为本次循环的结果,大于则加7,作为结果保存;循环2次。将两次计算的结果颠倒保存,再取用户名的下个字符进行同样的计算。所以整个用户名计算出的中间码1的个数就是用户名的个数的2倍。接下来根据这个中间码1计算真正的注册码:将该串所有的奇数位和偶数位调换,结果就是真正的注册码了。看到这里,发现什么了吗?对,实际上在第1次计算中间码1的时候,不进行单个字符的计算结果调换,直接就是真码了,后面的操作完全是兜了个圈子,没有用的,呵呵,看程序吧,注册机源码(C语言):

--------------------------------------------------------------------------------
/////////////////////////////////////// 
// program: Keygen for InnoCustomize //
// Author : Coded by prince          //
// Date   : 2005.05.13               //
// E-mail : cracker_prince@163.com   //
///////////////////////////////////////

#include "stdafx.h"
#include <stdio.h>

char chUserName[128] = {0};
char chKey[256]   = {0};

void ConvertToCap( char *pchUserName )
{
  int j = 0;
  while ( pchUserName[j] != '\0' )
  {
    if ( pchUserName[j] >= 0x61 && pchUserName[j] <= 0x7a )
    {
      pchUserName[j] -= 0x20;
    }
    j++;
  }
}

int main(int argc, char* argv[])
{
  printf( "Please input your name:\n" );
  scanf( "%s", chUserName );

  ConvertToCap( chUserName );
  int  i          = 0;
  int  j          = 0;
  int  nRemainder = 0;
  int  nQuotient  = 1;
  while ( chUserName[i] != '\0' )
  {
    chUserName[i] += 0xf;
    nQuotient = chUserName[i];
    while ( nQuotient != 0 )
    {
      nRemainder = nQuotient % 0x10;
      nQuotient  = nQuotient / 0x10;
      nRemainder += 0x30;
      if ( nRemainder >= 0x3a )
      {
        nRemainder += 7;
      }
      chKey[j] = nRemainder;
      j++;
    }
    i++;
  }

  printf( "Your key is: %s\n", chKey );
  return 0;
}

----------------------------------------------------------------------------------------
  以上程序在VC6.0+Win2K下测试通过,任何问题可至cracker_prince@163.com
  菜鸟写菜文~

                                                                    prince 2005.05.13