相关学习资料
http://baike.baidu.com/link?url=77B3BYIuVsB3MpK1nOQXI-JbS-AP5MvREzSnnedU7F9_G8l_Kvbkt_O2gKqFw7vm http://www.rfc-editor.org/rfc/rfc1035.txt http://www.rfc-editor.org/rfc/rfc3596.txt http://www.rfc-editor.org/rfc/rfc2782.txt http://www.rfc-editor.org/rfc/rfc3403.txt http://zhumeng8337797.blog.163.com/blog/static/10076891420110694312990/ tcp-ip详解卷1:协议.pdf http://wenku.baidu.com/link?url=gIwRwFkKbwB-x45_xVShR10VII_r3nchf0wYTPwelAu5CrS7T5N3zxoBtImYD8i3-mwJhgi5ix-XWMcXkI0uNg6gJCgCihZkRwVydqyFWF3 ftp://ftp.rs.internic.net/domain/ http://www.laojia1987.cn/show.asp?id=756 http://technet.microsoft.com/zh-cn/cc779148(WS.10).aspx http://blog.csdn.net/lastsweetop/article/details/5692312 http://blog.sina.com.cn/s/blog_6441e0640100scj2.html http://zhumeng8337797.blog.163.com/blog/static/100768914201082910759244/
目录
1. DNS简介 2. DNS查询方式 3. 和DNS有关的安全风险
1. DNS简介
DNS是指:域名系统(Domain Name System)。在Internet上域名与IP地址之间是一一对应的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转换工作称为域名解析,域名解析需要由专门的域名解析系统来完成,DNS就是进行域名解析的系统。
DNS协议运行在UDP协议之上,使用端口号53。在传输层TCP提供端到端可靠的服务,在UDP端提供尽力交付的服务。其控制端口作用于UDP端口53。
0x1: DNS记录类型
DNS系统中,常见的资源记录类型有:
1. 主机记录(A记录) RFC 1035定义,A记录是用于名称解析的重要记录,它将特定的主机名映射到对应主机的IP地址上。过A记录,大家可以设置自己的不同域名转到不同的IP上去,如: 1) www.dns.la 转到IP 116.255.202.1 2) web.dns.la 转到IP 116.255.202.11 3) mail.dns.la 转到IP 116.255.202.111 2. 别名记录(CNAME记录) RFC 1035定义,CNAME记录用于将某个别名指向到某个A记录上,这样就不需要再为某个新名字另外创建一条新的A记录。对于CNAME的原理要注意和URL转发进行区分: 1) url转发可以转发到某一个目录下,甚至某一个文件上 2) 而cname是不可以,这就是url转发和cname的主要区别所在 3. MX记录(Mail Exchange) 邮件交换记录,用户可以将该域名下的邮件服务器指向到自己的mail server上,然后即可自行操作控制所有的邮箱设置。要注意的是邮箱DNS解析和HTTP DNS解析是独立的,如果你设置A记录 是指向123.12.123.123,而MX记录你设置是指向222.22.222.222,那么你的DNS服务器接收到别人的邮件路 由请求时就将会将它的请求解释到222.22.222.222上去!而别人访问你的网页 的时候仍然是访问123.12.123.123。 4. NS(Name Server) 域名服务器记录,用来指定该域名由哪个DNS服务器来进行解析,可以把一个域名的不同二级域名分别指向到不同的DNS系统来解析 5. IPv6主机语录(AAAA记录) RFC 3596定义,与A记录对应,用于将特定的主机名映射到一个主机的IPv6地址。 6. 服务位置记录(SRV记录) RFC 2782定义,用于定义提供特定服务的服务器的位置,如主机(hostname),端口(port number)等。 7. NAPTR记录 RFC 3403定义,它提供了正则表达式方式去映射一个域名。NAPTR记录非常著名的一个应用是用于ENUM查询。
0x2: 域名系统分类
DNS的名字空间和Unix的文件系统相似,也具有层次结构。每个结点有 一个至多63个字符长的标识。这颗树的树根是没有任何标识的特殊结点(即全球根DNS)。命名标识中一律不区分大写和小写。命名树上任何一个结点的域名就 是将从该结点到最高层的域名串连起来,中间使用一个点"."分隔这些域名,域名树中的每个结点必须有一个唯一的域名,但域名树中的不同结点可使用相同的标 识。
以点"."结尾的域名称为绝对域名或完全合格的域名FQDN(Full Qualified Domain Name),例如sun.tuc.noao.edu.
如果一个域名不以点结尾,则认为该域名是不完全的。
DNS根域名服务器 全球共有13台根"逻辑域名服务器"。这13台逻辑根域名服务器中名字分别为"A"至"M",真实的根服务器在2014年1月25日的数据为386台,分布于全球各大洲。 之所以是13台,这要从DNS协议(域名解析协议)说起。DNS协议使用了端口上的UDP和TCP协议,UDP通常用于查询和响应,TCP用于主服务器和从服务器之间的传送。由于在所有UDP查询和响应中 能保证正常工作的最大长度是512字节,512字节限制了根服务器的数量和名字。要让所有的根服务器数据能包含在一个512字节的UDP包中,根服务器只能限制在13个,而且每个服务器要使用字 母表中的单个字母命名,这也是根服务器是从A~M命名的原因。 在根域名服务器中虽然没有每个域名的具体信息,但储存了负责每个域(如COM、NET、ORG等)的解析的域名服务器的地址信息,如同通过北京电信你问不到广州市某单位的电话号码,但是北京电 信可以告诉你去查020114。世界上所有互联网访问者的浏览器的将域名转化为IP地址的请求(浏览器必须知道数字化的IP地址才能访问网站)理论上都要经过根服务器的指引后去该域名的权威域 名服务器(authoritative name server),当然现实中提供接入服务的ISP的"缓存域名服务器"上可能已经有了这个对应关系(域名到IP地址)的缓存。 1. 顶级域名 顶级域名被分为三个部分 1) arpa是一个用作地址到名字转换的特殊域(即反过来用IP来解析域名) 2) 7个3字符长的普通域。有些书也将这些域称为组织域 2.1) com: 商业组织 2.2) edu: 教育机构 2.3) gov: 政府部门 2.4) int: 国际组织 2.5) mil: 军事网点 2.6) net: 网络 2.7) org: 其他组织 3) 所有2字符长的域均是基于ISO3166中定义的国家代码,这些域被称为国家域,或地理域。,例如 3.1) hk 3.2) us 2. 二级域名 一个独立管理的DNS子树称为一个区域(zone)。一个常见的区域是一个二级域,如noao.edu。许多二级域将它们的区域划分成更小的区域。例如,大学可能根据不同的系来划分区域,公司可能 根据不同的部门来划分区域。
0x3: DNS协议格式
1. 标识 一个4字节16bit的字段,Transaction ID,表明当前DNS请求的序号 2. 标志 16bit的标志字段 1) QR 1bit字段 1.1) 0表示查询报文 1.2) 1表示响应报文。 2) opcode 4bit字段 2.1) 通常值为0(标准查询) 2.2) 其他值为1(反向查询) 2.3) 2(服务器状态请求) 3) A 1bit标志,表示"授权回答(authoritative answer)"。该名字服务器是授权于该域的。 4) TC 1bit字段,表示"可截断的(truncated)"。使用UDP时,它表示当应答的总长度超过512字节时,只返回前512个字节 5) RD 1bit字段,表示"期望递归(recursion desired)"。该比特能在一个查询中设置,并在响应中返回。这个标志告诉名字服务器必须处理这个查询,也称为一个递归查询。如果该位为0,且 被请求的名字服务器没有一个授权回答,它就返回一个能解答该查询的其他名字服务器列表,这称为迭代查询。 6) RA 1bit字段,表示"可用递归"。如果名字服务器支持递归查询,则在响应中将该比特设置为1。大多数名字服务器都提供递归查询,除了某些根服务器 7) 随后3bit必须为0 8) rcode 4bit的返回码字段。通常的值为: 8.1) 0(没有差错) 8.2) 3(名字差错) 名字差错只有从一个授权名字服务器上返回,它表示在查询中制定的域名不存在。 3. 问题数 查询问题的数量,对于请求报文,通常为1 4. 资源记录数 返回的资源记录数 5. 授权资源记录数 返回的授权资源记录数 6. 额外资源记录数 返回的额外资源记录数 7. 查询问题 注意,一个DNS数据报中可能会有多个查询问题,这也是为什么在之前的"问题数"等字段会有大于1数字的原因。每个"查询问题格式"都相同,如下: 1) 查询名 查询名是要查找的名字,它是一个或多个字符串的序列。每个字符串以首字节的计数值来说明随后标识符的字节长度,每个名字以最后字节为0结束,字符串之间以"."分隔,例如 www.baidu.com中www、baidu、com都是字符串,长度为0的字符串是根标识符。计数字节的值必须是0~63的数,因为单个字符串的最大长度仅为63 2) 查询类型 2.1) A: IP地址 2.2) NS: 名字服务器 2.3) CNAME: 规范名称 2.4) PTR: 指针记录,即IP到域名的反向查询 2.5) HINFO: 主机信息 2.6) MX: 邮件交换记录 2.7) AXFR: 区域传送请求 2.8) */ANY: 对所有记录的请求 最常用的查询类型是A类型,表示期望获得查询名的IP地址。一个PTR查询则请求获得一个IP地址对应的域名。这是一个指针查询 3) 查询类 查询类通常是1,指互联网地址(某些站点也支持其他非IP地址) 8. 回答(只在DNS响应数据报中存在) 1) 域名 客户端请求解析的域名,和请求报文中的问题中的查询名一样 2) 类型 2.1) A: IP地址 2.2) NS: 名字服务器 2.3) CNAME: 规范名称 2.4) PTR: 指针记录,即IP到域名的反向查询 2.5) HINFO: 主机信息 2.6) MX: 邮件交换记录 2.7) AXFR: 区域传送请求 2.8) */ANY: 对所有记录的请求 3) 类 通常是1,指互联网地址(某些站点也支持其他非IP地址) 4) 生存时间 客户程序保留该资源记录的秒数。资源记录通常的生存时间值为2天。 5) 资源数据长度 资源数据的数量。该数据的格式依赖于类型字段的值 5.1) 对于类型1(A记录)资源数据是4字节的IP地址 5.2) 对于类型CNAME资源数据是域名字符串长度 9. 授权(只在DNS响应数据报中存在) 10. 额外信息(只在DNS响应数据报中存在)
2. DNS查询方式
0x1: 递归查询和迭代查询的区别
1.递归查询: 一般客户机和首选DNS服务器(宽带连接是设置的默认DNS)之间属递归查询,即当客户机向DNS服务器发出请求后,若DNS服务器本身不能解析,则会向另外的DNS服务器发出查询请求,得到最终结 果后转交给客户机 2.迭代查询(反复查询): 一般DNS服务器之间属迭代查询,如:若DNS2不能响应DNS1的请求,则它会将DNS3的IP给DNS2,以便其再向DNS3发出请求
下图为一次完整的DNS查询过程,包括客户端和本地DNS的递归查询、以及本地DNS和上机DNS之间的迭代查询
1. 网络客户端准备访问www.163.com这个域名,浏览器准备进行DNS解析以获取对应的IP 2. 网络客户端将DNS解析请求发送到"本地DNS服务器"后,就进行等待状态,因为对于网络客户端和本地DNS服务器来说它们是递归关系,本地DNS服务器最终一定会把结果返回给网络客户端 (IP、或者是不存在) 3. 本地DNS服务器接收到DNS解析请求后,开始和上机DNS服务之间进行迭代查询 1) 向全球根域名DNS服务器发送解析请求,根DNS根据请求中的顶级域名,返回该域名对应的顶级DNS服务器(.com)IP 2) 本地DNS服务器接着继续向".com"DNS服务器发送解析请求,".com"服务器查询自己的DNS缓存,返回.163.com域名服务器,要求本地DNS继续迭代查询 3) 本地DNS继续向".163.com"进行请求,并得到解析IP: 1.1.1.1 4. 本地DNS服务器将最终的解析结果返回给网络客户端,完成一次DNS解析
0x2: DNS解析抓包实验过程
1. DNS普通查询
2. DNS查询响应
可以看到,www.baidu.com是一个CNAME地址,是百度为了用户好记而登记的域名
3. Zone Transfer(AXFR)请求
4. PTR域名反向查询
3. 和DNS有关的安全风险
DNS是互联网的一个基础设施,针对DNS的安全风险大致有如下几个:
1. 防火墙不会限制对DNS的访问 这导致攻击者可能会借助DNS通道进行数据库带外信道注入、或者DNS隐藏隧道等攻击 2.DNS可以泄漏内部的网络拓朴结构 即DNS Zone Transfer攻击 3.DNS的本身性能问题 安全的一个重要标志是可用性。对于DNS服务器而言这点尤其重要,在现实生活中经常定义攻击者入侵系统获取数据为一个安全问题,但是对于DNS服务器来说,遭到了拒绝服务攻击则是一件更严 重的问题。失去了DNS服务器的话,任何在internet网络上的人将不能够再使用域名找到你的服务器,不可想像让普通的网民们使用202.106.184.200来代替www.sina.com.cn使用。更严重的 是,没有了DNS的服务,所有的邮件发送都将失败,而你的内部网络将由于解析域名的失败而失去和外部网络的联系。 注册了一个域名以后,可以最大为你的域名设置6个DNS服务器名。 例如,microsoft.com公司的就为自己设置了五个DNS服务器来解析自己的域名: Name Servers: DNS4.CP.MSFT.NET 207.46.138.11 DNS5.CP.MSFT.NET 207.46.138.12 Z1.MSFT.AKADNS.COM 216.32.118.104 Z7.MSFT.AKADNS.COM 213.161.66.158 DNS1.TK.MSFT.NET 207.46.232.37 这样,即使这三个中的两个停止了工作,但是了,仍然可以有一个会对外提供服务,而对于广大的用户而言,当出现这种多个DNS服务器停止服务带来的唯一的影响就是查询域名的时候会延迟,因 为它需要一个一个的去查询,直到找到最后的一个为止。而上面这一个步骤也是你应付恶意攻击者对DNS服务器进行拒绝服务攻击的一个保护手段。
0x1: DNS Zone Transfer
Zone Transfer漏洞可以被渗透测试人员用来进行内网信息搜集、拓朴获取等目的,是内网横向渗透、信息搜集的一个很重要的手段。
一个名字服务器(NS)负责一个或多个区域(zone)。一个区域的管理者必须为该区域提供一个主名字服务器(master)和至少一个辅助名字服务器(slave)。主、辅名字服务器必须是独立和冗余的,以便当某个名字服务器发生故障时不会影响该区域的名字服务。
主、辅名字服务器的主要区别在于主名字服务器从磁盘文件中调入该区域的所有信息,而辅名字服务器则从主服务器调入所有信息。我们将辅名字服务器从主服务器调入信息称为区域传送(zone transfer)
了解了DNS Zone Transfer的基本概念之后,我们来一起学习一下dig、nslookup这2个命令,看看我们都能利用Zone Transfer获得哪些信息
nslookup命令解释
在win上,nslookup会根据ipconfig连接后的默认DNS去找到所要使用的local DNS server
在linux上,nslookup会根据/etc/resolv.conf的内容去找到所要使用的local DNS server
C:\Users\LittleHann>nslookup 默认服务器: FJ-DNS.fz.fj.cn Address: 218.85.157.99 > ? 命令: (标识符以大写表示,[] 表示可选) 1. NAME 打印有关使用默认DNS服务器(系统默认的DNS服务器)的主机/域NAME的信息,例如www.baidu.com 2. set OPTION 设置选项,OPTION可选项如下,[no]代表默认关闭 1) all: 打印选项、当前服务器和主机 2) [no]debug: 打印调试信息 3) [no]d2: 打印详细的调试信息 4) [no]defname: 将域名附加到每个查询 5) [no]recurse: 询问查询的递归应答 6) [no]search: 使用域搜索列表 7) [no]vc: 始终使用虚拟电路 8) domain=NAME: 将默认域名设置为 NAME 9) srchlist=N1[/N2/.../N6]: 将域设置为 N1,并将搜索列表设置为 N1、N2 等 10) root=NAME: 将根服务器设置为 NAME 11) retry=X: 将重试次数设置为 X 12) timeout=X: 将初始超时间隔设置为 X 秒 13) type=X: 设置查询类型(如 A、AAAA、A+AAAA、ANY、CNAME、MX、NS、PTR、SOA 和 SRV) set type=ptr 本来要由IP反查 domain name时, 在直接打IP就行了, 但如果已经下了type=any的话, 要由IP反查时就没那么方便了, 此时IP 4个数字要倒着写, 最后还要加上in-addr.arpa. 以查140.116.72.219 为例, 要输入的就是219.72.116.140.in-addr.arpa 14) querytype=X: 与类型相同 15) class=X: 设置查询类(如IN(Internet)和ANY) 16) [no]msxfr: 使用MS快速区域传送 17) ixfrver=X: 用于IXFR传送请求的当前版本 3. server NAME 将默认服务器设置为NAME(例如8.8.8.8),同时对NAME进行解析 很多情况下,我们的DNS查询并不会被发送到跟DNS服务器上,而是由DNS Cache服务器进行返回,为了获得最新的数据,我们需要手动去修改"默认本地DNS"从而获得最新的DNS数据 4. lserver NAME 将默认服务器设置为NAME,使用初始服务器 5. root 将当前默认服务器设置为根服务器 6. ls [opt] DOMAIN [> FILE] 列出DOMAIN中的地址(可选: 输出到文件 FILE) 这个命令是要求name server将其负责的zone内容show出来, 这个动作相当于name server的整份记录从server端传回给nslookup这个程序, 这种传回整个zone的动作叫作zone transfer 1) -a 列出规范名称和别名 2) -d 列出所有记录 3) -t TYPE 列出给定 RFC 记录类型 3.1) A 3.2) CNAME 3.3) MX 3.4) NS 3.5) PTR等的记录 7. view FILE 对 'ls' 输出文件排序,并使用 pg 查看 8. exit 退出程序
dig命令解释
Dig是linux中的域名解析工具,Dig是domain information groper的缩写
Usage: dig [@global-server] [domain] [q-type] [q-class] {q-opt} {global-d-opt} host [@local-server] {local-d-opt} [ host [@local-server] {local-d-opt} [...]] 1. domain 要查询的域名 2. q-class 1) IN(默认) 2) HS 3) CH 3. q-type 1) A(默认) 2) ANY 3) MX 4) NS 5) SOA 6) HINFO 7) AXFR 8) TXT 4. q-opt 1) -x dot-notation (shortcut for reverse lookups) 2) -i (use IP6.INT for IPv6 reverse lookups) 3) -f filename (batch mode) 4) -b address[#port] (bind to source address/port) 5) -p port (specify port number) 6) -q name (specify query name) 7) -t type (specify query type) 8) -c class (specify query class) 9) -k keyfile (specify tsig key file) 10) -y [hmac:]name:key (specify named base64 tsig key) 11) -4 (use IPv4 query transport only) 12) -6 (use IPv6 query transport only) 13) -m (enable memory usage debugging) 5. d-opt 格式为: +keyword[=value],keyword的可选值如下 1) +[no]vc (TCP mode) 2) +[no]tcp (TCP mode, alternate syntax) 3) +time=### (Set query timeout) [5] 4) +tries=### (Set number of UDP attempts) [3] 5) +retry=### (Set number of UDP retries) [2] 6) +domain=### (Set default domainname) 7) +bufsize=### (Set EDNS0 Max UDP packet size) 8) +ndots=### (Set NDOTS value) 9) +edns=### (Set EDNS version) 10) +[no]search (Set whether to use searchlist) 给你的公众服务器提供清楚的统计信息 11) +[no]showsearch (Search with intermediate results) 12) +[no]defname (Ditto) 13) +[no]recurse (Recursive mode) 14) +[no]ignore (Don't revert to TCP for TC responses.) 15) +[no]fail (Don't try next server on SERVFAIL) 16) +[no]besteffort (Try to parse even illegal messages) 17) +[no]aaonly (Set AA flag in query (+[no]aaflag)) 18) +[no]adflag (Set AD flag in query) 19) +[no]cdflag (Set CD flag in query) 20) +[no]cl (Control display of class in records) 21) +[no]cmd (Control display of command line) 22) +[no]comments (Control display of comment lines) 23) +[no]question (Control display of question) 24) +[no]answer (Control display of answer) 查询的结果 25) +[no]authority (Control display of authority) 告诉我们哪个DNS服务器给我们提供权威的答案 26) +[no]additional (Control display of additional) 包含了列出的权威DNS的IP地址 27) +[no]stats (Control display of statistics) 输出包含了查询的统计数据 28) +[no]short (Disable everything except short form of answer) 29) +[no]ttlid (Control display of ttls in records) 30) +[no]all (Set or clear all display flags) 31) +[no]qr (Print question before sending) 32) +[no]nssearch (Search all authoritative nameservers) 33) +[no]identify (ID responders in short answers) 34) +[no]trace (Trace delegation down from root) 35) +[no]dnssec (Request DNSSEC records) 36) +[no]nsid (Request Name Server ID) 37) +[no]sigchase (Chase DNSSEC signatures) 38) +trusted-key=#### (Trusted Key when chasing DNSSEC sigs) 39) +[no]topdown (Do DNSSEC validation top down mode) 40) +[no]multiline (Print records in an expanded format) 6. global d-opts and servers (before host name) affect all queries. 7. local d-opts and servers (after host name) affect only that lookup. 1) -h: (print help and exit) 2) -v: (print version and exit)
常用的dig命令
1. 查找yahoo.com的A记录: dig yahoo.com A +noall +answer (注意:TTL的单位为秒s) 2. 查找yahoo.com MX记录的列表: dig yahoo.com MX +noall +answer 3. 查找yahoo.com的权威DNS: dig yahoo.com NS +noall +answer 4. 查询上面所有的记录: dig yahoo.com ANY +noall +answer 5. 在现在这种IPv4和IPV6混用的情况下,你也可以使用AAAA的选项查询主机的IPv6 AAAA记录: dig www.isc.org AAAA +short 6. 答案的精简查询 dig +nocmd www.baidu.com mx +noall +answer 7. 用-x的选项查找IP地址的主机名 dig -x 8.8.8.8 +short 8. 显式指定NS服务器 dig @ns1.google.com www.google.com 9. 查询大量的主机名 dig -f /path/to/host-list.txt 10. 找到最新的named.root文件 dig +nocmd . NS +noall +answer +additional 11. 跟踪DNS解析过程 dig gentoo.de +trace 可以在dig输出的头部分看到根DNS,然后找到负责解析所有*.de的DNS,最后找到gentoo.de的域名IP 12. 用dig查看zone数据传输 dig @server www.baidu.com AXFR 13. 用dig查看zone数据的增量传输 dig @server www.baidu.com IXFR=N 14. 用dig查看反向解析 dig -x 8.8.8.8 @server 15. 查找一个域的授权dns服务器 dig www.baidu.com +nssearch
0x2: Anti-DNS Zone Transfer配置
正常情况下,在一个信任网域下,将DNS资料列出是没有问题的(即正常的主从服务器的Zone Transfer),但是若是能由外界进行任意查询,那将衍变为具有危险的行为,因此,对DNS服务器进行Zone Transfer安全配置是十分重要的。对于DNS的安全加固,大致可以从以下几个方面入手:
1. 控制允许可以向DNS服务器提出查询请求的主机: allow-query { myAddresses; trusted; }; 如果你没有定义选项 allow-query ;那么Bind默认会允许所有的来访者都可以查询这个DNS服务器,相当于: allow-query { any; }; 2. 控制允许可以进行迭代解析或查询的主机列表: allow-recursion { match-list; }; 如果你没有定义选项 allow-recursion ; 那么Bind默认会允许所有可以访问DNS的来访者都可以进行迭代查询 3. 定义可以与主DNS服务器进行数据交流的辅助DNS服务器的ip地址,即限制zone transfer的访问 allow-transfer { match-list; }; 允许这些match-list中的辅助DNS与主DNS服务器进行数据的交流与传送。 例如:我的主DNS服务器192.168.68.128;辅助DNS服务器192.168.68.2,允许它们进行数据交流: allow-transfer { 192.168.68.2; };: 如果你没有定义选项 allow-transfer ;那么Bind默认会允许所有的辅助DNS服务器与主DNS服务器进行数据交流 4. 定义主DNS服务器的对客户的请求查询失败时的行为: forwarders { match-list; }; forward first | only; 选项forwarders定义了当DNS服务器应答失败时,就将客户端的请求转发到match-list中的其他服务器; 选项forward 定义DNS服务的转发类型:first 是优先转发上述定义的DNS服务器;only是只会转发到上述定义的DNS服务器; 如果你没有定义选项 forwarders的选项 ;那么Bind默认不做任何转发动作。
Copyright (c) 2014 LittleHann All rights reserved
转自: http://www.cnblogs.com/LittleHann/p/3828927.html