Python嗅探黑帽Python书籍

Fou*_*ree 13 python sockets linux networking

import socket
import os
import struct
import sys
from ctypes import *

# host to listen on
host   = sys.argv[1]

class IP(Structure):

    _fields_ = [
        ("ihl",           c_ubyte, 4),
        ("version",       c_ubyte, 4),
        ("tos",           c_ubyte),
        ("len",           c_ushort),
        ("id",            c_ushort),
        ("offset",        c_ushort),
        ("ttl",           c_ubyte),
        ("protocol_num",  c_ubyte),
        ("sum",           c_ushort),
        ("src",           c_ulong),
        ("dst",           c_ulong)
    ]

    def __new__(self, socket_buffer=None):
        return self.from_buffer_copy(socket_buffer)    

    def __init__(self, socket_buffer=None):

        # map protocol constants to their names
        self.protocol_map = {1:"ICMP", 6:"TCP", 17:"UDP"}

        # human readable IP addresses
        self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
        self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))

        # human readable protocol
        try:
            self.protocol = self.protocol_map[self.protocol_num]
        except:
            self.protocol = str(self.protocol_num)

# create a raw socket and bind it to the public interface
if os.name == "nt":
    socket_protocol = socket.IPPROTO_IP 
else:
    socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

sniffer.bind((host, 0))

# we want the IP headers included in the capture
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# if we're on Windows we need to send some ioctls
# to setup promiscuous mode
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

try:
    while True:

        # read in a single packet
        raw_buffer = sniffer.recvfrom(65565)[0]

        # create an IP header from the first 20 bytes of the buffer
        ip_header = IP(raw_buffer[0:20])

        print "Protocol: %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)

except KeyboardInterrupt:
    # if we're on Windows turn off promiscuous mode
    if os.name == "nt":
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
Run Code Online (Sandbox Code Playgroud)

这是Black Hat Python一书中的代码.这段代码应该用原始套接字嗅探并显示来自IP头的信息.它适用于Windows(使用Windows 8.1 64位).当我尝试在linux上运行它(Kali linux 1.1.0-amd64)时,我收到以下错误

ValueError: Buffer size too small (20 instead of at least 32 bytes)
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,我在缓冲区中添加了12个空格

ip_header = IP(raw_buffer[0:20]+' '*12)
Run Code Online (Sandbox Code Playgroud)

当我这样做时,我收到以下错误

struct.error: 'L' format requires 0 <= number <= 4294967295
Run Code Online (Sandbox Code Playgroud)

这发生在线上

self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
Run Code Online (Sandbox Code Playgroud)

我已经尝试将L之前的符号更改为>和!我只用L试了一下它们都给了我同样的问题.我也试过在ntohs中包装self.src,就像这样

self.src_address = socket.inet_ntoa(struct.pack("<L",socket.ntohs(self.src)))
Run Code Online (Sandbox Code Playgroud)

我认为这与字节序有关,但我不确定.任何帮助将不胜感激.

注意:在Windows上,您必须以管理员身份运行,而在Linux上,由于原始套接字,您必须以超级用户身份运行.如果你在linux上运行它,打开另一个终端并ping www.google.com,这样你就可以生成一些ICMP数据包供它捕获.

编辑:我也试过逆转缓冲区

ip_header = IP(raw_buffer[0:20][::-1]+' '*12)
Run Code Online (Sandbox Code Playgroud)

编辑2:我在下面的行上尝试了65535和65534,然后再做我在这里列出的任何其他项目.

raw_buffer = sniffer.recvfrom(65565)[0]
Run Code Online (Sandbox Code Playgroud)

编辑3:这在运行python 2.7.6的ubuntu机器上工作,我的kali发行版是2.7.3所以我决定在我的kali盒子上获得最新版本的python,恰好是2.7.9.仍然没有运气.

我将以下代码放在我的结构中的函数中以查看缓冲区大小

