第八章
口令破解
第一节口令破解器
口令破解器是一个程序,它能将口令解译出来,或者让口令保护失效。口令破解器一般并不是真正地去解码,因为事实上很多加密算法是不可逆的。
也就是说,光是从被加密的数据和加密算法,不可能从它们身上反解出原来未加密的数据。其实大多数口令破解器是通过尝试一个一个的单词,用知道的加密算法来加密这些单词,直到发现一个单词经过加密后的结果和要解密的数据一样,就认为这个单词就是要找的密码了。
这就是目前最有效的方法。这种方法之所以比想象得有效得多的原因是:
许多人在选择密码时,技巧性都不是很好。许多人还认为他的私人数据反正没有放在网上,所以,密码选择也比随便。其实,一个用户在一个系统里有一个帐号,就是一个通入系统的门。如果,其中一个的密码不安全,则整个系统也就是不安全的。由于用户的密码的设置往往都是一些有意义的单词,或者干脆就是用户名本身,使得破解器的尝试次数大为降低。
许多加密算法在选择密钥时,都是通过随机数算法产生的。但往往由于这个随机数算法并不是真正意义上的随机数,从而大大降低了这个随机性,从而为解密提供了一些列的方便。比如,本来,需要尝试1000次,但由于上述随机性并不好,结果使得只需尝试1000次就能成功。
还有一个原因是目前计算机的速度相当的快,而且,互联网的存在,使得协同进行解密的可能性大为增加。这样强的计算能力用到解密上,造成了破解的时间大为降低。
通过上述分析,可见,从理论上来讲,任何密码都是可以破解的,只是一个时间的问题。对于一些安全性较低的系统,速度通常很快。
对于那种需要一个口令或注册码才能安装软件的情况,口令破解会显得更为简单。这种情况你可能会经常遇到。比如安装一个微软的软件,在安装过程中通常需要你输入一个CD-Key,如果这个CD-Key是正确的,那么它就开始安装。如果非法的,那么就退出安装。
通常有两种方法可以使这种方式失效。
一种是修改安装程序。因为这种方法的流程一般是在安装的时候先弹出一个对话框,请求输入CD-Key。接着程序会对输入的CD-Key进行运算,最后根据得到的结果决定是继续安装还是退出。现在有很多调试软件,它们提供丰富的调试功能,如单步执行,设置断点等等。一个比较好的软件是Soft-ICE。在运行安装程序之前,可以在调试软件里设置在系统弹出CD-Key输入对话框的时候停止执行。接着就可以用调试器跟踪代码的执行,将CD-Key判断部分整个的跳过去,直接进入安装程序。
另一个方法就是算法尝试。由于安装程序要对CD-Key进行运算,判断其合法性。因此,只要知道CD-Key的算法,就能轻而易举的进入。
已经有人对为软的这种算法进行了探讨。发现这些算法策略很简单。
第二节口令破解器是怎样工作的
要知道口令破解器是如何工作的,主要还是要知道加密算法。正如上面所说的,许多口令破解器是对某些单词进行加密,然后在比较。
可以将口令破解器用下面的图来表示:
侯选口令产生器的作用是产生认为可能是密码的单词。通常有好几种方法产生侯选密码。一种是从一个字典里读取一个单词。这种方法的理论根据是许多用户由于取密码有些不是很明智,比如将自己的名字,或者用户名,或者一个好记住的单词等等。所以,攻击这通常都将这些单词收集到一个文件里,叫做字典。在破解密码是,从这些字典里取出侯选密码。
另一种方法是用枚举法来产生这样的单词。通常从一个字母开始,一直增加,知道破解出密码为止。这里,通常要指定组成密码的字符集,比如从A-Z,0-9等等。为了便于协同破解密码,常常需要为密码产生器指定产生的密码的范围。
口令加密就是用一般的加密算法对从口令侯选器送来的单词进行加密。通常,对于攻击不同的系统,要采用不同的加密算法。加密算法有很多,通常是不可逆的。这就是造成了为什么口令破解器使用的是这种结构。
口令比较就是将从口令加密里出来的密文和要破解的密文进行比较。如果一致,那么当前侯选口令发生器中出来的单词就是要找的密码。如果不一致,则口令发生器再产生下一个侯选口令。
下面我们分别介绍Unix和Windows 95屏幕保护程序的密码算法。同时给出破解的源程序。另外还介绍Windows NT口令破解方法。最后再举一个软件注册码破解实例。
Unix口令破解简介
首先讲讲怎样在Unix下得到口令文件。
在标准的Unix系统中,口令文件是/etc/passwd。但是在使用NIS/yp或shadow的系统时,口令数据可能放在别的地方。
口令文件中的每一条目包含7个分号搁开的区域:
用户名
加密的password,口令有效期
用户号码
组号码
GECOS信息
Home目录
Shell
下面举个实例:
will:5fg63fhD3d5gh:9406:12:Will Spencer:/home/fsg/will:/bin/bash
上面这个条目包含了下面的信息:
用户名: will
加了密的口令: 5fg63fhD3d5gh
用户号码: 9406
组号码: 12
GECOS信息: Will Spencer
Home目录: /home/fsg/will
Shell: /bin/bash
当入侵者拿到了这个密码文件后,就开始对密码进行破解。当用户登录系统时,Unix将password的内容读入,并对这个密码进行加密,并将运算结果和口令文件中的相比较。
Unix口令破解器的基本结构就是我们前面分析的那种结构。目前较为流行的是John程序。他运行在Windows系统下,并且能很快的破解密码。
那么,对于shadow的口令怎么办呢?口令shadow是指将口令文件中的加了密的口令密文部分用一个特殊的符号表示,真正的密文放在另一个单独的文件里,一般的用户无法读到这个文件。
为了能读到这个文件,写一个程序,通过调用getpwent()函数来得到这个文件。程序举例如下:
#include < pwd.h>
main()
{
struct passwd *p;
while(p=getpwent())
printf("%s:%s:%d:%d:%s:%s:%s\n", p->pw_name, p->pw_passwd,
p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);
}
那么这个shadow文件放在哪个目录下面呢?
Unix Path Token
-----------------------------------------------------------------
HP-UX /.secure/etc/passwd *
IRIX 5 /etc/shadow x
Linux 1.1 /etc/shadow *
SCO Unix #.2.x /tcb/auth/files/< first letter *
of username>/< username>
SunOS4.1+c2 /etc/security/passwd.adjunct ##username
SunOS 5.0 /etc/shadow
< optional NIS+ private secure maps/tables/whatever>
System V Release 4.0 /etc/shadow x
System V Release 4.2 /etc/security/* database
Ultrix 4 /etc/auth[.dir|.pag] *
对于NIS/yp又怎样呢?
现在的NIS (Network Information System)以前也叫yp (Yellow Pages)。NIS的目的是允许一个网络上的多台计算机共享配置信息,包括口令数据。NIS的目的是提高系统的安全性。如果你使用的系统是NIS的,那么,口令文件相当小,看上去可能就是:
+::0:0:::
如果要看真正的口令,需要使用命令:"ypcat passwd"
在有的口令文件中,还包含一项数据--口令有效期。口令有效期的目的是促使用户在一定的时间后更改口令。这样就能提高系统的安全性。
/etc/passwd文件中如果保存口令有效期数据的话,这个条目看上去是这样的。
will:5fg63fhD3d,M.z8:9406:12:Will Spencer:/home/fsg/will:/bin/bash
上面这个条目中,密文后面有一个逗号,逗号后面的便是口令有效期了。这里是:
M.z8
对这四个字符的解释如下:
1.口令可以不改变而存在的最大的周数。
2.口令在改变之前必须使用的最小的周数。
3&4.口令上次改变的时间,以从1970年算起的周数。
如果1和2设置成"..",表示,下次登录的时候,必须改变口令了。随后口令管理程序会将口令有效期移去,这样,用户以后就没有口令有效期的限制了。
如果3和4设置成"..",表示下次登录时,必须改变口令。口令有效期由前两个字符表示。
如果第一个字符小于第二个字符,就不允许用户改变口令了。只有root才有权力改变这个用户的口令。必须注意,su命令并不检查口令有效期。一个过期的口令可以在使用su是,没有被迫改变口令的要求。
口令有效期代码
+------------------------------------------------------------------------+
| |
| Character: . / 0 1 2 3 4 5 6 7 8 9 A B C D E F G H |
| Number: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
| |
| Character: I J K L M N O P Q R S T U V W X Y Z a b |
| Number: 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
| |
| Character: c d e f g h i j k l m n o p q r s t u v |
| Number: 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
| |
| Character: w x y z |
| Number: 60 61 62 63 |
| |
+------------------------------------------------------------------------+
Windows 95屏幕保护口令密码破解简介
Window95共享目录口令与屏幕保护口令的加密方法是相同的。共享目录的口令密文放在注册表的HKEY_LOCAL_MACHINE\SOFTWARE\micorsoft\windows\current_version\network\lanman\目录名\Parm1enc和Parm2enc两位置,但有时注册库里缺少最后一个字符的密文。明文与数列(前八个数是35,9a,4d,a6,53,a9,d4,6a)作异或运算即得密文。屏幕保护程序的口令密文放在注册表的HKEY_CURRENT_USERS\ControlPanel\desktop\ScreenSave_Data下面。
关于PWL文件的一些说明:14个字符长的密码(均转为大写),用它生成一个32位的密钥,由以下算法求得一个XOR串,接下来用此XOR串 XOR 20字节长的UserName(也转为大写), 结果存于PWL文件offset 0x208-0x21B, 0x21C开始为一系列指向资源串的指针(当然已XOR过了)。资源串中保存的主要是该USER的一些共享目录的口令,资源串也分别与XOR串 XOR。
由注册表数据库system.dat极易解出共享目录, 因此若Win95目录被共享(不需口令)则解出其余需口令的目录就变得比较简单了。但Win95目录没共享怎么办呢?用一个叫 glide 的程序,对从将别的机器上拷回来的PWL文件进行解密。用glide解其资源,很有可能找到所需的password。
但glide程序在反解资源指针时有点问题。下面的程序对glide进行了一点改进。在password未知情况下的反解并不能保证对(这种反解利用了M$的愚蠢的错误,将同一 Xor串用于加密许多不同串), 但在大多情况下应没问题。本程序来自《水木清华》BBS,并已略加改动。
#include < stdio.h>
#include < string.h>
#include < ctype.h>
#include < dir.h>
unsigned char Data[10001]; // pwl file buffer, 10K should enough!
unsigned char keystream[1001]; // xor key stream
int Rpoint[300]; // Resource pointers
int size,maxr,cracked;
void RecoverKeyStream()
{
int sizemask,i,rsz,pos;
int Rall[300];
int keylen,len;
/* find allocated recources */
sizemask=keystream[0]+(keystream[1]< < 8);
for(i=0;i< 256;i++) Rall[i]=0;
maxr=-1;
for(i=0x109;i< 0x208;i++)
{
if(Data[i]!=0xff)
{
Rall[Data[i]]++;
if (Data[i]>maxr) maxr=Data[i];
}
}
if (maxr == -1) return; // no resource
maxr=(((maxr/16)+1)*16);
// recource pointer table size appears to be divisable by 16
/* search after recources */
keylen = 2 * maxr + 20 + 2;
Rpoint[0]=0x0208+keylen; /* first recource */
for(i=0;i< maxr;i++)
{
/* find size of current recource */
pos=Rpoint[i];
if (pos >= size)
{
printf("Decrypt pwl file error!\n");
maxr = i;
break;
}
rsz=Data[pos]+(Data[pos+1]< < 8);
rsz^=sizemask;
pos+=rsz+2;
if(i< maxr-1)
{
while(pos < size)
{
len = (*(unsigned int*)(Data+pos)) ^ sizemask;
if (Rall[i+1] == 0 && len == 0)
break; // correct position
if (Rall[i+1] > 0 && len >= 2 && len < = keylen)
break; // may be correct position ?
pos+=2; // else, increase by 2
}
}
Rpoint[i+1]=pos;
}
Rpoint[maxr]=size;
/* insert Table data into keystream */
for(i=0;i < = maxr;i++)
{
keystream[20+2*i]^=Rpoint[i] & 0x00ff;
keystream[21+2*i]^=(Rpoint[i] >> 8) & 0x00ff;
}
cracked+=maxr*2+2;
}
void DecryptResources()
{
int i,j,rsz;
/* decrypt resources */
for(i=0;i< maxr;i++)
{
rsz=Rpoint[i+1]-Rpoint[i];
if (rsz>cracked) rsz=cracked;
if (rsz > 2)
{
printf("Recource[%02d] (length: %02d)\n",i,rsz);
for(j=0;j< rsz;j++)
{
unsigned char c = Data[Rpoint[i]+j]^keystream[j];
printf("%c", c >= 0x20 && c < = 0x7e ? c : '.');
}
printf("\n");
}
}
}
int main (int argc,char *argv[])
{
struct ffblk ffblk;
int i,done,index = 0;
FILE *fd;
char *name,ch;
if (argc< 2)
{
printf("Usage: Pwl pwlfile(s) (eg: *.pwl)");
return 1;
}
done = findfirst(argv[1],&ffblk,0);
while (!done)
{
name = ffblk.ff_name;
printf("\n-----------File %2d: %11s------------\n", ++index,name);
/* read PWL file */
fd=fopen(name,"rb");
if (fd==NULL)
printf("can't open file %s",name);
else
{
size=0;
while(!feof(fd))
{
Data[size++]=fgetc(fd);
}
size--;
fclose(fd);
/* copy encrypted text into keystream */
cracked=size-0x0208;
if(cracked< 0) cracked=0;
if(cracked>1000) cracked=1000;
memcpy(keystream,Data+0x208,cracked);
/* generate 20 bytes of keystream */
for(i=0;i< 20;i++)
{
ch=toupper(name[i]);
if(ch==0) break;
if(ch=='.') break;
keystream[i]^=ch; // xor UserName
}
cracked=20;
RecoverKeyStream();
// recover key stream (54 bytes or more)
if (maxr == -1)
printf("No resource!\n");
else DecryptResources();
}
done = findnext(&ffblk);
}
return 0;
}
第三节注册码破解
下面将有关注册码破解的问题。这需要能熟练使用调试软件及有关计算机程序设计的知识。这里只是一个示范讲解。
破解WinZip 6.3 SR-1 (32-bit)
============================
1. 运行WinZip, 选Agree, 选HELP, 选About WinZip, 按R,
Username: 输入 Winter Lee
Register code: 输入 48319840 (随个人习惯)
2. 使用 Ctrl-D 进入WinICE 设断点
BPX HMEMCPY
按F5返回到WinZip中
3. 按OK, 立即被Winice中断
4. 取消断点
BD *
5. 按F12多次, 按F8跟踪进CALL 004096EA中
6. 按F10多次, 运行完CALL 004098C3后, 下指令
D AX
显示一个字符串: 45260FF8
7. 继续按F10运行完CALL 004099E6后, 下指令
D AX
又显示一个字符串: 49041381
8. 怀疑上述两个字符串即是正确的注册码, 重新输入
Username: Winer Lee
Register number: 49041381
注册成功! 用45260FF8 同样成功