MIME协议及源邮件格式分析

转载链接:http://wenku.baidu.com/view/7246de671ed9ad51f01df277.html


电子邮件也许是一个Internet上的流行最广泛的应用。也是我们现在的大多数网络办公流程的基础。各种邮件服务器很多,但都大都遵循以1982年出版的RFC822--《ARPA网络文本信息格式标准(STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES)》为基础的一系列邮件格式的规定。 
 
RFC(The Requests for Comments)是用来规定互联网工作标准的文档。我们使用的时候并没有注意到这些协议在我们的邮件通信过程中默默的发挥着的作用,这丝毫也不能减低这些作用的重要性。邮件内部还有很多不为人知的秘密。
 
在RFC822中规定一封信包括一个必须的多个头部域(header fields)和一个可选的体部(body)组成。从一封信头开始至第一个空行都是头部。头部定义了一个邮件的各项基本要素,路由信息等内容。
 
在Outlook Express中选定一封信看它的属性。在详细资料选项卡中显示的就是这封邮件的头部内容。也可以选定一封信,另存为一个.eml文件。由于文件是一个纯文本文件,用一般的编辑器打开就可以看到邮件的内容。 
头部有各个头部域组成,每一个头部域都包括域名(field-name)和域体(field-body),它们之间以":"分隔。每一个头部域都可以看作由ASCII码字符组成的独立的文本。常见的头部域包括:"Return-Path", "Received", "Date", "From", "Subject", "Sender","To", "cc","MIME-Version"等。各头部域之间没有规定顺序。  就像各个域的名字一样。他们表示的具体意义也不同。 Return-Path域表示退信的地址。 Received域表示路由信息。
 
Internet上的信件可能是由多个服务器依靠协议传递到达最终的目的邮箱的。每一个服务器都会把自己的一段
Received域信息添加近信件。所以可能有多段Received域,依传递次序排列。这个域的内容很有意思,我们可以根据每一段的内容来跟踪一封信在internet上的传递过程。网管人员也可以根据这些信息做出很有价值的判断。
  例如一段代码中   Received: from unknown (unknown [202.108.44.208]) by mx7.163.com (Postfix) with SMTP id 74E0E8 for; Fri, 28 Feb 2003 18:10:25 +0800 (CST)中,from 表示发送主机,by表示接受主机,via物理路径(本例没有),with表示使用的协议,id表示接受消息号,for表示目的邮件地址,分号后面表示时间。
  Date域表示建立信件的时间。   From域表示邮件作者。
  Subject域就是邮件的主题。
  Sender域表示邮件的实际发送者。   To,cc域都表示接受的邮件地址。
  MIME-Version域表示MIME的版本(以后扩充的域)
  还有一个域—Content-Type。标识了一个重要的概念:邮件内容的格式。这个域的内容根据互联网的发展产生,使邮件可以用来传输非文本内容,在RFC822中并没有定义,而是在后续的RFC2045,RFC2046等文档中有定义。
  还有一些不太常见的头部域,如"References","Message-ID"域。以及一些由不同厂家定义的自己的域(域名以"X-"开头)。
  知道了这些知识,就可以看懂邮件头部。了解到那些邮件的秘密。   比如这是一封邮件的头部。
Return-Path: "sea" 
Received: from unknown (unknown [202.108.44.208]) by mx7.163.com (Postfix) with SMTP id 74E0E8 for  Fri, 28 Feb 2003 18:10:25 +0800 (CST)
Received: from smtp.netcs.com (unknown [211.150.100.6])
by 192.168.1.208 (Coremail:www.163.com) with SMTP id cAsAAJY1Xz7kAmQG.1 for  Fri, 28 Feb 2003 18:10:30 +0800 (CST) Received: from sea (unknown [192.168.18.160])
by smtp.netcs.com (Postfix) with ESMTP id E55D02675F
for  Fri, 28 Feb 2003 18:09:58 +0800 (CST)
Message-ID: <000901c2df11$2fcac090$a012a8c0@sea> From: "sea" 
To: "=?gb2312?B?usK6vQ==?=" 
Message-ID: <000901c2df11$2fcac090$a012a8c0@sea> References: <000c01c2decb$9c7b7860$a012a8c0@sea>  <004601c2dee2$28a348c0$2400a8c0@haostation>  <001501c2deeb$352b34a0$a012a8c0@sea> Subject: Re: hello
Date: Fri, 28 Feb 2003 18:07:22 +0800 MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_000_0006_01C2DF54.3DDEBE50" X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2600.0000
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2600.0000  
  通过辨认就可以知道这是一封从(From域)"sea" 写给(To域)"=?gb2312?B?usK6vQ==?=" 的Id(Message-ID域)为<000901c2df11$2fcac090$a012a8c0@sea>的回信。退信的地址(Return-Path域)是haibo.yang@netcscom可以看出(References域)这封信已经经过了连续三次回复。主题(Subject域)是Re: hello。时间(Date域)是2003年的3月28日。邮件优先级(X-Priority域)是普通。邮件的传递依次经过了三个服务器(Received域):smtp.netcs.com;unknown;和mx7.163.com。还可以看到各服务器的协议和到达各服务器的时间。其中Postfix和Coremail都是常见的邮件系统。其中邮件接收地址中的内容由于包含汉字进行了编码。 
  一封邮件首先建立的是核心的几个域:From,To,Message-Id,Subject,Date等,在传输过程中每通过一个服务器再由此服务器在邮件头部添加一行Received信息,最后一个服务器添加退信地址(Return-Path)。
  邮件头的格式制定的细节是根据信息交流和网络传输的特点制定的。阅读它们不仅可以知道邮件传输的秘密,还可以让我们体会到协议制定与实现间的互相关联的紧密关系。可以通过http://www.rfc.net/ 查询到RFC822,同时可以查询到其它重要的RFC文档。 
 