print sizeof(self)
Run Code Online (Sandbox Code Playgroud)

在我的Ubuntu和Windows机器上它是20但是在我的kali机器上它是32

Niz*_*med 25

#raw_buffer = sniffer.recvfrom(65565)[0]
raw_buffer = sniffer.recvfrom(65535)[0]
Run Code Online (Sandbox Code Playgroud)

IP paket大小为(2 ^ 16) - 1

问题在于32对64位系统.
ip_header = IP(raw_buffer[:20])适用于x86 Ubuntu.
ip_header = IP(raw_buffer[:32])适用于amd64 CentOS 6.6 Python 2.6.6
ip_header = IP(raw_buffer)适用于两者.

你必须改变这些,

("src",           c_ulong),
("dst",           c_ulong)  

self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
Run Code Online (Sandbox Code Playgroud)

("src",           c_uint32),
("dst",           c_uint32)  

self.src_address = socket.inet_ntoa(struct.pack("@I",self.src))
self.dst_address = socket.inet_ntoa(struct.pack("@I",self.dst))
Run Code Online (Sandbox Code Playgroud)

'@I'是原始顺序的unisigned int.因为c_ulongi386中有4个字节,amd64中有8个字节.检查以下内容,

struct.calcsize('@BBHHHBBHLL')  
Run Code Online (Sandbox Code Playgroud)

在i386中是20,在amd64中是32,大小是_fields_.实际上它是amd64中的28个字节加上用于字对齐的4个字节.

ip_header = IP(raw_buffer[:20]) 现在可以独立于平台正常工作.


小智 5

所以这是一个 64/32 位的问题。它需要 32 个字节而不是 20 个字节的事实意味着该结构没有正确打包。“c_ulong”在 64 位 linux 中是 64 位,并且在“IP”类中以这种方式映射。

IP 标头为 20 字节 + 可选字段。源和目标 IP 地址以字节 20 结尾,这是当前 IP 结构所采用的。(如果你想要这些选项,你将不得不手动解析它们)。

我查找了 UDP 位域并直接将它们设置为“IP”类。查看 ctypes 文档,可以映射整数类型以限制位数。

class IP(Structure):

    _fields_ = [
        ("ihl",           c_ubyte, 4),
        ("version",       c_ubyte, 4),
        ("tos",           c_ubyte, 8),
        ("len",           c_ushort, 16),
        ("id",            c_ushort, 16),
        ("offset",        c_ushort, 16),
        ("ttl",           c_ubyte, 8),
        ("protocol_num",  c_ubyte, 8),
        ("sum",           c_ushort, 16),
        ("src",           c_uint, 32),
        ("dst",           c_uint, 32),
    ]
Run Code Online (Sandbox Code Playgroud)

如果对位偏移进行求和,它们的总和为 160。160/8 = 20 字节,这就是 ctypes 将此结构打包的内容。

在 ping 上运行它会产生一些看起来可以接受的东西。

Protocol: ICMP 127.0.0.1 -> 127.0.0.1
Protocol: ICMP 127.0.0.1 -> 127.0.0.1
Protocol: ICMP 127.0.0.1 -> 127.0.0.1
Protocol: ICMP 127.0.0.1 -> 127.0.0.1
Run Code Online (Sandbox Code Playgroud)

此外,数据包大小是 MTU(或最大传输单元)的函数,因此如果您计划在以太网上运行它,限制因素是帧的 MTU。较大的数据包在被推出以太网端口之前将在 tcp/ip 堆栈中分段。

$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:00:00:ff:ff:ff  
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
Run Code Online (Sandbox Code Playgroud)

此外,这个问题应该有助于澄清为什么某些平台具有不同大小的整数和长整数的问题:

64 位 Windows 上 long 的位大小是多少?

作为替代方案,我发现 dpkt 是一个相当不错的用于解码/编码 ip 数据包的库,除非您特别需要使用或想要 ctypes。

https://code.google.com/p/dpkt/