tcp/ip协议学习 第四章 ARP:地址解析协议
ARP干嘛的
曾经有段时间, 六七年前了吧. 本科的时候, 流行了一阵子ARP病毒攻击, 导致整个局域网都不能上网了. 当时只听说这个东西防不住, 只要有一个人中毒, 就导致所有人上不了网. 现在也终于知道这是怎么回事了, 也能手工让某个同学上不了网了, 咳咳.
大家应该也都知道ARP是干嘛的, 我再啰嗦一下.. 比如我访问了百度, 百度回了包给我, 百度只知道我的IP是什么,不知道我的MAC地址. 这个包到网关的时候, IP这一层再把数据交给下一层的链路层, 链路层不知道IP是什么东西的, 它只认MAC地址. 所以就需要把IP转成MAC地址, ARP请求就是做这个的.
就是说, 我可以通过这个协议广播问一下所有机器 , 谁的IP是XXX.XXX.XXX.XXX, 请把你的MAC地址告诉我. 这个IP是XXX.XXX.XXX.XXX的机器收到请求之后, 就会告诉我, XXX.XXX.XXX.XXX的MAC地址是啥啥啥.
不幸这个是基于互相信任的, 理论上大家都会相信别人说的是正确的. 但是, 我可以撒谎说, XXX.XXX.XXX.XXX是我, 我的MAC是啥啥啥. 然后本来应该到 XXX.XXX.XXX.XXX那里的数据包就到我这里来了. XXX.XXX.XXX.XXX不仅仅是断网了, 我还能窃听他的数据.
协议格式
要想伪造, 啊,不, 发送一个ARP请求或者应答, 一定要了解协议格式. 从RFC文件抄了一份.
To communicate mappings from <protocol, address> pairs to 48.bit
Ethernet addresses, a packet format that embodies the Address
Resolution protocol is needed. The format of the packet follows.
Ethernet transmission layer (not necessarily accessible to
the user):
48.bit: Ethernet address of destination
48.bit: Ethernet address of sender
16.bit: Protocol type = ether_type$ADDRESS_RESOLUTION
Ethernet packet data:
16.bit: (ar$hrd) Hardware address space (e.g., Ethernet,
Packet Radio Net.)
16.bit: (ar$pro) Protocol address space. For Ethernet
hardware, this is from the set of type
fields ether_typ$<protocol>.
8.bit: (ar$hln) byte length of each hardware address
8.bit: (ar$pln) byte length of each protocol address
16.bit: (ar$op) opcode (ares_op$REQUEST | ares_op$REPLY)
nbytes: (ar$sha) Hardware address of sender of this
packet, n from the ar$hln field.
mbytes: (ar$spa) Protocol address of sender of this
packet, m from the ar$pln field.
nbytes: (ar$tha) Hardware address of target of this
packet (if known).
mbytes: (ar$tpa) Protocol address of target.
这个还是TCP/IP协议详解书里面的图好看一些, 且等我截个图来, 呃,截歪了.
其实里面有些值, 像”硬件地址长度”什么的, 是个变量, 后面的”发送端以太网地址”就是根据这个而变化 .就我们目前的应用和环境来说, 拿这个图就可以啦.
这里的长度单位是字节, 不是上一篇IP协议里面的bit了.
详细解释我也copy一下吧. 也是出自TCP/IP协议详解一书.
以太网报头中的前两个字段是以太网的源地址和目的地址。目的地址为全 1的特殊地址是 广播地址。电缆上的所有以太网接口都要接收广播的数据帧。
两个字节长的以太网帧类型表示后面数据的类型。对于 ARP请求或应答来说,该字段的 值为0x0806。
形容词hardware(硬件)和protocol(协议)用来描述 ARP分组中的各个字段。例如,一个 ARP 请求分组询问协议地址(这里是 IP地址)对应的硬件地址(这里是以太网地址)。
硬件类型字段表示硬件地址的类型。它的值为 1即表示以太网地址。协议类型字段表示要 映射的协议地址类型。它的值为 0x0800即表示 IP地址。它的值与包含 IP数据报的以太网数据 帧中的类型字段的值相同,这是有意设计的(参见图 2-1)。
接下来的两个 1字节的字段,硬件地址长度和协议地址长度分别指出硬件地址和协议地址 的长度,以字节为单位。对于以太网上 IP地址的 ARP请求或应答来说,它们的值分别为 6和4。 操作字段指出四种操作类型,它们是 ARP请求(值
(值为3)和RARP应答(值为4)(我们在第5章讨论RARP)。这个字段必需的,因为 ARP请求 和ARP应答的帧类型字段值是相同的。
接下来的四个字段是发送端的硬件地址(在本例中是以太网地址)、发送端的协议地址 (IP地址)、目的端的硬件地址和目的端的协议地址。注意,这里有一些重复信息:在以太网的数据帧报头中和 ARP请求数据帧中都有发送端的硬件地址。
对于一个 ARP请求来说,除目的端硬件地址外的所有其他的字段都有填充值。当系统收到一份目的端为本机的 ARP请求报文后,它就把硬件地址填进去,然后用两个目的端地址分 别替换两个发送端地址,并把操作字段置为 2,最后把它发送回去。
来段程序吧
其它也没啥好说的了, 先来段程序吧.
广播一个ARP请求, 问一下网关的MAC地址是多少. 运行环境是ubuntu12.04. osx上面是不行的. 据说windows也不行. 搜索了好久, 也不知道mac上面怎么搞.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
广播一个ARP请求, 问一下网关的MAC地址是多少.
运行环境是ubuntu12.04.
osx上面是不行的. 据说windows也不行. 搜索了好久, 也不知道mac上面怎么搞.
'''
import socket
import struct
def main():
st = struct.Struct('!6s 6s h h h b b h 6s 4s 6s 4s')
GATEWAY = '192.168.1.1'
MYIP = '192.168.1.8'
MYMAC = '20:c9:d0:88:96:3f'
dst_ethernet_addr = ''.join(
[chr
(int(e, 16))
for e in 'FF:FF:FF:FF:FF:FF'.split(':')])
protocol_type = 0x0806
hw_addr_space = 1
protocol_addr_space = 0x800
hw_addr_length = 6
protocol_addr_length = 4
op = 1
my_mac = ''.join([chr(int(e, 16)) for e in MYMAC.split(':')])
my_ip = socket.inet_aton(MYIP)
target_hw_addr = ''.join(
[chr
(int(e, 16))
for e in '00:00:00:00:00:00'.split(':')])
des_ip = socket.inet_aton(GATEWAY)
data = (
dst_ethernet_addr,
my_mac,
protocol_type,
hw_addr_space,
protocol_addr_space,
hw_addr_length,
protocol_addr_length,
op,
my_mac,
my_ip,
target_hw_addr,
des_ip,
)
packed_data = st.pack(*data)
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.SOCK_RAW)
s.bind(('eth0', socket.SOCK_RAW))
# 下面这样也行, 不知道区别.
#http://sock-raw.org/papers/sock_raw 这个应该可以参考
#s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
#s.bind(('eth0',0))
r = s.send(packed_data)
print r
return
if __name__ == '__main__':
main()
抓包结果, 这里已经忽略了以太网首部的14个字节. 是从上图中的”硬件类型”开始的.
% sudo tcpdump -nn -vvv -x -c1 arp
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:28:43.209235 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 192.168.1.1 tell 192.168.1.8, length 28
0x0000: 0001 0800 0604 0001 20c9 d088 963f c0a8
0x0010: 0108 0000 0000 0000 c0a8 0101
1 packet captured
1 packet received by filter
0 packets dropped by kernel
ARP攻击
上面的代码简单改一下就可以做ARP攻击了.
对网关做一个ARP应答.
op改为2, 代表ARP应答. 然后把 “以太网源地址”和”发送端以太网地址”都写自己的. IP地址写要攻击的人.
OVER