MX记录的应用
在DNS服务器上除了可以建立主机名与IP地址的映射外,还可以建立其他多种映射,例如,建立某个主机名与其别名的映射;建立某个域名与其SMTP服务器的映射。在DNS服务器上创建的各项映射关系称为记录,一项映射关系就是一条记录,在DNS服务器上创建的主机名与IP地址的映射关系称为A记录,主机名与别名的映射关系称为CNAME记录,域名与其SMTP服务器的映射关系称为MX记录。
在DNS服务器上为什么要建立MX记录呢?即为什么要建立域名与其SMTP服务器的映射关系呢?这与电子邮件地址的表示形式和工作原理有关。邮件地址后缀部分表示的通常都是一个域名,而不是接收邮件的服务器的主机名,例如,邮件地址“[email protected]”中的“it315.org”对应的就是一个域名。域只是一个逻辑组合概念,它并不代表真正的计算机,对于使用某个域名作为后缀的邮件地址,外界发送给它的电子邮件必须由一台专门的SMTP服务器来进行接收和处理,接收和处理某个域的电子邮件的SMTP服务器即为该域的SMTP服务器,外界发送给某个域的电子邮件实际上都是发送给该域的SMTP服务器。外界如何知道一个域的SMTP服务器的地址呢?这就是通过管理该域的DNS服务器上的MX记录来获得的,这也就是在DNS服务器上为什么要建立域名与其SMTP服务器的映射关系的原因。
当某台SMTP服务器要给“[email protected]”发送一封电子邮件时,该SMTP服务器将根据邮件地址的后缀部分而去查询“it315.org”这个域的MX记录,得到这个域的SMTP服务器的主机名为“mail.it315.org”,然后将邮件发送给“mail.it315.org”这个SMTP服务器。
动手实践  根据域名查询SMTP服务器 

Windows操作系统自带的nslookup命令可以查询一个域的MX记录。要想知道负责接收某个电子邮箱的邮件的SMTP服务器,可以先根据该邮件地址的后缀部分推断出其域名,然后使用nslookup命令进行查询即可。下面以查找负责接收“[email protected]”这个邮件地址的SMTP服务器的信息为例进行讲解。
 
启动Windows下的DOS命令行窗口,执行nslookup命令,结果如图1.16所示。
 
图1.16中“ns2.bjgwbn.net.cn”表示执行nslookup命令的这台计算机当前所使用的DNS查询服务器的主机名,“211.161.46.84”则表示该DNS服务器的IP地址。
 
在图1.16中的“>”提示符后面先输入“set type=mx”命令,设置所要查询的记录类型为MX,然后输入域名
“sina.com”,结果如图1.17所示。
 
图1.16                                  图1.17
图1.17中显示了“sina.com”这个域的SMTP服务器的主机名和IP地址,其中“sinamx.sina.com.cn”为SMTP服务器的主机名,并且该主机名对应了3个IP地址。这说明“sina.com”为了扩充其邮件处理能力,使用了3台SMTP服务器来接收外界发送给该域的邮件,这3台服务器的主机名都为“sinamx.sina.com.cn”,外界的SMTP服务器可以选择其中任何一台进行连接,然后将邮件发送给该服务器。
 
在图1.17中的“>”提示符后面再次输入“sina.com”,nslookup命令将再次查询该域的MX记录,结果如图
1.18所示。
比较图1.18中的用方框标识的部分,可以看到这两次查询出的“sinamx.sina.com.cn”所对应的3个IP地址的排列顺序并不相同,第一次查询时“202.108.3.187”排列在最前面,而第二次查询时却变成了“202.108.3.188”排列在最前面。如果继续查询“sina.com”域的MX记录,可以看到“sinamx.sina.com.cn”所对应的3个IP地址的排列顺序总是在改变。这是由于sina在管理“sina.com.cn”域的DNS服务器上进行了专门的设置,当外界每次查询“sinamx.sina.com.cn”这个主机名的IP地址时,管理“sina.com.cn”域的DNS服务器都将对该主机名所对应的3个IP地址进行轮循排列后再返回。
 
