SMTP电子邮电技术揭密 <首发于黑客X档案2003-2004年中期合订本>
作者:LionD8
SMTP又叫简单邮件传输协议,我们平时发的电子邮件就是利用的这个协议进行传输的,同时电子邮件也经常被黑客们利用进行一些攻击,比如伪造,或者攻击邮件服务器的电子邮件炸弹,在大家用这些黑软的同时有没有想过其中的技术内幕啊?现在我就起以一个抛砖引玉的作用简单的介绍一下电子邮件利用的技术和具体的实现方法。
一.首先我们必须简单的介绍一下SMTP邮件协议。SMTP服务默认是打开的25端口,我们下面用163的邮件服务器为例,来进行电子邮件伪造的实现。
1.首先要发邮件必须连接到邮件服务器上,然后发送HELO命令和服务器打招呼。命令格式
HELO<SP>意思就是,你好,我是某某。比如说HELO root。服务器会返回
250表示我们的请求成功。
2.然后我们就应该发送是谁发送的电子邮件,比如:MAILFROM:hacker@hacker.com伪造一个发送者。如果成功也会返回250。
3.然后我们要让邮件服务器知道,我们要给谁发送邮件。发送命令:RCPT
TO:xiaoji198288@163.com xiaoji198288@163.com为我们收件人的地址。一会我们就会用这个地址来试验。
4.收件人确定后我们就应该发送电子邮件的正文的数据了。发送命令:DATA。然后就是发送我们的邮件的内容。在邮件正文发送完毕后最后发送<CRLF>.<CRLF>告诉服务器邮件正文发送结束。
5.最后我们发送命令:QUIT。断开和邮件服务器的连接。到这里我们的整个邮件发送过程就结束了。也许您还觉得上面的过程比较空洞,没有关系下面我们将用代码来证实上面的每一个过程。
二.代码和注释部分。
首先我们定义一个CMAIL类。
class
CEmail
{
public:
void CBase64Encode(char* pSr,char*
pDes,int nSourLen); //BASE64编码用于发送附件。
void Sender();
//发送电子邮件
CString m_FileName;
//作为附件的文件名
CString m_MailDes; //收件人的油箱
CString m_ServerIP;
//MX记录IP,我们试验用的是163的MX记录,一会我们将介绍MX记录IP的查询方法。
SOCKET
m_sock;
//一个套结字
CEmail(CString FileName,CString SIP="",
CString DES=""); //构造函数
virtual ~CEmail();
};
下面我们简单的介绍一下MX邮件服务器的查询方法和BASE64编码。
MX记录查询:在CMD下输入
nslookup -qt=MX 163.com 回车。
MX记录的IP就是类似下面的返回结果:
G:\>nslookup
-qt=MX 163.com
***
Can't find server name for address 211.158.22.118: No response from
server
Server: dns.cta.net.cn
Address: 61.128.128.68
Non-authoritative
answer:
163.com MX
preference = 50, mail exchanger = m203.163.com
163.com MX
preference = 50, mail exchanger = m209.163.com
163.com MX
preference = 50, mail exchanger = m210.163.com
163.com
nameserver = ns.nease.net
163.com
nameserver = ns2.nease.net
m209.163.com internet address =
202.108.44.209
m210.163.com internet address =
202.108.44.210
m203.163.com internet address =
202.108.44.203
202.108.44.203
202.108.44.209 202.108.44.210 都是MX邮件服务器IP
我们选用的第一个202.108.44.203作为试验对象。
BASE64编码:
由于SMTP仅仅局限7位的ASCII码,由于电子邮件的用途广泛于是出现了MIME。MIME没有改动SMTP,只是在遵循SMTP的规则上对其进行了一些扩充,包括邮件头部,和非ASCII码的编码规则,用得最广泛的就是BASE64编码。由于附件不一定是纯文本格式,大多都是流式文件即2进制文件。所以对2进制文件进行编码。首先将24位(3个字节的)数据分为4个6位组。6位一共有64种值,分别对应A--Za--z0--9+/
用==和=分别表示最后一组只有8为或者为16位的数据。例如:01001001 00110001 01111001 对应编码为:010010
010011 000101 111001。BASE64编码:STE5。
基本的都介绍完了,下面就是发送邮件的主体部分了。
CEmail::CEmail(CString
FileName, CString SIP, CString DES)
{
m_FileName = FileName;
m_ServerIP = SIP;
m_MailDes = DES;
m_sock = NULL;
} //构造函数进行函数的初始化
//发送的主体函数
void
CEmail::Sender()
{
if
((m_sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET)
{
AfxMessageBox("m_sock
False");
exit(1);
}
//TCP套结字
SOCKADDR_IN
addr_in={0};
char* pbuf=NULL;
pbuf =
m_FileName.GetBuffer(m_FileName.GetLength());
//取得文件名字并转换为char*型。
char* next = 0;
while( next=strstr(pbuf,"\\")
)
pbuf=next+1; //获得文件的第一个字母
addr_in.sin_family=AF_INET;
addr_in.sin_port=htons(25);
addr_in.sin_addr.S_un.S_addr=inet_addr(m_ServerIP); //MX邮件记录IP
while (1)
{
int ret=connect(m_sock,(struct sockaddr*
)&addr_in,sizeof(addr_in));
if ( ret !=
SOCKET_ERROR)
break;
Sleep(1000);
}
//连接远程服务器。如果成功就跳出死循环。
char* pTString="helo
root\r\n";
char buf[256]={0};
send (m_sock,pTString,strlen(pTString),0);
//发送helo命令
Sleep(1000);
recv (m_sock,buf,256,0); //接收返回的信息
memset(buf,0,256);
CString tmp="";
tmp="mail
from:LionD8@CQSN.com\r\n";
send (m_sock,tmp,tmp.GetLength(),0); //发送mail from命令
recv
(m_sock,buf,256,0);
memset(buf,0,256);
tmp="rcpt
to:"+m_MailDes+"\r\n";
send (m_sock,tmp,tmp.GetLength(),0); //发送rcpt to命令
recv
(m_sock,buf,256,0);
memset(buf,0,256);
tmp = "data\r\n";
send (m_sock,tmp,tmp.GetLength(),0); //发送data命令
recv
(m_sock,buf,256,0);
memset(buf,0,256);
//MIME头部
tmp = "Subject:";
tmp += m_Title;
tmp+="\n";
//邮件的主题
send
(m_sock,tmp,tmp.GetLength(),0);
tmp = "From:"+m_SenderName+"\n"; //我们伪造的发件人
send (m_sock,tmp,tmp.GetLength(),0);
tmp = "To:"+m_MailDes+"\r\n"; //收件人
send
(m_sock,tmp,tmp.GetLength(),0);
tmp =
"Content-Type:multipart/mix;boundary=qwertyuiop\r\n";
//定义分段的标示为qwertyuiop.
send
(m_sock,tmp,tmp.GetLength(),0);
tmp =
"\n--qwertyuiop\n\n";
send
(m_sock,tmp,tmp.GetLength(),0);
m_StringText+="\n";
send
(m_sock,m_StringText,m_StringText.GetLength(),0); //发送正文内容
void *basepointer;
tmp =
"--qwertyuiop\n";
send
(m_sock,tmp,tmp.GetLength(),0);
tmp = "Content-Type:
application/octet-stream; name=";
tmp+=pbuf;
tmp+="\n";
send
(m_sock,tmp,tmp.GetLength(),0);
tmp = "Content-Transfer-Encoding:
base64\r\n";
//采用BASE64编码
send
(m_sock,tmp,tmp.GetLength(),0);
tmp = "Content-Disposition: attachment;
filename=";
tmp+=pbuf;
tmp+="\n\n";
send
(m_sock,tmp,tmp.GetLength(),0);
HANDLE hFile,
hMapping;
if ((hFile = CreateFile(m_FileName,
GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0))
== INVALID_HANDLE_VALUE)
{
AfxMessageBox("could not open
file");
return ;
} //打开文件.
if (!(hMapping = CreateFileMapping(hFile, 0,
PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
{
AfxMessageBox("mapping
failed");
CloseHandle(hFile);
return ;
}
if (!(basepointer = MapViewOfFile(hMapping,
FILE_MAP_READ, 0, 0, 0)))
{
AfxMessageBox("view
failed");
CloseHandle(hMapping);
CloseHandle(hFile);
return ;
}
//把文件映射到内存。
DWORD cb=GetFileSize(hFile,NULL);
//取得文件大小
char *t=(char*)malloc((cb/3)*4+4);
//分配BASE64编码内存
memset(t,0,(cb/3)*4+4);
CBase64Encode((char*)basepointer,t,cb);
//编码
send (m_sock,(char*)t,(cb/3)*4+4,0); //把编码后的文件发送出去
tmp = "\n\n\n--qwertyuiop--\n\n";
send (m_sock,tmp,tmp.GetLength(),0);
UnmapViewOfFile(basepointer);
CloseHandle(hMapping);
CloseHandle(hFile);
tmp = "\n\r\n.\r\n"; //正文传输结束
send
(m_sock,tmp,tmp.GetLength(),0);
recv
(m_sock,buf,256,0);
send
(m_sock,"quit\n",5,0);
AfxMessageBox("邮件发送完成");
closesocket(m_sock);
}