下载本节例子程序和源代码 (9.67 KB) |
最近在CSDN论坛上看到许多人问着同一个问题——如何编写带服务器验证的 ESMTP 邮件发送程序,回答的人还真不少:有建议用 MAPI 的、有建议使用别人的类库的……其实我对此是颇不以为然的。ESMTP 的编写并不困难,关键是弄清楚了协议——只要严格按照协议来进行,就一定能OK,反而编程的技巧在此倒是无足轻重的。换句话说,写这个程序是Easy job,没有必要用到 MAPI 等“庞然大物”啊。
既然如此,让我们来使用 Win32ASM 编写一个自己的 ESMTP “引擎”,可以用在你的病毒或者木马上哦! :)
我们先来看看 SMTP 的发送协议,在 rfc821(smtp) 和 rfc1521(mime) 里面写得非常清楚,读者请自行参考这两个文档。什么?看不懂英文?……没关系,我给大家整理一下。ESMTP 的整个发送过程如下:
Socket连接后,按 ESMTP 协议通讯(注意每条命令结尾符“回车/换行/./回车/换行”结束): 1、EHLO <Domain>/r/n 这条命令可以不要,但是按照标准的写法,还是加上好。 Example: EHLO smtp.163.net/r/n 2、AUTH LOGIN/r/n 告诉服务器,要进行验证了。 3、Base64_Username/r/n 发送经过 Base64 编码的用户名给服务器。 4、Base64_Password/r/n 发送经过 Base64 编码的密码给服务器。 5、MAIL FROM:/r/n 发送者的Email地址。 Example: MAIL FROM: lcother@163.net/r/n 6、RCPT TO:/r/n 目标Email地址。 Example: RCPT TO: target@163.net/r/n 7、DATA/r/n 开始传送数据。 8、发送数据/r/n./r/n 注意:SMTP协议只发送信息,至于如何区分CC、BCC、Subject、Body等不属于SMTP的范围,详情请查看RFC文档MAIL格式部分 简单说一下MAIL格式:Sunject:<主题>/r/n/r/n<内容>,还有好多…… 返回值为“250 ***”表示正常,同时***里含有MessageID等信息(前面的命令都有相应的返回信息) 9、QUIT/r/n Over,走人…… |
上面的是 ESMTP 的协议,如果我们严格按照上面的协议,就可以写出 ESMTP 的发送程序了。大家看出来了吗?其实 ESMTP 与 SMTP 唯一不同的地方,就是在于第三步和第四步——缺少了这两个步骤, ESMTP 就等于 SMTP 了。
至于如何区分正文和附件,其实是在 DATA/r/n 后,通过一个叫做“Boundary”的分隔字符串来分隔开来的,这个“Boundary”可以是任意的字符串,随你喜欢怎么取都行,但是必须注意的是,分隔的时候要按照“--Boundary”的格式,结束的时候要用“--Boundary--”。而附件的内容,是通过 Base64 编码,包含在 DATA 里面进行发送;如果有多个附件,就要进行循环,把每个附件的内容包含进去,直至结束。
这样说会不会太抽象了?好吧,我举个例子:
"EHLO smtp.163.net", 13, 10 "AUTH LOGIN", 13, 10 "lcother", 13, 10 "lcother_password", 13, 10 "MAIL FROM:lcother@163.net", 13, 10 "RCPT TO:target@163.net", 13, 10 "DATA", 13, 10 "From: 老罗", 13, 10,/ "To: Somebody", 13, 10, / "Subject: 你好吗?", 13, 10, 0 "MIME-Version: 1.0", 13, 10, / "X-Mailer: LCMailer by LC", 13, 10, / "Content-type:multipart/mixed;Boundary=www.LuoCong.com", 13, 10, 13, 10, / "--www.LuoCong.com", 13, 10, / "Content-type:text/plain;Charset=GB2312", 13, 10, / "Content-Transfer-Encoding:8bit", 13, 10, 13, 10, / "好久不见,最近过得好吗?(正文内容)", 13, 10, 13, 10, 0, / .if (有附件) "--www.luocong.com", 13, 10, / .while (files to be sent) 13, 10, / 13, 10, / "--", 13, 10, / "Content-Type:application/octet-stream;Name=文件名", 0, / "Content-Disposition:attachment;FileName=文件名", 0, / 13, 10, / "Content-Transfer-Encoding:Base64", 13, 10, 13, 10, 0, / 经过 Base64 编码的文件内容, 13, 10, 0/ .endw .endif 13, 10, / "--www.luocong.com--", / 13, 10, ".", 13, 10 "QUIT", 13, 10 |
最后我再给出一个用 Win32ASM 写的 ESMTP 发送程序。值得注意的是,为了尽量简化程序,我没有处理服务器返回的消息,而且我在内存的动态分配上偷懒了,附件的发送有可能会出错。如果你有兴趣对它进行研究,请自行想办法解决吧。如果它倒了你的胃口,那……我真的是实在抱歉! ^_^ (代码写得很糟糕,我没有时间去 debug 了,见谅!!)
--------------- 主程序 ---------------
文件名: smtp.asm
--------------------------------------
---------- 主程序的资源文件 ----------
文件名: smtp.rc
--------------------------------------
#include "resource.h" #define IDI_LC 1 #define IDC_BUTTON_SEND 3000 #define IDC_EDIT_SMTPSERVER 3001 #define IDC_EDIT_USERNAME 3002 #define IDC_EDIT_PASSWORD 3003 #define IDC_EDIT_FROM 3004 #define IDC_EDIT_TO 3005 #define IDC_EDIT_SUBJECT 3006 #define IDC_EDIT_CONTENT 3007 #define IDC_EDIT_ATTACHMENT 3008 #define IDC_BUTTON_BROWSE 3009 #define IDC_STATIC -1 IDI_LC ICON "lc.ico" LC_DIALOG DIALOGEX 10, 10, 200, 280 STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "ESmtp demo by LC, 2002-11-2" FONT 9, "宋体", 0, 0, 0x0 BEGIN GROUPBOX "服务器信息", IDC_STATIC, 5, 5, 190, 60 LTEXT "SMTP服务器:", IDC_STATIC, 15, 20, 50, 10 LTEXT "用 户 名:", IDC_STATIC, 15, 35, 50, 10 LTEXT "密 码:", IDC_STATIC, 15, 50, 50, 10 GROUPBOX "邮件信息", IDC_STATIC, 5, 75, 190, 170 LTEXT "发信人电邮:", IDC_STATIC, 15, 90, 50, 10 LTEXT "收信人电邮:", IDC_STATIC, 15, 105, 50, 10 LTEXT "主 题:", IDC_STATIC, 15, 120, 50, 10 LTEXT "附 件:", IDC_STATIC, 15, 135, 50, 10 EDITTEXT IDC_EDIT_SMTPSERVER, 65, 20, 120, 10, ES_AUTOHSCROLL | NOT WS_BORDER, WS_EX_STATICEDGE EDITTEXT IDC_EDIT_USERNAME, 65, 35, 120, 10, ES_AUTOHSCROLL | NOT WS_BORDER, WS_EX_STATICEDGE EDITTEXT IDC_EDIT_PASSWORD, 65, 50, 120, 10, ES_PASSWORD | ES_AUTOHSCROLL | NOT WS_BORDER, WS_EX_STATICEDGE EDITTEXT IDC_EDIT_FROM, 65, 90, 120, 10, ES_AUTOHSCROLL | NOT WS_BORDER, WS_EX_STATICEDGE EDITTEXT IDC_EDIT_TO, 65, 105, 120, 10, ES_AUTOHSCROLL | NOT WS_BORDER, WS_EX_STATICEDGE EDITTEXT IDC_EDIT_SUBJECT, 65, 120, 120, 10, ES_AUTOHSCROLL | NOT WS_BORDER, WS_EX_STATICEDGE EDITTEXT IDC_EDIT_ATTACHMENT, 65, 135, 100, 10, ES_AUTOHSCROLL | NOT WS_BORDER | ES_READONLY | NOT WS_TABSTOP, WS_EX_STATICEDGE PUSHBUTTON "..", IDC_BUTTON_BROWSE, 170, 135, 15, 10, BS_FLAT | BS_RIGHT | BS_BOTTOM EDITTEXT IDC_EDIT_CONTENT, 15, 150, 170, 85, ES_AUTOVSCROLL | NOT WS_BORDER | WS_VSCROLL | ES_WANTRETURN | ES_MULTILINE, WS_EX_STATICEDGE DEFPUSHBUTTON "发送(&G)", IDC_BUTTON_SEND, 70, 255, 50, 15, BS_FLAT END |
我相信我已经把 ESMTP 的协议说明白了。如果你还有不太明白的地方,欢迎给我来信。 lcother@163.net
老罗
2002-11-2