图1.18
对于查询到一个主机名对应多个IP地址的情况,外界的计算机通常都是选择其中的第一个IP地址来进行通信,只要管理“sina.com.cn”域的DNS服务器每次都将“sinamx.sina.com.cn”所对应的3个IP地址进行轮循排列后再返回给外界,外界的SMTP服务器将会分别选择到不同的IP地址来进行通信,sina通过这种简单的方式就在“sinamx.sina.com.cn”所对应的3台SMTP服务器之间实现了负载均衡。
提示:使用nslookup查询“sina.com”域的MX记录时,读者可能会遇到DNS服务器只返回SMTP服务器的主机名,而不返回该主机名所对应的IP地址的情况,这时候可以在原来的nslookup命令窗口中输入“set type=a”命令,即

将要查询的记录类型设置为A,然后输入前面查询到的SMTP服务器的主机名就可以得到其IP地址

电子邮件的工作原理
下面以sina和sohu这两个电子邮局为例来讲解电子邮件的传输过程和工作原理。假设sina邮箱的账户为[email protected],sohu邮箱的账户为[email protected],它们之间的邮件收发过程如图1.20所示。
 
图1.20
图1.20中实线部分表示[email protected]账户向[email protected]账户发送邮件的过程,虚线部分表示[email protected]账户向[email protected]账户发送邮件的过程。下面通过分析[email protected]账户向[email protected]账户发送邮件的过程,来具体讲解一封邮件从发送到接收所涉及的环节。
 
[email protected]的邮件客户端程序(这里假设为Outlook Express)与sina的SMTP服务器建立网络连接,并
以lisi的用户名和密码进行登录后,使用SMTP协议把邮件发送给sina的SMTP服务器。
 
sina的SMTP服务器收到[email protected]提交的电子邮件后,首先根据收件人的地址后缀判断接收者的邮件地
址是否属于该SMTP服务器的管辖范围,如果是的话就直接把邮件存储到收件人的邮箱中,否则,sina的SMTP服务器向DNS服务器查询收件人的邮件地址后缀(sohu.com)所表示的域名的MX记录,从而得到sohu的SMTP服务器信息,然后与sohu的SMTP服务器建立连接并采用SMTP协议把邮件发送给sohu的SMTP服务器。
 
sohu的SMTP服务器收到sina的SMTP服务器发来的电子邮件后,也将根据收件人的地址判断该邮件是否属于
该SMTP服务器的管辖范围,如果是的话就直接把邮件存储到收件人的邮箱中,否则(一般不会出现这种情况),sohu的SMTP服务器可能继续转发这封电子邮件,也可能丢弃这封电子邮件。
 
拥有[email protected]账户的用户通过邮件客户端程序(这里假设也为Outlook Express)与sohu的POP3/IMAP
服务器建立网络连接,并以wangwu的用户名和密码进行登录后,就可以通过POP3或IMAP协议查看[email protected]邮箱中是否有新邮件,如果有的话,则使用POP3或IMAP协议读取邮箱中的邮件。
图1.20中的虚线部分表示[email protected]账户向lisi@sina. com账户发送邮件的过程,此过程与[email protected]账户向wangwu@ sohu.com账户发送邮件的过程类似,这里不再复述。
多想一想  邮件客户端程序和SMTP服务器
分别如何发送邮件
邮件客户端软件与SMTP服务器之间,以及两台SMTP服务器之间都采用SMTP协议进行通信。邮件客户端软件只采用SMTP协议发送邮件,即邮件客户端软件只作为SMTP协议的发送方。SMTP服务器既要采用SMTP协议向其他SMTP服务器发送邮件,又要采用SMTP协议接收其他SMTP服务器或邮件客户端软件发送来的邮件,即SMTP服务器既作为SMTP协议的发送方,又作为SMTP协议的接收方。邮件客户端软件与SMTP服务器都可以使用SMTP协议发送邮件,仅发送邮件这一功能而言,它们在技术实现上有何差异呢?它们的差异主要体现在以下两个方面:
(1)SMTP服务器接收到邮件客户端软件发送来的邮件后,需要根据收件人地址的域名将邮件转发给目标域的SMTP服务器,而大量的收件人地址中肯定会出现各种可能的域名,因此,SMTP服务器涉及到要与其他多台不能事先确定的SMTP服务器进行通信,它需要具有根据收件人地址的域名查询出该域的SMTP服务器的功能,即需要具有查询域名的MX记录的功能。邮件客户端软件被设置为与固定的SMTP服务器通信,它可以直接与指定的SMTP服务器建立网络连接,因此它不需要具有根据收件人地址的域名来查询该域的SMTP服务器的功能。  

