还原精灵密码算法分析
作 者: figo
时 间: 2006-05-31 14:47
链 接: [url]http://bbs.pediy.com/showthread.php?threadid=26616[/url]
详细信息:
【文章标题】 : 还原精灵密码算法分析
【调试环境】 : WINXP OllyDbg1.10  PEID 0.94 
【软件名称】 : 还原精灵
【破解目的】 : 研究算法分析
【加密方式】 : 加壳,未知壳
【文章作者】 : figo
【作者声明】 : 只为研究算法分析,没有其他目的. 请勿用与恶意破坏等非法用途,
否则给自己或他人带来严重后果,概与本人无关.失误之处恳请批评指正,
或有更好的方法或者技巧,欢迎互相交流. 版主若觉此帖违规,请删除,勿封我ID.


【破解过程】 :

还原精灵是一款很优秀的软件,它能够记录下一切对硬盘的写操作,
不论您对硬盘进行拷贝还是移动删除甚至是格式化分区等操作,只要一重新启动,
一切都会恢复到这个操作之前的状态,所以被广泛应用在学校机房和网吧等公共场合的
电脑上.  但是装还原精灵的用户如果忘记了密码就成麻烦事了,想卸掉要密码,想删也删不掉.
记得网上有一种能删除还原精灵的软件,是风般的男人(我也不知道他的真名,呵呵)编写的.
我个人是极不赞成用这种方法,因为这软件太 "野蛮" 了,它在取得 RING0 后,直接用硬盘 IO 把
原来的 BOOT 扇区强制写入物理 0 磁道 1 扇区.对 5.5 以上的版本会造成系统崩溃,
因为还原精灵为了与 NT/2000/XP 兼容,会替换一些驱动程序.
希望本文能对那些想卸载还原精灵而又忘记密码的用户带来帮助.
 



还原精灵早期版本密码加密算法很简单,甚至不加密直接明码比较.
5.5 版本加密算法是: 密文 = 密码 XOR A5A5A5A5A5A5A5A5H
6.0 版本的加密算法比较复杂,下面来分析一下

该程序加了壳,是弱壳,用OD 手动脱了
入口点是 004318A4.
下面列出程序一些关键代码片段(用 OD 分析的),至于怎么跟踪得来,由于篇幅关系暂不讨论
本文只讨论密码算法和解密器的编写.



0040160E      .  E8 B9F70200   call    
00401613      .  8D86 18020000 lea     eax, [esi+218]
00401619      .  8BCE          mov     ecx, esi
0040161B      .  50            push    eax                                       
0040161C      .  E8 EF010000   call    00401810        ;  --------> 验证密码子程序
00401621      .  85C0          test    eax, eax
00401623      .  0F85 D6000000 jnz     004016FF        ; ----------> 关键跳转
00401629      .  8D4C24 0C     lea     ecx, [esp+C]
0040162D      .  E8 4CF70200   call    
00401632      .  8D4C24 08     lea     ecx, [esp+8]


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
跟进 CALL 00401810
代码如下:
00401839  |.  E8 A0F50200   call    
0040183E  |.  8BCB          mov     ecx, ebx
00401840  |.  8BF0          mov     esi, eax
00401842  |.  8BC1          mov     eax, ecx
00401844  |.  8D7C24 10     lea     edi, [esp+10]
00401848  |.  C1E9 02       shr     ecx, 2
0040184B  |.  F3:A5         rep     movs dword ptr es:[edi], dword p>
0040184D  |.  8BC8          mov     ecx, eax
0040184F  |.  83E1 03       and     ecx, 3
00401852  |.  F3:A4         rep     movs byte ptr es:[edi], byte ptr>
00401854  |.  8D4C24 10     lea     ecx, [esp+10]
00401858  |.  51            push    ecx
00401859  |.  E8 02490100   call    00416160  

注意这个CALL,入栈的是密码原文地址.
把它步过,看看各个寄存器和密码原文有何变化.
我们可以看到密码原文变成密文.

把这个CALL 置断点,重新来过

跟进这个CALL

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

00416160  /$  8B4424 04     mov     eax, [esp+4]
00416164  |.  6A 20         push    20                 ;  Arg3 = 00000020
00416166  |.  50            push    eax                ;  Arg2 = 密码原文
00416167  |.  68 80254400   push    00442580           ;  Arg1 = 00442580 ASCII "NJYZ-RG-60-TT-60"
0041616C  |.  E8 1FFFFFFF   call    00416090           ;  -----------------------> 转换密文子程序
00416171  |.  83C4 0C       add     esp, 0C
00416174  \.  C3            retn


