【问题标题】:BPF in python to sniff packets for multiple TCP portspython中的BPF嗅探多个TCP端口的数据包
【发布时间】:2017-05-11 11:29:11
【问题描述】:

我从http://allanrbo.blogspot.in/2011/12/raw-sockets-with-bpf-in-python.html 获得了代码。它工作正常,但我想嗅探多个 TCP 端口上的流量,如端口 9000、80、22...

所以我修改了filter_list就像一击

filters_list = [  
    # Must have dst port 67. Load (BPF_LD) a half word value (BPF_H) in   
    # ethernet frame at absolute byte offset 36 (BPF_ABS). If value is equal to  
    # 67 then do not jump, else jump 5 statements.  
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 36),  
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 0, 5), <===== Here I added another port
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 80, 0, 5),


    # Must be UDP (check protocol field at byte offset 23)  
    bpf_stmt(BPF_LD | BPF_B | BPF_ABS, 23),   
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x06, 0, 3), #<==Changed for TCP "0x06"

    # Must be IPv4 (check ethertype field at byte offset 12)  
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 12),   
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x0800, 0, 1),  

    bpf_stmt(BPF_RET | BPF_K, 0x0fffffff), # pass  
    bpf_stmt(BPF_RET | BPF_K, 0), # reject   ]

问题是,有时它工作有时它不是,就像只在 9000 上获得流量而不是在 80 上获得流量,有时在 80 上获得流量。我没有完全理解代码。有什么帮助吗?

【问题讨论】:

    标签: python sockets tcp bpf


    【解决方案1】:

    据我所知,问题似乎来自前两个条件跳转的逻辑。具体来说:

    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 0, 5), # if false, skip 5 instructions
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 80, 0, 5),
    

    一条指令bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, &lt;val&gt;, &lt;jtrue&gt;, &lt;jfalse&gt;)表示

    if value currently in register K is equal to <val>
        then add <jtrue> to instruction pointer
            (i.e. skip the next <jtrue> instructions),
        else add <jfalse> instead`
    

    所以这两行的意思是:

    if port is 9000
        then if port is 80
            then go on with checks…
        else skip 5 instructions (i.e. reject)
    else
        skip 5 instructions (i.e. pass, as jump offset was not updated from 5 to 6)
    

    虽然您可能想要看起来更像:

    if port is 9000
        then go on with checks…
    else
        if port is 80
            then go on with checks…
        else reject
    

    我没有测试过,但要获得这个逻辑,我会说你需要调整跳转偏移量,如下所示:

    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 1, 0), # if true skip 1 insn
                                                     # (i.e. port 80 check) else 0
                                                     # and check for port 80
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 80, 0, 5),   # if true skip 0 else skip 5
                                                     # (and land on “reject”)
    

    编辑 1: 然后过滤三个端口,将变为:

    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 8084, 2, 0), # skip the next 2 checks if true
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 1, 0), # skip the next check if true
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 22,   0, 5), # if true go on else reject
    

    编辑 2: 要同时过滤源端口(除了目标端口),您可以尝试这样的事情(我这边仍未测试):

    # Load TCP src port into register K, and check port value
    # For packets with IP header len == 20 bytes, TCP src port should be at offset 34
    # We adapt the jump offsets to go to next check if no match (or to “reject” after
    # the last check), or to skip all remaining checks on ports if a match is found.
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 34),           # 34 == offset of src port
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 8084, 6, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 5, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 22,   4, 0),
    
    # As before: if no match on src port, check on dst port
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 36),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 8084, 2, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 1, 0),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 22,   0, 5),
    
    …
    

    【讨论】:

    • 感谢您的回答。如果我想过滤 2 个或更多端口,过滤器是什么。我尝试了 3 个端口---> bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 8084, 1, 0), bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 9000, 1, 0),bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 22, 0, 5),。我只获得9000 和22 的流量。
    • @Veerendra:我为三个端口编辑了我的答案(但仍未测试)。
    • 谢谢!你拯救了我的一天:) 现在我得到目标端口 ==9000 或 8084 或 22 的数据包(无论我们在过滤器中添加了什么),但我想要两种方法,比如我需要有兴趣的数据包端口(感兴趣的端口可以在source或destination:需要两个数据包!)有什么帮助吗?
    • 太棒了!这是工作!。只是好奇,如果我想提交IPs,我该如何实现呢?
    • @Veerendra 对于 IP,它是一样的:以正确的偏移量加载数据(对于 IPv4 src 应该是 26,对于 IPv4 dst 应该是 30),但将它们作为单词加载(BPF_W 而不是 @ 987654339@),检查值并跳转到相关说明。以现有过滤器为起点,这应该相当简单。
    【解决方案2】:

    我知道您想在原始套接字上附加一个过滤器,这是我最近使用的。感谢几个多星期的努力,我找到了一种简单的方法来将过滤器连接到原始套接字。我想和你分享:)
    首先确保您已经安装了tcpdump(这是一个Linux系统管理工具),如果您的平台是Linux发行版之一,让我们进行下一步。
    其次,您需要拥有sudo or root 权限才能执行该工具。
    第三个跟随演示示例,根据您的情况进行更改。

    $ sudo tcpdump -i enp4s0 -dd 'tcp and (port 9000 or port 80 or port 22)'  
    

    我先解释一下参数。
    tcpdump --> 在网络上转储流量
    -i --> 具体接口
    enp4s0 --> 网络接口
    -dd --> 将包匹配代码转储为 C 程序片段。
    tcp and (port 9000 or port 80 or port 22) --> Berkeley Packet Filter (BPF) 语法

    在这个命令之后,你应该生成一些类似这样的代码:

    { 0x28, 0, 0, 0x0000000c },
    { 0x15, 0, 8, 0x000086dd },
    { 0x30, 0, 0, 0x00000014 },
    { 0x15, 0, 21, 0x00000006 },
    { 0x28, 0, 0, 0x00000036 },
    ....
    

    看起来一团糟,但别担心,我们勇敢地继续。

    先说清楚是什么,这是一个包过滤代码,如果你之前学过汇编语言,你会觉得很熟悉。

    第二,因为是C风格的代码,不适合我们python的使用,所以需要对数据进行润色,使用follow python code

    import subprocess
    
    cmd = "sudo tcpdump -i enp4s0 -dd 'tcp and (port 9000 or port 80 or port 22)'"
    tcpdumpBinary = subprocess.check_output(cmd, shell=True)
    macroString = '( ' + tcpdumpBinary.decode('utf-8').replace(
                '\n', '').replace('{', '[').replace('}', ']') + ')'
    macroString = eval(macroString)
    

    第三现在您可以像使用filters_list 一样使用macroString。

    这个方法帮我摆脱了过滤代码的困境,希望对你有所帮助。

    【讨论】:

      猜你喜欢
      • 2023-02-08
      • 2020-06-06
      • 2010-10-02
      • 1970-01-01
      • 2015-07-28
      • 2011-09-29
      • 1970-01-01
      • 1970-01-01
      • 2015-04-02
      相关资源
      最近更新 更多