(2)对于来自某个邮件客户端软件的邮件传输请求,SMTP服务器可能需要对发件人的用户账号信息进行验证,因此,邮件客户端软件需要具有向SMTP服务器传送用户账号信息的功能。SMTP服务器能够直接接受来自其他SMTP服务器的邮件传输请求,因此,SMTP服务器在发送邮件时根本不需要传送用户账号信息的功能。
 
RFC822邮件格式
邮件内容的格式在RFC822文档中定义,它包括两个主要的组成部分:邮件头和邮件体。在第2章的SMTP协议的实验中,it315_test @sohu.com邮箱中接收到的[email protected]发送来的邮件的最终内容如例程3-1所示,其中的行号不属于邮件内容,是作者为了便于讲解而加上的。
例程3-1  test.eml
1.Return-Path: 
2.Delivered-To: [email protected]
3.Received: from smtp.sina.com.cn (unknown [202.108.3.177]) 4. by sohumx139.sohu.com (Postfix) with SMTP id E4F9802C1249
5. for ; Thu, 10 Nov 2005 16:39:50 +0800 (CST)
6.Received: (qmail 49221 invoked from network); 10 Nov 2005 08:39: 33 -0000 7.Received: from unknown (HELO it315?test) (218.246.5.151) 8.   by smtp.sina.com.cn with SMTP; 10 Nov 2005 08:39:33 -0000 9. From: [email protected] 10.To: [email protected] 11.subject:test
12.Message-Id: <[email protected]> 13.Date: Thu, 10 Nov 2005 16:39:50 +0800 (CST) 14.Status: RO
15.X-UIDL: 1131611863.21509_77.mx72 16.
17.test!!!
例程3-1中的第1~15行是邮件的邮件头,第17行是邮件的邮件体,邮件头和邮件体之间以一个空行进行分隔。这封邮件的邮件体内容非常少,只有一行“test!!!”文本。邮件头部分由多个头字段和字段内容组成,各种头字段分别用于表示邮件的发件人、收件人、发件时间和主题等信息。细心的读者可能已经看到,例程3-1中的邮件头部分比在第2章的SMTP协议的实验中实际发送的邮件头多出了一些头字段,这些头字段是各个SMTP服务器在传递邮件的过程加上的。SMTP服务器在传递邮件时,会把一些相关信息增加到邮件的邮件头中,这种情况有点类似于现实生活中的邮局在处理邮件时,通常都会在信封上加盖邮戳一样,表示这封邮件在什么时候经过了哪个邮局和由哪个工作人员经手处理。SMTP服务器按从下往上的方式添加各个字段,即先添加的字段位于后添加的字段的下面,例如,例程3-1中的邮件是由sina发送给sohu的,它首先经过sina的SMTP服务器,然后经过sohu的SMTP服务器,所以,sohu的SMTP服务器添加的头字段(1~5行)位于sina的SMTP服务器添加的头字段(6~8行)的上面。另外,POP3服务器也会在邮件头中增加一些头字段,例如,例程3-1中的12~15行。
每一个邮件头以“字段名:字段值”的格式出现,即每一行邮件头的内容依次由字段名、冒号、空格、字段值、回车换行符组成。RFC822文档中定义了多个标准的邮件头字段,每一个邮件头字段表示一种特定的信息。邮件头中也可以包含自定义的头字段,这种自定义的头字段通常是某个组织或机构内部专用的。下面是对例程3-1中出现的一些主要的邮件头字段的解释:
—  Return-Path  该字段代表邮件的回复地址,该字段内容由接收邮件的SMTP服务器填写,接收邮件的SMTP服务器从邮件发送程序发出的mail form命令中获得该字段内容。
—  Received  该字段的基本格式为Received from A by B for C,其中A为发送方,B为接收方,C为收件人的邮箱地址。该字段的内容由接收邮件的SMTP服务器填写,常常被用来追踪邮件传输的路线和分析邮件的来源,例如,从例程3-1中的各个Received字段中,可以知道这封邮件的传输路径:从IP地址为【218.246.5.151】的机器上发出→【smtp.sina.com.cn】→【sohumx139.sohu.com】→【[email protected]】。例程3-1中的第6行的“Received: (qmail 49221 invoked from network)”是sina的SMTP服务器内部调用的一个邮件发送模块添加的,它说明sina的SMTP服务器接收到邮件后再通过这个邮件发送模块将邮件转发出去。显然,通过分析一封邮件的源内容,是可以知道发件人的IP地址的。 