注意这个CALL 的3个参数
第一个参数是指向一个字符串 "NJYZ-RG-60-TT-60"
第二个参数是指向密码原文
第三个参数是一个常数 00000020H
不难猜出这个CALL 是一个明文转换密文的子程序

跟进去看看,下面这段代码就是密码转换算法



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

00416090  /$  55            push    ebp
00416091  |.  8BEC          mov     ebp, esp
00416093  |.  8B45 10       mov     eax, [ebp+10]
00416096  |.  53            push    ebx
00416097  |.  56            push    esi
00416098  |.  57            push    edi
00416099  |.  85C0          test    eax, eax
0041609B  |.  7E 5D         jle     short 004160FA
0041609D  |.  60            pushad
0041609E  |.  8B75 08       mov     esi, [ebp+8]
004160A1  |.  8B7D 0C       mov     edi, [ebp+C]
004160A4  |.  57            push    edi
004160A5  |.  8B1F          mov     ebx, [edi]
004160A7  |.  8B4F 04       mov     ecx, [edi+4]
004160AA  |.  33C0          xor     eax, eax
004160AC  |.  BA B979379E   mov     edx, 9E3779B9
004160B1  |.  66:BF 2000    mov     di, 20
这时候各个寄存器:
ESI 指向字符串 "NJYZ-RG-60-TT-60"
EDX 常数 9E3779B9    
EBX 密码明文前4个字节
ECX 密码明文后4个字节
DI  常数 0020H





大家应该可以下面的代码看出:
密码密文是把明文和字符串"NJYZ-RG-60-TT-60"和常数 9E3779B9 进行加密
其中密码的前4字节和字符串"NJYZ-RG-"加密
密码的后4字节和字符串"60-TT-60"加密
而且密码的前后4字节在每次加密循环都相互加密



004160B5  |>  03C2          /add     eax, edx    ;--------------->加密循环
004160B7  |.  8BE9          |mov     ebp, ecx
004160B9  |.  C1E5 04       |shl     ebp, 4
004160BC  |.  03DD          |add     ebx, ebp
004160BE  |.  8B2E          |mov     ebp, [esi]
004160C0  |.  33E9          |xor     ebp, ecx
004160C2  |.  03DD          |add     ebx, ebp
004160C4  |.  8BE9          |mov     ebp, ecx
004160C6  |.  C1ED 05       |shr     ebp, 5
004160C9  |.  33E8          |xor     ebp, eax
004160CB  |.  03DD          |add     ebx, ebp
004160CD  |.  035E 04       |add     ebx, [esi+4]

004160D0  |.  8BEB          |mov     ebp, ebx
004160D2  |.  C1E5 04       |shl     ebp, 4
004160D5  |.  03CD          |add     ecx, ebp
004160D7  |.  8B6E 08       |mov     ebp, [esi+8]
004160DA  |.  33EB          |xor     ebp, ebx
004160DC  |.  03CD          |add     ecx, ebp
004160DE  |.  8BEB          |mov     ebp, ebx
004160E0  |.  C1ED 05       |shr     ebp, 5
004160E3  |.  33E8          |xor     ebp, eax
004160E5  |.  03CD          |add     ecx, ebp
004160E7  |.  034E 0C       |add     ecx, [esi+C]
004160EA  |.  66:4F         |dec     di
004160EC  |.^ 75 C7         \jnz     short 004160B5    ;------------>加密循环

004160EE  |.  5F            pop     edi
004160EF  |.  891F          mov     [edi], ebx
004160F1  |.  894F 04       mov     [edi+4], ecx 


想解出密码,我们得把密文解密 20H 次,如果直接用汇编代码逆运算是算不出来的,引用 《C 语言程序设计》中的
一句话 "超出人的大脑能直接思考的范围 ",而且碰到 shr 之类的语句就郁闷了,因为你无法知道前一个值是什么.
记得《C 语言程序设计》 有提到过用伪代码描述算法,我们不妨把汇编算法用 C 伪代码描述.

密码算法 C 伪代码:

a = 密码的前4个字节
b = 密码的后4个字节
n = 20H
k = 9E3779B9H


-------------------
c = b << 4
a = a + c
c = b ^ "NJYZ"
a = a + c
c = b >> 5             =============================> 密码的前4个字节加密
c = c ^ (k * n) 
a = a + c
a = a + "-RG-"

