如何从 Python 中的原始套接字接收数据?

How to receive data from a raw socket in Python?

我正在尝试使用套接字库创建一个端口扫描器(使用 SYN 数据包)(是的,我知道 scapy 会使这更容易,但我这样做主要是为了学习练习。)我制作了数据包并成功发送,但是我在接收和解析后续响应时遇到了麻烦。 到目前为止,我已经尝试了 s.recv(1024) 和 4096,以及 recvfrom().

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.sendto(packet, (dstip, 80))
r = s.recv(1024)
print(r)

但是,我在接收响应时遇到问题,我可以看到数据包正在通过 Wireshark 正确发送,并且 SYN-ACK 已发送到我的机器,但是我无法正确接收和打印它。有没有更好的方法可以使用 s.recv() 函数进行此类输入?还是我使用了错误的功能? 感谢任何帮助,我是套接字库的新手。谢谢。

以下是我最近在各种资源的帮助下为套接字 IO 编写的一个模块,您可以从中获取您想要的内容。

import socket
import threading
import time    
import pygogo as gogo    

from icentralsimulator.bridgeio.read_packets import PacketFactory
from icentralsimulator.bridgeio.write_packets import WritePacket
from icentralsimulator.configurations.interfaces import IServerInfoProvider

logger = gogo.Gogo(__name__).logger
send_lock = threading.Lock()


class BridgeConnection:

    def __init__(self, bridge_info_provider: IServerInfoProvider):
        info = bridge_info_provider.get_bridge_server_info()
        self.callback = None
        self.bridge_ip = info.IpAddress
        self.bridge_port = info.Port
        self._connection = None
        self._terminate_wait_for_incoming = False


    @property
    def is_connected(self):
        return self._connection is not None


    def connect(self, callback):
        """
        The purpose of this method is to create (and hold) a connection to the server. At the same time,
        it creates a new thread for the purpose of waiting on incoming packets.
        """
        if self._connection is not None: return
        self._connection = socket.create_connection((self.bridge_ip, self.bridge_port))
        self._connection.settimeout(0.5)

        self.callback = callback

        t = threading.Thread(target=self._wait_for_incoming)
        t.start()
        time.sleep(5)


    def disconnect(self):
        """
        Breaks existing connection to the server if one is currently made and cancels the thread that is waiting
        for incoming packets. If the connection is not currently open, simply returns silently -- thus it is safe
        to call this method repeatedly.
        """
        self._terminate_wait_for_incoming = True
        while self._terminate_wait_for_incoming:
            time.sleep(0.1)
        self._connection.close()
        self._connection = None


    def send_packet(self, packet: WritePacket):
        """
        Sends an arbitrary packet to the server.
        """
        with send_lock:
            logger.debug(f"Sending packet: {packet.payload_plain_text}")
            payload = packet.payload
            self._connection.sendall(payload)


    def _wait_for_incoming(self):

        """
        Continually runs a loop to wait for incoming data on the open socket. If data is received, it is converted
        to a receive packet and forwarded to the consumer as part of a callback.
        """
        self._terminate_wait_for_incoming = False
        buf_len = 4096
        try:
            while not self._terminate_wait_for_incoming:
                data = None
                try:
                    _cnx = self._connection
                    if _cnx is None: break

                    data = _cnx.recv(buf_len)
                    if data is not None and len(data) > 0:

                        while True:
                            new_data = _cnx.recv(buf_len)
                            if new_data is None or len(new_data) == 0:
                                break
                            data = data + new_data

                except socket.timeout:
                    if data is not None and self.callback is not None:
                        packet = PacketFactory.get_packet(data)
                        self.callback(packet)
                        logger.debug(f"Received packet: {data}")
                    time.sleep(0.5)

                except OSError:  # Happens when stopping the application
                    logger.info("Application aborted")
                    return
        finally:
            self._terminate_wait_for_incoming = False

请注意,我没有在此处包括 IServerInfoProvider 或 PacketFactory。这些对我的应用程序来说是非常定制的。您将需要根据到达您的特定用例的数据包数据来解释数据包。

Black Hat Python 一书有使用套接字库创建扫描器的示例,不幸的是不是端口扫描器。他们检查主机是否启动,并使用原始套接字接收数据。代码可用 here.

他们在新线程中使用一个套接字 object 发送 SYN-packets,并使用另一个套接字 object 嗅探回复。

在示例中,他们使用 socket.IPPROTO_IPsocket.IPPROTO_ICMP 而不是 socket.IPPROTO_RAW,具体取决于它是否 Windows。

对于嗅探器,他们使用函数 setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) 进行嗅探,其中 IPPROTO_IP 是 TCP 的 dummy-protocol,IP_HDRINCL是在IP包中包含headers,1映射到代码中的ICMP-protocol。

祝你好运!