图3.6
从图3.6中可以看出,如果要在邮件中要添加附件,就必须将整封邮件的MIME类型定义为multipart/mixed;如果要在HTML格式的正文中引用内嵌资源,那就要定义multipart/related类型的MIME消息;如果普通文本内容与HTML文本内容共存,那就要定义multipart/alternative类型的MIME消息。
注意:如果整封邮件中只有普通文本内容与HTML文本内容,那么整封邮件的MIME类型则应定义为multipart/ alternative;如果整封邮件中包含有HTML文本内容和内嵌资源,但不包含附件,那么整封邮件的MIME类型则应定义为multipart/related。
在Content-type头字段中除了可以定义消息体的MIME类型外,还可以在MIME类型后面包含相应的属性,属性以“属性名=属性值”的形式出现,属性与MIME类型之间采用分号(;)分隔,如下所示:
Content-Type:multipart/mixed;boundary="----=_NextPart_000_0050_01C" 常用的属性如表3.1所示。 表3.1
主 类 型        属 性 名        说    明
 text               charset         用于说明文本内容的字符集编码
image           name           用于说明图片文件的文件名

application   name           用于说明应用程序的文件名
multipart      boundary      用于定义MIME消息之间的分隔符
 
2:  其他头字段
除了Content-Type头字段之外,MIME协议中还定义Content- Transfer-Encoding、Content-Disposition、Content-ID、Content-Location、Content-Base等几个重要的头字段,这几个头字段需要与Content-type头字段配合使用,它们的作用如下:
—  Content-Transfer-Encoding头字段
Content-Transfer-Encoding头字段用于指定MIME消息体中的内容所采用的邮件编码方式,详细细节请参看3.4节的讲解。
—  Content-Disposition头字段
Content-Disposition头字段用于指定邮件阅读程序处理数据内容的方式,有inline和attachment两种标准方式,inline表示直接处理,而attachment表示当做附件处理。如果将Content-Disposition设置为attachment,在其后还可以指定filename属性,如下所示:
Content-Disposition: attachment; filename="1.bmp"
上面的MIME头字段表示MIME消息体的内容为邮件附件,附件名"1.bmp"。 —  Content-ID头字段
Content-ID头字段用于为“multipart/related”组合消息中的内嵌资源指定一个唯一标识号,在HTML格式的正文中可以使用这个唯一标识号来引用该内嵌资源。例如,假设将一个表示内嵌图片的MIME消息的Content-ID头字段设置为如下形式:
Content-ID: it315logo_gif
那么,在HTML正文中就需要使用如下HTML语句来引用该图片资源:
 
注意,在引用Content-ID头字段标识的内嵌资源时,要在资源的唯一标识号前面加上“cid:”,以说明要采用唯一标识号对资源进行引用。
—  Content-Location头字段
Content-Location头字段用于为内嵌资源设置一个URI地址,这个URI地址可以是绝对或相对的。当使用Content- Location头字段为一个内嵌资源指定一个URI地址后,在HTML格式的正文中也可以使用这个URI来引用该内嵌资源。例如,假设将一个表示内嵌图片的MIME消息的Content- Location头字段设置为如下形式:

Content-Location:http://www.it315.org/images/it315logo.gif 
那么,在HTML正文中就可以使用如下HTML语句来引用该图片资源:
  —  Content-Base头字段
Content-Base头字段用于为内嵌资源设置一个基准路径,只有这样,Content-Location头字段中设置的URI才可以采用相对地址。例如,假设将一个表示内嵌图片的MIME消息的Content-Base和Content-Location头字段设置为如下形式:
Content-Base: http://www.it315.org/images/  Content-Location: it315logo.gif  
那么,内嵌资源的完整路径就是Content-Base头字段设置的基准路径与Content-Location头字段设置的相对路径相加的结果,在HTML正文中就可以使用如下HTML语句来引用该图片资源:
  
