一、前言
公共机房的管理是一件很麻烦的事情,尤其近几年新型病毒猖獗,机房就更成了学校里的病毒爆发源头。归纳现在流行病毒的传播途径,主要有如下几种:USB移动存储设备(由于软驱应用比较少,所以已经逐步淡出传播途径);系统漏洞;系统弱密码;第三方软件漏洞;网页木马。
个人PC防范的手段主要是更新系统补丁、设置强密码、安装杀毒软件,但是很不幸这几种手段在公共机房里没法得到很好的应用。因为机房通常都安装了保护卡或者还原软件。系统更新和杀毒软件的病毒更新需要周期对系统分区进行读写,而保护卡在阻止了系统遭到破坏的同时,也防止了补丁和病毒库的及时更新。系统密码的问题在机房更是普遍存在,并非说机房的机器密码设置得不够严谨,而是机房的操作系统都是使用同一母盘克隆而来,密码都是一样的。Windows操作系统在访问另一台机器,会首先以本机用户名和密码尝试登录,如果两者相同,不需要再输入用户名和密码就可以直接访问对方机器。在机房环境中,这种机制简直是致命的,因为这意味着上机人员可以获取机房内任意一台机器的控制权,病毒同样可以利用这种方法传播。
针对以上的分析,为了尽可能减轻管理员的更新负担,笔者采用Delphi开发了机房机器自动更新的程序,主要是根据服务器的指令,在客户机器执行相应的安全策略。当然也有机房管理员采用远程执行服务端的共享批处理文件up.bat的办法,但是采用共享文件的方法会严重影响机器进入系统的速度(Windows初次访问共享文件耗时较长),而且不能根据需要添加特别指令。本程序实现的功能主要有:自动执行服务器下达的命令;下载服务器指定的文件至客户机;更改指定的客户机的系统账号和密码。
二、程序架构
程序采用C/S架构,开发环境为Delphi7,使用HTTP协议通信,只需开发客户端程序,不需要额外再开发服务器。
1.服务器
可以使用机房服务器或者任意一台空闲机器充当,需要安装一种Web服务软件,可以为IIS/Apache/Resin等任意一种web服务软件,无需启用动态脚本的支持。
2.客户机
Windows操作系统,具备网络连接能力,安装有我们编写的程序。
三、程序实现
为了使得机器在开机过后可以立即连接到服务器执行更新指令,程序设计成服务程序,程序为后台执行,不需要设计交互界面。 “file”→“new” →“others”,选择“Service Application”,“Serice Unit”的属性“StartType”选择“stAuto”,本例中的name属性为“AutoCMD”。其他的属性如“Display Name”、 “name”根据自己需要填写。需要注意的是,在Delphi中直接调试本程序是无法进入到程序主函数ServiceExecute()中的,所以程序中使用了写记录文件的方式进行程序调试。主要模块和代码如下:
1.读写取注册表
利用注册表记录程序中的一些信息,包括:
URL: 包含更新信息的服务器网页地址。例如http://server-up:8080/up.txt。
delaytime: 延时一定时间后再获取服务器指令。因为有时开机后相关服务可能尚未启动,需要等待一定时间。
//写入信息至注册表,注意注册表的键值位置
procedure TAutoCMD.WriteRegInfo(wrkey,wrvalue:string);
var
Reg:TRegistry;
getName:string;
begin
Reg:=TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
getname:=AutoCMD.GetNamePath;
reg.OpenKey('\SYSTEM\CurrentControlSet\Services\'+getname,true);
reg.WriteString(wrkey,wrvalue);
reg.CloseKey;
reg.Free;
end;
//读取注册表键值是rdkey的信息,返回获取到的值
function TAutoCMD.ReadRegInfo(rdkey:string):string;
var
Reg:TRegistry;
getname:string;
begin
Reg:=TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
getname:=AutoCMD.GetNamePath;
reg.OpenKey('\SYSTEM\CurrentControlSet\Services\'+getName,true);
DownURL:=reg.ReadString(rdKey);
result:=reg.ReadString(rdKey);
reg.CloseKey;
reg.Free;
end;
2.从URL下载文件函数
下载函数并没有使用微软的API函数UrlDownloadToFile,因为在测试中发现,如果某页面之前曾经下载过,再次应用UrlDownloadToFile时程序将不会向服务器发送请求,而是直接使用缓存中的页面信息。这样的机制在应用中会出现问题,令客户机读取到的指令信息无法与服务器同步。使用TIDHTTP组件可以解决这个问题。
function TAutoCMD.DownloadFile(SourceFile, DestFile: string): Boolean;
var
DownLoadFile:TFileStream;
idHttp1:TidHttp;
begin
idhttp1:=TidHttp.Create(nil);
DownLoadFile:=TFileStream.Create(DestFile,fmCreate);
try
IdHTTP1.Get(SourceFile,DownLoadFile);
result:=DownLoadFile.Size>0;
except
result:=false;
end;
IdHttp1.Free;
DownLoadFile.Free;
if not result then deletefile(DestFile);
end;
3.指令识别和执行函数
//执行读取下载回来的指令文件
//fname是指令文件名,RepeatCheck用于指定是否定时循环检查
//可识别的指令包括:"user,sleep,down,repeaturl,repeattime
//不被识别的指令将被直接用winexec执行
function TAutocmd.executecmds(fname:String;var RepeatCheck:Boolean):Boolean;
var
f:textfile;
cmd:string;
user,pwd,cstr,par1,par2,strdownto,strdownfrom,downname:string;
pos1,pos2,SleepTime,strlen:integer;
begin
RepeatCheck:=false;
//检查文件是否存在
if not FileExists(fname) then
begin
result:=false;
exit;
end;
assignfile(f,fname);
reset(f);
while not eof(f) do
begin
//读取指令文件的一行
readln(f,cmd);
strlen:=length(cmd);
if trim(cmd)<>'' then
begin
if pos('user:',LowerCase(cmd))>0 then
//修改系统用户密码 指令格式 user:ab pass:fffff
begin
pos1:=pos('pass:',LowerCase(cmd));
user:=copy(cmd,6,pos1-7);
pwd:=copy(cmd,pos1+5,strlen-pos1-4);
//cpwd是修改系统用户密码的函数,以机器名为基准
cmd:=cpwd(user,pwd,GetHostName);
winexec(pchar(cmd),SW_HIDE);
end
else
if pos('sleep:',LowerCase(cmd))>0 then
//休眠一定时间,指令例子 sleep:30
begin
sleeptime:=strtoint(trim(copy(cmd,7,strlen-6)));
sleep(sleeptime);
end
else if pos('down:',LowerCase(cmd))>0 then
//下载文件至客户机
//指令格式 down:http://server/abc.txt to:d:\test.exe
begin
pos2:=pos('to:',LowerCase(cmd));
if pos2>0 then
begin
par2:=copy(cmd,pos2+3,strlen-pos2-3+1);
par1:=copy(cmd,6,pos2-6-1);
end
else
begin
par1:=copy(cmd,6,strlen-6+1);
par2:=getTempDirectory;
end;
downloadFile(par1,par2);
end
else if pos('repeaturl:',LowerCase(cmd))>0 then
begin repeaturl:=trim(copy(cmd,length('repeaturl:')+1,strlen-length('repeaturl:')));
RepeatCheck:=true;
end
else if pos('repeattime:',LowerCase(cmd))>0 then
repeattime:=strtointdef(copy(cmd,length('repeattime:')+1,strlen-length('repeattime:')),60*1000*10)
else
winexec(pchar(cmd),SW_HIDE);
end;
end;
closefile(f);
end;
4.服务程序的执行主代码
procedure TAutoCMD.ServiceExecute(Sender: TService);
var
i:integer;
downloaded:boolean;
strtmp:string;
strdown:string;
delaytime:string;
tmpboolean:boolean;
begin
//暂停delaytime秒,避免开机时的下载造成进入系统速度变慢
delaytime:=readreginfo('delaytime');
if delaytime<>'' then sleep(strtointdef(delaytime,60*1000));
cmdstr:=''; repeaturl:='';
downloaded:=false;
AppPath:=extractfilepath(paramstr(0));
tmpPath:=getTempDirectory;
strdown:=readreginfo('url');
if strdown='' then exit;
i:=0;
//如果下载没有成功,再重复尝试3次
while (not downloaded) and (i<=3) do
begin
downloaded:=DownloadFile(strdown,tmpPath+'startcmds.txt');
inc(i);
end;
//如果开机时的下载没有成功,则启用循环检查
if not downloaded then
begin
checkrepeat:=true;
repeaturl:=strdown;
end
else
begin
//识别和执行指令
executecmds(tmpPath+'startcmds.txt',tmpboolean);
checkrepeat:=tmpboolean;
end;
//查看是否需要循环执行指令
while (checkrepeat) do
begin
i:=0; downloaded:=false;
while (not downloaded) and (i<=2) do
begin
downloaded:=DownloadFile(repeaturl,tmpPath+'repeatcmds.txt');
inc(i);
end;
if downloaded then
executecmds(tmpPath+'repeatcmds.txt',tmpboolean);
//暂停repeattime毫秒,即定时检查更新的指令
sleep(repeattime);
end;
end;
5.服务程序被安装后,需要写入预先设定的注册表数据,此事件发生在使用“/install”参数执行安装时。
procedure TAutoCMD.ServiceAfterInstall(Sender: TService);
var
strdown:string;
delaytime:string;
begin
//读取安装时的ini文件中的值,并写入注册表中
strdown:=ReadIniInfo('downurl');
delaytime:=ReadIniInfo('delaytime');
writereginfo('url',strdown);
WriteRegInfo('delaytime',delaytime);
//服务程序的描述,Delphi生成的服务程序缺少这个选项
WriteRegInfo(‘Description’,’ 华南师范大学南海校区(学院)机房升级专用’)
end;
四、程序说明
设置Web服务使用IIS,新建站点,指定服务端口666,指定根目录为d:\up,在d:\up下新建up.txt,内容可以依照机房实际情况修改,范例如下:
repeattime:300000
repeaturl:http://server:666/repeat.txt
down:http://server:666/noAutorunInf.reg to:c:\noAutorunInf.reg
regedit /s c:\noAutorunInf.reg
user:administrator pass:pwdprefix
arp -s 192.168.1.1 00-11-33-44-55-65
net share ipc$ /delete
net share admin$ /delete
net stop “Task Scheduler”
各行分别说明如下:
①设定定时检查的间隔时间,单位毫秒,300000=1000*60*5,即5分钟;
②设定定时检查的指令地址;
③下载禁止Autorun.inf功能的文件至c:\pskill.exe;
④导入c:\noAutorunInf.reg到注册表中,实现客户机对U盘病毒的免疫;
⑤修改本机系统账号密码,为避免密码相同,密码采用“pwdprefix+机器名”的方式;
⑥执行本机命令,绑定网关mac地址,防arp欺骗;
⑦⑧执行本机命令,去除默认共享;
⑨停止“计划任务”服务,很多病毒可以利用这一服务。
如果服务器与客户机在同一网段,客户机指定的指令URL地址中建议使用机器名,例如http://servera:666/up.txt;如果服务器与客户机不在同一网段,URL中必须使用IP,例如http://192.168.1.1:666/up.txt。另外,建议使用非标准HTTP端口作为程序的Web服务的端口,例如666。
假设编译生成的程序名为NHAutoCMD.exe,在相同目录下写入了指令地址的“install.ini”,“install.ini”的内容是设定服务器指令地址,即“downurl =http://servera:666/up.txt”,客户即执行“NHAutoCMD /install”即可完成客户机服务程序的安装。关闭保护卡保存好本次程序的安装,重启后程序将自动运行。需要卸载本程序时使用“NHAutoCMD /uninstall”。
五、结语
采用每次开机执行服务器上指令的办法可以有效增强机房机器的防护能力,尤其是对于层出不穷的病毒。譬如最近流行的arp欺骗病毒,可以导致整个机房的机器不能上网,利用我们的程序,通过执行服务端上设定的绑定网关MAC的指令,可以比较好地解决这个问题;关闭默认IPC共享可以避免病毒的网络拷贝和感染;进入系统后再更改默认系统密码可以解决病毒传播和学生控制室内其他机器的恶作剧;下载特定的专杀工具至客户机并执行可以及时对付最新的病毒。所有这些操作都只需要在服务端的指令集中指定即可,可以根据最新的病毒传播途径和特征进行及时修改,无需每次都关闭还原卡后再分别对大量的机器进行更新。该程序在我校机房中应用已有一段时间,效果非常明显。
|