zoukankan      html  css  js  c++  java
  • python使用原始套接字 解析原始ip头数据

    使用底层套接字解码底层流量,是这次做的重点工作。

    首先来捕获第一个包

     1 # coding:utf-8import socket
     2 
     3 # 监听的主机IP
     4 host = "192.168.1.100"
     5 
     6 socket_protocol = socket.IPPROTO_ICMP
     7 
     8 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
     9 sniffer.bind((host, 0))
    10 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
    11 
    12 raw_buffer = sniffer.recvfrom(65535)
    13 print raw_buffer

    下面一行一行解释上面代码的意思。

    1. 导入socket包

    2. 需要监听的本机ip地址

    3. 给socket_protocol变量赋值icmp变量

    4. 为sniffer变量创建一个soket对象,该对象为ipv4 原始套接字并指定其协议为icmp

    5. 绑定到指定地址和端口进行监听

    6. 为sniffer套接字设置选项参数,使其携带ip头

    7. 将监听端口的套接字收到的原始数据赋值给raw_buffer

    8. 打印raw_buffer的值

    这个时候,我们使用root权限运行这个脚本,并且开启另外一个terminal对任意一个地址发送icmp包,我们监听的接口的recvfrom 会收到回监听回包到指定地址。recvfrom与recv不同的是 recvfrom会同时接收回包地址。(string, address)的格式

    这个时候我们可以看到打印出来的值,是一堆完全看不懂的东西,因为是没有解码的状态,下面我们将对ip头进行解码。

    使用python的struct和ctypes两个库实现这一点。

     1 # coding:utf-8import socket
     2 import struct
     3 from ctypes import *
     4 
     5 # 监听的主机IPhost = "192.168.1.100"
     6 
     7 # IP头定义
     8 class IP(Structure):
     9     _fields_ = [
    10         ("ihl",             c_ubyte, 4),
    11         ("version",         c_ubyte, 4),
    12         ("tos",             c_ubyte),
    13         ("len",             c_ushort),
    14         ("id",              c_ushort),
    15         ("offset",          c_ushort),
    16         ("ttl",             c_ubyte),
    17         ("protocol_num",    c_ubyte),
    18         ("sum",             c_ushort),
    19         ("src",             c_uint),
    20         ("dst",             c_uint),
    21     ]
    22 
    23     def __new__(self, socket_buffer=None):
    24         return self.from_buffer_copy(socket_buffer)
    25 
    26     def __init__(self, socket_buffer=None):
    27         self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
    28 
    29         # readable ip address
    30         self.src_address = socket.inet_ntoa(struct.pack("<I", self.src))
    31         self.dst_address = socket.inet_ntoa(struct.pack("<I", self.dst))
    32 
    33         # type of protocol
    34         try:
    35             self.protocol = self.protocol_map[self.protocol_num]
    36         except:
    37             self.protocol = str(self.protocol_num)
    38 
    39 socket_protocol = socket.IPPROTO_ICMP
    40 
    41 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
    42 sniffer.bind((host, 0))
    43 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
    44 
    45 try:
    46     while True:
    47         raw_buffer = sniffer.recvfrom(65535)[0]
    48 
    49         ip_header = IP(raw_buffer[:20])
    50 
    51         print "Protocol: %s %s -> %s " % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
    52 
    53 except KeyboardInterrupt:
    54     pass

    1. 导入各模块

    2. 监听的本机ip地址

    3. 使用ctypes 构造一个解析ip头的结构体(structure) IP

    4. 使用from_buffer_copy方法在__new__方法将收到的数据生成一个IP class的实例

    5. __init__方法初始化一部分数据保存到对应的实例属性值中。

    6. 特别说明下面代码, 使用了python struct库的pack方法 用指定的格式化参数将src 和dst的long型数值转换为字符串,然后使用socket.inet_ntoa方法将字符串的一串数字转换为对应的ip格式。最后赋值给对应的src或者dst变量

    # readable ip address
    self.src_address = socket.inet_ntoa(struct.pack("<I", self.src))
    self.dst_address = socket.inet_ntoa(struct.pack("<I", self.dst))

    7. 一个接收icmp包的服务器,没什么说的。

    8. 无限循环监听指定端口,将recvfrom收到的数据的第一部分 也就是不要ip地址的部分传递给raw_buffer

    9. ip头raw_buffer的前20个字节传递给结构体进行解码。

    10. 然后打印。

    可以看到大致思路就是,将原型socket数据拿过来,然后通过模拟c语言的结构体,使用python的库对这个格式的包进行一一对应的解码,将解码之后的数据打印出来。

    到此为止可以看到,在ip层已经可以解析出数据包从哪儿去哪儿的信息。

  • 相关阅读:
    shell 统计行数
    sqlldr errors
    sqlldr 远程数据库
    load Properties
    查看shell 版本
    linux中的网络通信指令
    给EditText的drawableRight属性的图片设置点击事件
    p2p网贷3种运营模式
    p2p网贷3种运营模式
    linux常用的压缩与解压缩命令
  • 原文地址:https://www.cnblogs.com/hushaojun/p/6485632.html
Copyright © 2011-2022 走看看