MIME邮件的编码方式
由于每个ASCII码字符只占用一个字节(8个bit位),且最高bit位总为0,即ASCII码字符中的有真正意义的信息只是后面的7个低bit位,而传统的SMTP协议又是基于ASCII码字符设计的,因此,一些基于传统SMTP协议设计的SMTP服务器在处理邮件内容时只取出每个字节中的7个低bit位进行处理,而将最高bit位忽略不计。显然,这样的SMTP服务器在处理包含有非ASCII码字符的邮件内容时,会出现严重的问题,这就限制了邮件中只能出现英文的ASCII码字符,而不能出现中文字符或二进制数据。
为了能够在邮件内容中包含中文、图像或声音等非ASCII字符的数据,人们想到了采用某种编码方式将非ASCII字符的数据转换成可打印的ASCII字符后再发送,邮件阅读程序则按照相应的解码方式从邮件中还原出原始数据即可,比较常用的两种邮件编码方式为BASE64和Quoted-printable。后来的扩展SMTP协议允许直接在邮件中传递二进制数据,而不用对它们进行邮件编码,人们将这种没有进行邮件编码的二进制数据的邮件内容称为8bit编码,为了与此相区别,人们将没有进行邮件编码的纯ASCII码字符的邮件称为7bit编码。MIME消息体的邮件编码方式通过MIME消息头中的Content- Transfer- Encoding头字段指定,每种邮件编码方式的介绍如下:
—  7Bit
指消息体内容全部是没有经过编码的ASCII字符。 —  8Bit
指消息体内容是没有经过编码的原始数据,且其中包含有非ASCII字符的数据。现在的邮件服务器基本上都支持8Bit编码,使用支持8Bit编码的邮件服务器可以简化邮件的处理过程。
—  BASE64
Base64是将二进制数据转换成可打印的ASCII字符的一种最常见的编码方式,它的基本原理是将一组连续的字节
数据按6个bit位进行分组,然后对每组数据用一个ASCII字符来表示。6个bit位最多能表示26
=64个数值,因此可以使用64个ASCII字符来对应这64个数值,这64个ASCII字符为:
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
其中每个字符表示的数值就是该字符在上面的排列中的索引号,索引号从0开始编号。假设在内存中有如下三个连续的字节数据:
[0110,0001] [0110,0010] [0110,0011]
将它们按6个bit位进行分组后的形式如下:
[0110,00] [01,0110,] [0010,01] [10,0011]
分组后得到了四组数据,每组数据对应的十进制数值分别为24、22、9、35,它们分别对应Y、W、J、j这四个字符,所以,对[0110,0001] [0110,0010] [0110,0011]这三个字节的数据进行BASE64编码后的结果是“YWJj”。
BASE64编码要求把3个8位字节(即24个bit)的数据转化为4个6位字节(也是24个bit)的数据,如果原来的8位字节数据的字节个数不能被3整除,其余数只能是1或2,那么如何对余下的1个或2个8位字节数据进行处理呢?对于这种情况,仍然按6个bit位对剩余的字节进行分组,在最后不够6个bit位的内容后面添加几个为0的bit位来凑成6个bit位,例如,如果最后剩下的一个8位字节的内容如下:
[0110,0001]
对它进行分组后的结果如下: [0110,00] [01,0000]

其中用黑斜体标识的0为填充的bit位,所以,最后剩下的这个字节的BASE64编码结果为“YQ”。BASE64编码还有规定,如果编码后的整个结果文本的字符个数不是4的整数倍,那么需要在最后填充“=”字符来凑成4的倍数,所以,在最后这个字节编码的结果后面还要添加两个“=”字符,即“YQ==”。显然,如果最后剩下两个8位字节的内容,它可以被编码成三个字符,最后还需要添加一个“=”字符。对一大段数据进行BASE64编码时,可以在编码结果中的适当位置加入回车换行,MIME规范建议BASE64编码结果中的每行最多76个字符。
—  Quoted-printable
Quoted-printable也是一种将二进制数据转换成可打印的ASCII字符的编码方式,它对ASCII字符不进行转换,只对非ASCII字符的数据进行编码转化。每个非ASCII字符的字节数据,都被转换成一个"="号后跟这个字节的十六进制数据,例如,“ab中国”的Quoted-printable编码结果为“ab=d6=d0=b9= fa”。显然,由于"="号在Quoted-printable编码中具有的特殊意义,所以,原始数据中的"="号字符也需要进行编码转换,用“=3d”表示。
对一大段数据进行Quoted-printable编码时,可以在编码结果中的适当位置加入回车换行,在回车换行前需要额外再加入一个“=”字符,以表示后面的换行是因编码而造成的软回车,而非原始数据中原有的回车换行。例如,对于下面一段Quoted-printable编码后的数据:
=D5=E2=CA=C7=CD=A8=D0=C5=B5=C4=B3=CC=D0= =F2, =C7=EB=D6=B8=BD=CC!
在第一行末尾的“=”字符和换行,都是由于编码后生成的。 MIME实例分析
了解MIME协议的基本组织结构后,下面用Outlook Express撰写出一封显示效果如图3.4所示的电子邮件,然后分析该邮件的源文件,以便读者更加深入地了解MIME协议。
 
启动Outlook Express,单击工具栏中的“创建邮件”按钮,在打开的“新邮件”对话框中输入收件人地址、
主题和邮件正文,然后选中邮件正文,单击编辑窗口的工具栏上的“》”按钮,在弹出的菜单栏中单击表示超链接的图标,如图3.7所示。
 
图3.7
 
在打开的“超级链接”对话框中输入如图3.8所示的内容,然后单击“OK”按纽。
 
