驱动器路径表的结构及其使用方法
董占山
一般286或386微机都有一个容量极大的硬盘,为使用方便,在硬盘上要安装许多系统软件和专用软件,同时还有很多用户开发的应用程序,由于DOS系统的安全性比较差,软件、程序或数据往往容易被他人非法复制。怎样才能防止他人非法复制呢?
人们想出许多方法,其中有给硬盘加口令字,使子目录名变为隐含等多种方法,我向大家介绍一种使逻辑驱动器失效的防拷贝方法。巧妙地使用本程序,从硬盘启动后,可以使软驱(包括A、B驱)均失崐效,当在C:\>提字符下打入A:并回车,则显示“Invalid
drive specification”,键入B:时,同样显示此信息。这样在得不到许可的情况下,非法用户是很难拷贝程序和软件的。
一、获得驱动器路径表的方法
获得驱动器路径表需要用到未编入文档的DOS功能调用52H,该功能调用的用途是得到内部缓冲器的指针,该指针指向的表描述了与存储子系统有关的大多数DOS内部结构,返回指针存在ES:BX中。这个缓冲区的结构随DOS的主要版本而异,对DOS3.XX版及以上版本,此表的偏移16H处为指向驱动器路径的远指针。
驱动器路径表由多个表项组成,每个表项均包含缺省值路径、磁头位置和各种标志和指针,表项的数目等于有效逻辑驱动数加1,最后一表项的标志变量为零,没有任何有用数据。驱动器路径表项的结构如表1。
表1、驱动器路径表项的结构
┌────┬─────┬───────────────────────┐
│偏 移 │ 长 度 │ 说 明
│
├────┼─────┼───────────────────────┤
│ 0 │ 字 节 │ ASCIIZ格式的当前缺省值路径名,包含着逻辑驱动
│
│ │ (64) │
器字母、冒号分隔符和起始符"\"
│
│40H │ 双 字 │ 保留,置为0
│
│44H │ 字 节 │
标志变量,所有有效项包含一个40H,最后一项包含0│
│45H │ 双 字 │
逻辑驱动器参数块的远指针
│
│49H │ 字 │
此逻辑驱动器的当前块或磁道/扇区编号
│
│4BH │ 双 字 │ 远指针
│
│4FH │ 字 │
未知存储
│
│51H │ 字节(7) │ 未知存储(仅对MS DOS 5.0以上)
│
└────┴─────┴───────────────────────┘
从表1可知,在驱动器路径表每个表项的偏移44H处的一个字节为该逻辑驱动器是否有效的标志,有效时为40H,为其它值则无效,所以要使逻辑驱动器失效可以通过DOS功能调用52H,修改这个标志为0即可。
本人用TURBO C 2.0编写了一个程序SL.C,可以用来修改逻辑驱动器路径表,使逻辑驱动器有效或失效。该程序经编译、连接,生成COM文件即可使用。
二、程序的使用方法
该程序采用命令行格式:
SL [d:] [/switch]
其中d代表驱动器,switch为开关,可取L和U,取时L执行锁驱动器过程,取U时解锁已锁的驱动器。典型用法:
SL -- 显示程序的帮助信息
SL C: -- 显示C逻辑盘的当前状态
SL C: /L -- 锁C逻辑盘
SL C: /U -- 解锁C逻辑盘
该程序只能在MS DOS 3.0以上的操作系统下工作。
如果想在AUTOEXEC.BAT中使用该程序,又不想在每次启动系统时均输入口令字,可以利用DOS的输入输出重定向的功能来完成。具体方法是,首先建立一个含有口令字的文本文件(如A.DAT),其内容是两行同样的口令字符串,如:
AbCd
AbCd
然后,执行下面的DOS命令:
SL A: /L <A.DAT>NUL
即可将A盘锁住,对B盘只要将命令中的A:改为B:就行了。
三、作用与效果
如果能否巧妙地使用该程序,可以获得防止他人非法拷贝软件、程序或数据;如果将软驱加锁,对不知道加锁口令的用户,在未得到许可时无法使用软驱,从而可以减少在盘片交换过程中的病毒入侵机会。
经我长期使用该程序,取得了良好的效果。在软驱被锁后,减少了机器感染过病毒的可能,每当交换数据前,我们均用防毒软件对软盘进行消毒,以致于病毒很难侵入系统,这对系统的安全和数据的保密都有很好的效果。
四、源程序清单
/********************************************************/
/* 程序名称: SL.C 1.50 */
/* 作 者: 董占山 */
/* 完成日期: 1991,1995 */
/* 用 途: 软锁驱动器的程序 */
/* 编译方法: 用下列命令编译连接可以得到SL.COM: */
/* tcc -mt sl */
/* tlink c:\tc\lib\c0t+sl,sl,,c:\tc\lib\cs\lib /t */
/********************************************************/
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <ctype.h>
char drive,sw;
int seg,ofs;
int tablelen5=0x58,tabletail5=0x14; /* DOS 5.0以上版本的参数 */
int tablelen3=0x51,tabletail3=0xd; /* DOS 3.0以上5.0以下版本的参数 */
/* 显示程序使用方法 */
void help()
{
printf("Syntax: SL [d:] [/sw]\n");
printf("Switch: /L = Lock the drive d:\n");
printf(" /U = Unlock the drive d:\n");
printf("Examples: SL -- Display help text\n");
printf(" SL C: -- Display the state of drive C\n");
printf(" SL C: /L -- Lock the drive C\n");
printf(" SL C: /U -- Unlock the drive C\n");
printf("Note: Only Using in MS DOS 3.0 and above\n");
}
/* 输入口令字符串,并将口令写到驱动器路径表中 */
void getpassword()
{
char pw[9],pw1[9];
int i=0,j;
do {
printf("Enter the PASSWORD (8 characters) : ");
scanf("%s",pw);
printf("Enter the PASSWORD again (8 characters) : ");
scanf("%s",pw1);
} while (strcmp(pw,pw1)!=0);
j = ofs - 8 - 4;
while ((pw[i])!='\0') {
pokeb(seg,j,pw[i]); /* 将口令字写入驱动器路径表中 */
i++;
j++;
}
pokeb(seg,j,0);
}
/* 输入口令并校验 */
int checkpassword()
{
char pw[9],PsWdStr[9];
int i=0,j;
printf("Enter the PASSWORD (8 characters): ");
scanf("%s",pw);
j = ofs - 8 - 4;
/* 从路径表中取出口令字 */
while ((PsWdStr[i++]=peekb(seg,j))!=0) pokeb(seg,j++,0);
PsWdStr[i]='\0';
return strcmp(pw,PsWdStr);
/* 如果输入的口令与锁驱动器时的口令相吻合,返回0,否则返回非零值
*/
}
/* 取得驱动器的标识字节的地址 */
void getaddress()
{
int tablelen,tabletail;
switch (_osmajor) { /* 检查DOS系统的主版本号 */
case 3 : ; /* DOS 3.XX */
case 4 : tablelen=tablelen3; /* DOS 4.XX */
tabletail=tabletail3;
break;
case 5 : ; /* DOS 5.XX */
case 6 : tablelen=tablelen5; /* DOS 6.XX */
tabletail=tabletail5;
break;
default : printf("SL can not run under the DOS version !\n");
help();
exit(0);
}
asm SUB AX,AX
asm MOV AH,0x52 /* 取DOS内部缓冲器的指针 */
asm INT 0x21
asm MOV AX,ES:[BX+0x18] /* 取驱动器路径的远指针的段值 */
asm PUSH AX
asm MOV AX,ES:[BX+0x16] /* 取驱动器路径的远指针的偏移量 */
asm MOV BX,AX
asm POP ES
asm sub ch,ch
asm mov cl,drive
asm mov al,0x41 /* 字符A */
asm sub cl,al
asm inc cl /* 第几个驱动器 */
l1:
asm ADD BX,tablelen /* 驱动器路径表长度 */
asm LOOP l1
asm SUB BX,tabletail /* 减去表尾的字节数 */
asm mov seg,es /* 返回驱动器路径表的段地址 */
asm mov ofs,bx /* 返回驱动器路径表中标志字节的偏移量 */
}
/* 取驱动器的当前状态 */
void drivestate()
{
getaddress();
if (peekb(seg,ofs) == 0x40) /* 驱动器有效吗? */
printf("The state of the drive is unlocked !\n");
else
printf("The state of the drive is locked !\n");
}
/* 锁指定的驱动器 */
void Lockdrive()
{
getaddress();
if (peekb(seg,ofs) == 0x40) { /* 驱动器有效吗? */
getpassword(); /* 设置口令字 */
pokeb(seg,ofs,'\0'); /* 将标志字节置为无效 */
printf("This drive has been locked!\n");
}
else
printf("The state of the drive has been locked !\n");
}
/* 解锁指定的驱动器 */
void UnLockdrive()
{
getaddress();
if (peekb(seg,ofs) == 0) { /* 驱动器锁了? */
if (checkpassword()==0) { /* 检查口令字 */
pokeb(seg,ofs,0x40); /* 将标志字节置为有效 */
printf("\nYour password is correct,the drive is unlocked !\n");
}
else {
printf("\n%s\n%s\n","Your password is not correct,the drive cannot be
unlocked !",
"Please ask system manager to get the password !");
}
}
else
printf("The state of the drive has been unlocked !\n");
}
/* 取系统有效驱动器个数 */
char getdrivenum()
{
char mydrive;
asm sub ax,ax
asm mov ah,52h
asm int 21h
asm sub ah,ah
asm mov al,es:[bx+20h] /* 有效驱动器个数 */
asm mov mydrive,al
return mydrive;
}
/* 执行加/解锁 */
void works()
{
switch (sw) {
case 'L' : Lockdrive();break;
case 'U' : UnLockdrive();break;
default : printf("The switch is invalid !\n");
}
}
/* 显示错误信息 */
void writeerror()
{
printf("The parameter is error !\n\n");
help();
exit(0);
}
/* 分析DOS命令行参数 */
void getparameter(argc,argv)
int argc;
char *argv[];
{
char tempstr[3],tempchar;
if (argc > 1) {
strcpy(tempstr,argv[1]);
if (tempstr[1] == ':') {
tempchar = toupper(tempstr[0]); /* 取驱动器代号 */
if (isalpha(tempchar)) drive = tempchar;
else writeerror();
}
else writeerror();
}
if (argc > 2) {
strcpy(tempstr,argv[2]);
if (tempstr[0] == '/') {
tempchar = toupper(tempstr[1]);
sw = tempchar; /* 取开关字符 */
}
else writeerror();
}
}
/* 主程序 */
main(argc,argv)
int argc;
char *argv[];
{
printf("SL version 1.50 Copyright (c) 1991,95 Dong Zhanshan\n");
getparameter(argc,argv);
if (drive >= 'A') /* 判断驱动器是否有效 */
if (drive - 'A' + 1 > getdrivenum()) writeerror();
switch (argc) { /* 执行功能模块 */
case 1 : help();break;
case 2 : drivestate();break;
case 3 : works();break;
default: writeerror();
}
}
|