--------------------

-----------------------------------------------------------------------------
 
--------------------

c = a << 4
b = b + c
c = a ^ "60-T"
b = b +c
c = a >> 5         =================================> 密码的后4个字节加密
c = c ^ (k * n)
b = b + c
b = b + "T-60" 

---------------------

循环加密 20H 次


经过上面的分析,大家应该对还原精灵的加密算法比较熟悉了吧!
接下来讨论一下解密器的制作:
可以看出前后4个字节的加密算法一样的,我们可以遍一个解密子程序
先解出密码的后4个字节,再解出密码的前4个字节,如此循环20H次.
下面是我自己写的还原密码破解程序的源代码,在 VC++ 6.0 下编译通过(控制台程序)


#include "windows.h"
#include "stdio.h"


unsigned long esi0 = 0x5a594a4e;  // "NJYZ"
unsigned long esi4 = 0x2d47522d;  // "-RG-"
unsigned long esi8 = 0x542d3036;  // "60-T"
unsigned long esic = 0x30362d54;  // "T-60"
unsigned long edx0 = 0x9e3779b9;  
//加密常量要仔细填,不然不但算不出密码,而且也很难找出错误.



void sub(unsigned long *a, unsigned long *b, unsigned long *a1, unsigned long
  *b1, unsigned long c1);

void main()
{

  printf("\n\t\t\t 还原精灵 6.0 密码读取程序 \n\n\n");
  printf("\t\t\t\t\t by FIGO \n\n\n");
  printf("\t\t\t 仅供学习交流使用,请勿用于非法目的\n\n\n\n");

  long hmov = 0;
  unsigned long *pwd;
  char str1[20];
  char *p1;
  unsigned long *p2;
  pwd = (unsigned long*)str1;
  unsigned long tmp1, tmp2, tmp3, i;

  HANDLE hFile;
  hFile = CreateFile("\\\\.\\PhysicalDrive0", GENERIC_ALL, FILE_SHARE_READ |
    FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);


  PBYTE pBuffer = (PBYTE)malloc(512);

  DWORD dwLen;
  hmov = SetFilePointer(hFile, 0x00000e00, (long*)(&hmov), FILE_BEGIN);
  ReadFile(hFile, pBuffer, 512, &dwLen, NULL);

  p1 = (char*)pBuffer + 0x4f;
  p2 = (unsigned long*)p1;
  tmp1 =  *p2;
  p2++;
  tmp2 =  *p2;

  for (i = 0x20; i >= 1; i--)
  {
    tmp3 = i * edx0;
    sub(&tmp2, &tmp1, &esi8, &esic, tmp3);
    sub(&tmp1, &tmp2, &esi0, &esi4, tmp3);

  }


  *pwd = tmp1;
  pwd = pwd++;
  *pwd = tmp2;
  pwd++;

  *pwd = 0;

  printf("\t\t\t password is : %s \n\n\n\n\n\n\n\n\n\n\n\n\n\n ", str1);

  CloseHandle(hFile);

  free(pBuffer);


}

void sub(unsigned long *a, unsigned long *b, unsigned long *a1, 
unsigned long *b1, unsigned long c1)
{


  unsigned long higt, low, esi1, esi2, ebp0, edx1;
  edx1 = c1;
  higt =  *a;
  low =  *b;
  esi1 =  *a1;
  esi2 =  *b1;
  ebp0 = low << 4;
  higt = higt - ebp0;
  ebp0 = low ^ esi1;
  higt = higt - ebp0;
  ebp0 = low >> 5;
  ebp0 = ebp0 ^ edx1;
  higt = higt - ebp0;
  higt = higt - esi2;

  *a = higt;

}

至于还原精灵6.0 以上版本及还原精灵网络版6.x的算法也是一样(至少目前为止),
只是加密的字符串不一样.
例如:
解还原精灵网络版 6.02(目前应该算是最新版吧)的密码,只要将上面的代码中的
字符串 "NJYZ-RG-60-TT-60" 替换为 "NJZY-NetCard-V60" 即可.

实际上解还原精灵6.0密码可以直接用内存注册机来解,在 0041616C 出中断,
EAX 指向密码地址(一定要先结束掉还原精灵的进程),网络版的无此BUG.

还原精灵各个版本一般都是把密码的明文或密文写在 0 磁道 8 扇区
偏移量为 04FH,该扇区并没有什么特殊保护,可以直接用 CreateFile 进行读写.