如何在Python中使用原始套接字?

问题描述 投票:32回答:8

我正在编写一个应用程序来测试网络驱动程序以处理损坏的数据。我想使用原始套接字发送这些数据,因此发送机器的TCP-IP堆栈不会纠正它。

我只在Linux上编写这个应用程序。我有在系统调用中使用原始套接字的代码示例,但我真的希望尽可能保持测试的动态,并在Python中编写大部分(如果不是全部的话)。

我在网上搜索了一些关于在python中使用原始套接字的解释和示例,但是没有找到任何真正有启发性的东西。只是一个非常古老的代码示例,演示了这个想法,但绝不是有效的。

从我收集的内容来看,Python中的Raw Socket使用在语义上几乎与UNIX的原始套接字完全相同,但没有定义数据包结构的structs。

我想知道在Python中编写测试的原始套接字部分是否更好,但在C中使用系统调用,并从主Python代码调用它?

python sockets raw-sockets
8个回答
44
投票

你这样做:

首先禁用网卡的自动校验和:

sudo ethtool -K eth1 tx off

然后从python 2发送你的狡猾的框架(你必须自己转换为Python 3):

#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth1", 0))

# We're putting together an ethernet frame here, 
# but you could have anything you want instead
# Have a look at the 'struct' module for more 
# flexible packing/unpacking of binary data
# and 'binascii' for 32 bit CRC
src_addr = "\x01\x02\x03\x04\x05\x06"
dst_addr = "\x01\x02\x03\x04\x05\x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
ethertype = "\x08\x01"

s.send(dst_addr+src_addr+ethertype+payload+checksum)

完成。


10
投票

套接字系统调用(或Windows上的Winsocks)已经包含在标准模块socketintroreference中。

我从来没有使用过原始套接字,但看起来它们可以用于这个模块:

最后一个示例显示了如何在Windows上使用原始套接字编写非常简单的网络嗅探器。该示例需要管理员权限才能修改接口:

import socket

# the public network interface
HOST = socket.gethostbyname(socket.gethostname())

# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))

# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# receive a package
print s.recvfrom(65565)

# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

2
投票

this是你提到的旧代码吗?它对我来说很合理,但我自己没有测试过(或者使用了很多原始套接字)。文档中的This example显示了如何使用原始套接字来嗅探数据包,它看起来很相似。


2
投票

最终这个案例的最佳解决方案是用C编写整个事物,因为它不是一个大的应用程序,所以用一种以上的语言编写这么小的东西会受到更大的惩罚。

经过多次使用C和python RAW套接字后,我最终选择了C RAW套接字。 RAW套接字需要少于8位组的位级修改才能写入包头。有时只写4位或更少。 python没有定义任何帮助,而Linux C有一个完整的API。

但是我绝对相信如果只是在python中方便地处理了这一点头文件初始化,我就不会在这里使用C了。


1
投票
s = socket(AF_PACKET, SOCK_RAW)
s = socket(PF_PACKET, SOCK_RAW)

结果:

[root@localhost python]# tcpdump -i eth0

capture size 96 bytes
11:01:46.850438 

01:02:03:04:05:06 (oui Unknown) > 01:02:03:04:05:06 (oui Unknown), ethertype Unknown (0x0801), length 85:

        0x0000:  5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b  [[[[[[[[[[[[[[[[
        0x0010:  5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5b5b 5041  [[[[[[[[[[[[[[PA
        0x0020:  594c 4f41 445d 5d5d 5d5d 5d5d 5d5d 5d5d  YLOAD]]]]]]]]]]]
        0x0030:  5d5d 5d5d 5d5d 5d5d 5d5d 5d5d 5d5d 5d5d  ]]]]]]]]]]]]]]]]
        0x0040:  5d5d 5d00 0000 00                        ]]]....

0
投票

套接字类应该有所帮助。如果不是,您需要在C中编写Python模块或只使用C.请参阅http://mail.python.org/pipermail/python-list/2001-April/077454.html

基本的谷歌搜索发现。

我实际上尝试过“unwind”指出的代码示例。 AF_PACKET在python 2.7.4中为我工作


0
投票

FTR,如果你想要2级访问(以太网,RadioTap ......),这在Windows上是不可能的(截至今天)。

如果你想使用跨平台方法访问那些,那么首选的选择是libpcap及其Python绑定(因为它将使用Npcap/WinPcap在Windows上运行)。

您可以在各种级别(非常高或非常低)使用libpcap的各种Python绑定。

我的建议是使用scapy的套接字(即使你没有用它来剖析),实现Native和Libpcap调用,(并让你用conf.use_pcap = True选择它们)

from scapy.all import conf
# conf.use_pcap = True (will be automatic if required)
socket = conf.L2socket(iface="eth0")
# On any platforms, you have `get_if_list()` in `scapy.all` available, to see the ifaces available. You could also ignore it to use the default one

-1
投票

您可以使用此Python库:rawsocketpy它允许在第2层=>无IP / TCP / UDP标头上使用原始套接字。

#!/usr/bin/env python
from rawsocketpy import RawSocket

sock = RawSocket("wlp2s0", 0xEEFA)
sock.send("some data")
sock.send("personal data", dest="\xAA\xBB\xCC\xDD\xEE\xFF")

或服务器形式:

#!/usr/bin/env python
from rawsocketpy import RawRequestHandler, RawAsyncServerCallback
import time

def callback(handler, server):
    print("Testing")
    handler.setup()
    handler.handle()
    handler.finish()

class LongTaskTest(RawRequestHandler):
    def handle(self):
        time.sleep(1)
        print(self.packet)

    def finish(self):
        print("End")

    def setup(self):
        print("Begin") 

def main():
    rs = RawAsyncServerCallback("wlp2s0", 0xEEFA, LongTaskTest, callback)
    rs.spin()

if __name__ == '__main__':
    main()
© www.soinside.com 2019 - 2024. All rights reserved.