C CF 4F E6 19 27 ;
00000050h: E4 60 15 05 0F 36 D1 E3 DF B1 53 65 13 43 EB 3E ;
00000060h:B1 33 10 F0 79 5B B6 24 7C 2A 4A E0 7C 99 05 13 ;
怎么试呢,还是异或。取0x42处开始的字节0xDB与空密码文件的0x42处字节异或,取0x62处的加密标志与空密码文件0x62处字节异或,然后再把取得的两个值相异或:
(0xDB^0xBE)^(0x10^0x0C)=0x79 嘿嘿。这个值是Ascii的"y",然后取下一个字节(记得隔一个字节取一个)
(0x89^0xEC)^(0x10^0x0C)=0x79 咦,本来这个字节应该是"e"的,怎么变成"y"了?试着不与后面的两个异或值相异或,只计算0x89^0xEC=0x65 得到"e",哈。这下对了。下一个
(0x14^0x65)^(0x10^0C)=0x6D 得到"m",下一个
(0xF9^9C)=0x65 得到"e",注意这里只是这两个数异或。后面的大家可以自己试。
这样就总结出规律来了。
解密时,先取出加密文件从文件头开始0x62处的字节,与空密码数据库文件第0x62处相异或,得到一个加密标志。
再从0x42处开始每隔一个字节取一个字节,取得13个加密后的密码字节,分别与空密码数据库文件0x42处每隔一个字节取得的13个字节想异或,得到13个密码半成品。为什么说是半成品呢,因为还要将13个字节的密码每隔一个字节,就与加密标志相异或,最后得到的13个字节才是真正的密码。当然,如果中间有0x0的字节,则说明密码位数不够13位。直接show出来就可以了。
另外我发现加密标志会随着时间或机器不同而不同,所以也没有万能的,不过有一个参照的就可以了。以下代码是我在写这个
程序的时候取得的数,和我写这篇文章不是一个时间,所以数值不一样,但最终解密的结果是一样的。大家可以参考一下。
对了,还有个重要的就是先得判断数据库的版本,我用了个简单的办法,取0x14处的字节,如果为0就判断为是Access97,如果为1就认为是Access2000或2002的。只是目前没有研究出判断2000和2002的办法,如果哪位知道的话,请指点。
代码:
//这里定义的是13个字节作为Access2000异或的源码。与之相对应的加密标志是0x13,ccrun特此注明
//当然你可以用这一组: BE EC 65 9C FE 28 2B 8A 6C 7B CD DF 4F 与这一组相对应的加密标志是0x0c
//呵呵.
程序有些乱,希望大家能看的懂。
char PassSource2k={0xa1,0xec,0x7a,0x9c,0xe1,0x28,0x34,0x8a,0x73,0x7b,0xd2,0xdf,0x50};
//Access97的异或源码
char PassSource97={0x86,0xfb,0xec,0x37,0x5d,0x44,0x9c,0xfa,0xc6,0x5e,0x28,0xe6,0x13};
void __fastcall TMainForm::GetMdbPass()
{
char PassStrTemp[26],Ver,EncrypFlag,t1;
int FileHandle;
String MdbPassword,MdbVersion,MdbFileName;
FileHandle=FileOpen(MdbFileName,fmOpenRead);
if(FileHandle<0)
{
ShowMessage("文件打开错误!");
return;
}
//取得数据库版本
FileSeek(FileHandle,0x14,0);
FileRead(FileHandle,&Ver,1);
//取得加密标志
FileSeek(FileHandle,0x62,0);
FileRead(FileHandle,&EncrypFlag,1);
//读取加密后的密码到缓冲区
FileSeek(FileHandle,0x42,0);
FileRead(FileHandle,&PassStrTemp,26);
FileClose(FileHandle);
if(Ver<1)
{
MdbVersion="Access 97";
if(int(PassStrTemp[0]^PassSource97[0])==0)
MdbPassword="密码为空!";
else
{
MdbPassword="";
for(int j=0;j<13;j++)
MdbPassword=MdbPassword+char(PassStrTemp[j]^PassSource97[j]);
}
}
else
{
MdbVersion="Acces