图3.8
 再次单击编辑窗口的工具栏上的“》”按钮,在打开的如图3.7所示的菜单栏中单击表示图片的图标,在打开
的“图片”对话框中单击“浏览”按钮,然后通过打开的对话框选择一个图片文件,结果如图3.9所示。
 
单击图3.9中的“OK”按钮,结果如图3.10所示。
 
单击图3.10所示窗口中的“插入”à“文件附件”菜单项,在打开的“插入附件”对话框窗口中选择需要发送的附件
单击“附件”按钮,插入所选中的附件。
 
单击图3.10所示窗口中的“文件”à“保存”菜单项,在Outlook Express主窗口的“草稿”目录中就可以看到这封邮件
 
将这封邮件从“草稿”目录中移动到“发件箱”目录中,接着按照3.1节中讲解的查看邮件源文件步骤,打开
刚才撰写的这封邮件的源文件。或者在“草稿”目录中选中这封邮件,将它另存为一个eml文件,再用任意一种文本编辑程序打开这个eml文件,这样也可以查看到刚才撰写的这封邮件的源文件。为了便于读者看清邮件内容的组织结构关系,笔者专门为此画了一个描述邮件中的各个MIME消息分隔符的层次关系的示意图,如图3.13所示。
从图3.13中可以看出,在MIME组合消息的消息体中的每个消息单元都要以一个分割符开始,在组合消息的消息体结束时还需要用一个结束分割符。MIME消息中的分隔符的层次关系与一篇文章中的标题之间的层次关系非常相似,只是在每个组合消息结束时还要增加一个结束“标题”。为了便于讲解,笔者对刚才撰写的邮件源文件内容进行了适当修改,并添加了相应的注释,如下所示:
 
图3.13
1:From: "it315"  
2:To: 

3:Subject: =?gb2312?B?TUlNRdCt0unLtcP308q8/g==?=

4:Date: Thu, 1 Dec 2005 20:46:53 +0800

5:MIME-Version: 1.0

6:Content-Type: multipart/mixed;//定义邮件体类型为mixed

7: boundary="----=_NextPart_000_0050_01C"//定义整个邮件内容的分隔符

8:X-Priority: 3

9:X-MSMail-Priority: Normal
10:X-Mailer: Microsoft Outlook Express 6.00.2900.2670

11:X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2670

12:

13:This is a multi-part message in MIME format.//邮件注释

14:

//整个邮件内容的第一部分(即邮件正文)的开始标记

15:------=_NextPart_000_0050_01C 

16:Content-Type: multipart/related;

17: type="multipart/alternative";

18: boundary="----=_NextPart_001_0051_01C"//定义邮件正文内部的分隔符

19:

    //邮件正文内部的第一部分的开始标记

20:------=_NextPart_001_0051_01C

 21:Content-Type: multipart/alternative;

        //定义邮件正文内部的第一部分的内部分隔符

22: boundary="----=_NextPart_002_0052_01C"

23:

        //邮件正文内部的第一部分的第一部分的开始标记

24:------=_NextPart_002_0052_01C 

25:Content-Type: text/plain;

26: charset="gb2312"

27:Content-Transfer-Encoding: base64

28:

29:u7bTrbTzvNK3w87KztLDx7XEzfjVvg0KDQog

                    //经BASE64编码后的文本格式的邮件正文

30:

        //邮件正文内部的第一部分的第二部分的开始标记

31:------=_NextPart_002_0052_01C 

32:Content-Type: text/html;

33: charset="gb2312"

34:Content-Transfer-Encoding: base64

35:

36:PCFET0NUWVBFIEhUTUwgUFVC......//经BASE64编码后的HTML格式的邮件正文 

37:

        //邮件正文内部的第一部分的的结束标记

38:------=_NextPart_002_0052_01C-- 

39:

    //邮件正文内部的第二部分(HTML中的内嵌资源)的开始标记

40:------=_NextPart_001_0051_01C 

41:Content-Type: image/gif;

42: name="logo.gif"

43:Content-Transfer-Encoding: base64

44:Content-ID: <004f01c5f675$4e210300$b501a8c0@zxx>

45:

46:R0lGODlh/ABGAOYAADdhmaekZjxsq0N4vHik

47:xK27z9CtCJWpxCZEa3aczejr7......

                    // HTML中内嵌的经BASE64编码后的图片数据

48:

    //邮件正文的结束标记

49:------=_NextPart_001_0051_01C—

50:

//整个邮件内容的第二部分(第一个附件)的开始标记

51:------=_NextPart_000_0050_01C 

52:Content-Type: application/x-msdownload;

53: name="daemon.exe"

54:Content-Transfer-Encoding: base64

55:Content-Disposition: attachment;//声明内容类型为附件

56: filename="daemon.exe"

57:

58:TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA

 59:AAAA0AAAAA4fug4AtAnNAAAAAA......// 第一个邮件附件内容

60:

//整个邮件内容的第三部分(第二个附件)的开始标记

61:------=_NextPart_000_0050_01C 

62:Content-Type: audio/wav;

63: name="sndrec.wav"

64:Content-Transfer-Encoding: base64

65:Content-Disposition: attachment;//声明内容类型为附件

66: filename="sndrec.wav"

67:

68:UklGRjIAAABXQVZFZm10IBIAAAABAAEAIlYAACJWAAABAAgAKABmYWN0

69:AA==AAAAAAAAAAAAAAAAAACCC......//第二个邮件附件内容

70:

//整个邮件内容的结束标记

71:------=_NextPart_000_0050_01C5F6B8.5C492500--


源文件中第1~11行为邮件的邮件头,第15~71行为邮件体。邮件体中的15~49行为邮件正文,第51-59行为邮件的第一个附件,第61~71行为第二个附件。读者只要对照图3.13来阅读,就很容易看出这个邮件源文件内容的组织结构。关于上述的邮件源文件内容,还需要做出如下三点补充解释:
(1)每个MIME组合消息的Content-type头字段中的boundary属性用于定义其中嵌套的各个MIME消息之间的分隔符,如源文件中的第7行、第18行和第22行等。在各个MIME组合消息内部的起始分隔符是在该MIME组合消息的Content-type头字段中的boundary属性值前面增加了两个减号(-)字符而形成的,如源文件中的第15行、第20行和第24行等;而每个MIME组合消息的结束分隔符则是在其起始分隔符的后面又添加两个减号(-)字符而形成的,如源文件中的第38行、第49行和第71行。
(2)在每个MIME组合消息的消息体之前(即第一个开始分隔符之前),可以有一些附加的文本行,这些文本行相当于MIME消息的注释,在解码时将被忽略,如源文件的13行。
(3)源文件中的第44行使用Content-ID头字段为内嵌的图片资源指定了一个唯一标识号,在HTML格式的正文中需要使用这个唯一标识号来引用相应的内嵌资源,其引用语句为 004f01c5f675$4e210300$b501a8c0@zxx">,但是由于整个HTML正文部分采用了Base64编码,所以在源文件的HTML正文部分无法看到原始的引用语句。
多学两招  邮件传播病毒的原理

MIME协议其实就是一种邮件内容的组织协议,支持MIME协议的邮件阅读程序将根据MIME消息头中定义的MIME类型,调用相应的解析程序来处理消息体中的数据。例如MIME消息头中定义为邮件附件时,邮件阅读程序会提示用户保存消息体中的数据,如果定义为图像文件时,邮件阅读程序则把消息体中的数据作为一个图像文件自动打开。
由于邮件数据通常是经过BASE64编码后的ASCII码数据,邮件阅读程序只能通过分析数据的MIME消息头来获知数据类型,无法通过分析数据本身来获知数据的类型,因此,一些病毒制造者就可以把病毒程序进行BASE64编码后,再附加在邮件的MIME消息体中,然后在MIME消息头中将其MIME类型定义为图片或声音等类型,而文件名的扩展名却为.exe。这样,当邮件阅读程序解码带有病毒程序的MIME消息体后,将执行解码后得到的病毒程序。前些年曾经在全球范围内流行的Nimda病毒,就是通过这种方式进行传播的,其示意源代码如下:
MIME-Version: 1.0
Content-Type: multipart/related;//声明所包含内容为内嵌资源  type="multipart/alternative";
 boundary="====_ABC1234567890DEF_====" --====_ABC1234567890DEF_==== Content-Type: multipart/alternative;
 boundary="====_ABC0987654321DEF_====" --====_ABC0987654321DEF_==== Content-Type: text/html;   charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
  
--====_ABC0987654321DEF_====-- --====_ABC1234567890DEF_====
Content-Type: audio/x-wav; name="readme.exe"                     //把exe文件定义成一个wav文件
Content-Transfer-Encoding: base64 Content-ID: 
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAA……//经BASE64编码后的病毒源代码
--====_ABC1234567890DEF_====
这封邮件传播病毒的运行原理非常简单,如上面的源文件中的黑体字部分及相应注释所示,它就是将一个可执行文件(readme.exe)作为一个audio/x-wav类型的声音文件嵌入到HTML页面中。由于最初的Outlook Express程序没有检查MIME消息的Content-Type头字段定义的MIME类型与其中的name属性指定的文件扩展名是否匹配,于是导致用户打开邮件时将执行解码后的readme.exe程序,从而感染上了病毒。这个病毒程序又利用Outlook Express中的地址簿向别人发送带毒的邮件,这样一传十,十传百,Nimda蠕虫就大行其道了。

你可能感兴趣的:(Email)