python中的原始套接字和sendto

Posted

技术标签:

【中文标题】python中的原始套接字和sendto【英文标题】:Raw sockets and sendto in python 【发布时间】:2012-06-15 18:47:58 【问题描述】:

我正在努力将 scapy 与 twisted 集成,但我在 OSX 上遇到了一个我似乎无法弄清楚的非常奇怪的错误。

基本上我无法通过原始套接字发送有效的 TCP 数据包(包括 IP 标头)。这就是我正在做的:

import socket
from scapy.all import IP, TCP
pkt = IP(src='0.0.0.0', dst='127.0.0.1')/TCP()
spkt1 = str(pkt)
outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
outs.sendto(spkt1, ('127.0.0.1', 0))

当我运行它时,我收到以下错误:

outs.sendto(spkt1, ('127.0.0.1', 0)) socket.error: [Errno 22] Invalid argument

如果你没有 scapy 不想使用它,这是 base64 编码的数据包:

import base64
spkt1 = base64.b64decode("RQAAKAABAABABvvOAAAAAH8AAAEAFABQAAAAAAAAAABQAiAAEH4AAA==")

非常奇怪的是,一个几乎完全相同的数据包似乎被正确发送:

spkt2 = base64.b64decode("RQBAAAWwAAACBgAAAAAAAH8AAAEAyAOEAAAAAAAAAACwAgDIAHsAAAIEBbQBAwMBAQEICk3PUjMAAAAABAIAAA==")

这是两个数据包的样子:

SPKT1
0000   45 00 00 28 00 01 00 00  40 06 FB CE 00 00 00 00   E..(....@.......
0010   7F 00 00 01 00 14 00 50  00 00 00 00 00 00 00 00   .......P........
0020   50 02 20 00 10 7E 00 00                            P. ..~..
SPKT2
0000   45 00 40 00 05 B0 00 00  02 06 00 00 00 00 00 00   E.@.............
0010   7F 00 00 01 00 C8 03 84  00 00 00 00 00 00 00 00   ................
0020   B0 02 00 C8 00 7B 00 00  02 04 05 B4 01 03 03 01   ...............
0030   01 01 08 0A 4D CF 52 33  00 00 00 00 04 02 00 00   ....M.R3........

通过在wireshark 中检查它们,它们仅在TCP 部分有所不同。

我做了很多不同的实验,最后我可以通过设置某些特定的 TCP 选项来发送数据包,但这样的数据包不应该工作是没有意义的。

有人知道为什么会发生这种情况吗?

编辑:

这个数据包似乎确实有效:

pkt = IP(len=16384, src='0.0.0.0', dst='127.0.0.1',
     id=RandShort(), ttl=2)/TCP(sport=255,
      dport=900, flags="S", window=200,
      options=[('MSS', 1460), ('WScale', 2)])
spkt = bytes(pkt)
spkt += '\x00'*20

如果你不添加零,它就不起作用。

【问题讨论】:

你能在你的第一个代码 sn-p 中修复import 吗? (另外,当我阅读您的问题请求时,有趣的事实是:您可以使用"…".decode("base64")"…".encode("base64") 而不是import base64)。好吧,对不起,这个帮不了了。但你有我的赞成票。 FWIW,我在您的代码上遇到了同样的错误。 【参考方案1】:

我最终决定原始套接字只是为了可用。特别是因为这个软件需要跨平台,OSX的怪癖可能不适用于其他操作系统。

目前我只是简单地包装了 scapy 提供的“套接字”。将来我会写一些只依赖于 libdnet 的东西(因为这就是 scapy 写原始帧的方式)。

你可以在这里找到这个实现:

https://github.com/hellais/txscapy

【讨论】:

【参考方案2】:

IP 标头必须是 32 位的倍数才有效。最后还有一个填充区域。

因此,根据标头中设置的 IP 选项 — 占用可变数量的位 — 需要计算位填充。

看起来不同的操作系统以不同的方式处理这个问题。有人可能会认为一个聪明的操作系统会为你做这个填充。

【讨论】:

【参考方案3】:

0.0.0.0 在我看来不是一个有效的 IP 源地址。将其更改为任何其他值有什么不同吗?

【讨论】:

看起来没什么区别。 不,没有任何区别。例如,奇怪的是,这个数据包似乎确实有效:pkt = IP(len=16384, src='0.0.0.0', dst='127.0.0.1', id=RandShort(), ttl=2)/TCP(运动=255, dport=900, flags="S", window=200, options=[('MSS', 1460), ('WScale', 2)]) spkt = bytes(pkt) spkt += '\x00 '*20 如果你不添加 20 个零,它会像另一个一样失败。 (将数据包添加到格式正确的帖子中) 很奇怪。不知道。我的下一个想法是 OS X 内核充满了像这样的模糊错误。 ;) 是的,这可能是一个 OSX 错误。我将放弃使用原始套接字,因为它们对于跨平台支持很糟糕,只使用 libdnet。【参考方案4】:
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> from scapy.all import IP, TCP
WARNING: No route found for IPv6 destination :: (no default route?)
>>> pkt = IP(src='0.0.0.0', dst='127.0.0.1')/TCP()
>>> spkt1 = str(pkt)
>>> 
>>> outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
>>> outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
>>> outs.sendto(spkt1, ('127.0.0.1', 0))
40

我在编写特定数据包集时似乎没有任何错误 - 我使用的是 x86_64 和 2.6.38-* Gnu/Linux 内核。

也许您的问题与使用原始套接字的某些 Mac OS X 脑损伤有关?

【讨论】:

【参考方案5】:

另一个相关问题。 Python impacket 模块有 ping.py 脚本来 ping 主机。在 Mac OS X Lion 上,使用此脚本时出现错误:

Traceback (most recent call last):
  File "/private/var/www/env/bin/ping.py", line 73, in <module>
    s.sendto(ip.get_packet(), (dst, 0))
socket.error: [Errno 22] Invalid argument

但在 Ubuntu 上一切正常,我收到了来自主机的回复。

【讨论】:

【参考方案6】:

我没有确凿的证据,但我认为这可能与以太网最小负载大小有关。

来自wikipedia:

当 802.1Q 标签存在时,最小有效载荷为 42 个八位字节,不存在时为 46 个八位字节。

您的第一个示例数据包只有 40 个字节,因此无论哪种情况都低于限制。您可以尝试将填充从 20 字节更改为这些值,以验证它是否在某个限制处停止工作。

如果是这样,那么这种行为就很合理了;操作系统拒绝数据包,因为您没有提供足够的数据来构造有效数据包。

【讨论】:

以上是关于python中的原始套接字和sendto的主要内容,如果未能解决你的问题,请参考以下文章

使用原始套接字 (c++)

通过 C 中的套接字传递结构

为啥数据不通过 Windows 中的原始套接字发送

python 原始套接字和嗅探

Python原始套接字编程

python使用原始套接字 解析原始ip头数据