hbx12345 发表于 2006-10-30 20:04:03

还原精灵密码算法分析

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




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

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



0040160E      .E8 B9F70200   call    <jmp.&MFC42.#6334_CWnd::UpdateData>
00401613      .8D86 18020000 lea   eax,
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,
0040162D      .E8 4CF70200   call    <jmp.&MFC42.#540_CString::CString>
00401632      .8D4C24 08   lea   ecx,


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

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

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

跟进这个CALL

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

00416160/$8B4424 04   mov   eax,
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,
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,
004160A1|.8B7D 0C       mov   edi,
004160A4|.57            push    edi
004160A5|.8B1F          mov   ebx,
004160A7|.8B4F 04       mov   ecx,
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,
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,

004160D0|.8BEB          |mov   ebp, ebx
004160D2|.C1E5 04       |shl   ebp, 4
004160D5|.03CD          |add   ecx, ebp
004160D7|.8B6E 08       |mov   ebp,
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,
004160EA|.66:4F         |dec   di
004160EC|.^ 75 C7         \jnz   short 004160B5    ;------------>加密循环

004160EE|.5F            pop   edi
004160EF|.891F          mov   , ebx
004160F1|.894F 04       mov   , 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;
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 进行读写.

转载JUJU猫宽带宝藏论坛
我也不知道他是原还是转不过加密算法可以借鉴的
页: [1]
查看完整版本: 还原精灵密码算法分析