How can I parse all the packets in my pcap file instead of one?

3.8k views Asked by At

I am a beginner at python, currently trying to build a packet capture analysis tool using dpkt in python 3. I have not done much yet, I'm attempting to build it slowly, step by step so I can really understand my problems and learn from them. As it stands my first task is simply to be able to read, parse and close the packet capture, storing the relevant information as suitable data types. The first problem I have encountered is that I am able to parse all the packet information inside my initial function as demonstrated in the commented out line (line 17), however when I attempt to call any packet info inside main() I only get the first packet in the stream (line 36). I think the solution is either to create a list/dict to store the data from the parsepcap function or to move the opening, parsing and closing of the pcap file to main(). I'm not sure how to proceed from here so any help would be appreciated. The second task is to summarise the packet info into number of packet types (UDP,TCP,IGMP), print first and last timestamps for each type and mean packet length for each type, but I'm not quite there yet. Again, any help is appreciated.

import dpkt
import socket
import sys

#Open, Parse and Close a PCAP file
#Input = File
#Output = File Contents    
def parsepcap(pcap):
    with open(pcap,'rb') as pcap_in:
        contents = dpkt.pcap.Reader(pcap_in)       
        for ts, buf in contents:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            tcp = ip.data
            dst_ip = socket.inet_ntoa(ip.dst)
            src_ip = socket.inet_ntoa(ip.src)
            #print(f'Source: {src_ip} -> Destination: {dst_ip}')
        return eth, ip, tcp, dst_ip, src_ip

def main():
    #Test Arguments
    sys.argv.append('packets1.pcap')
    #sys.argv.append('packets2.pcap')
    #sys.argv.append('evidence-packet-analysis.pcap')

    #Checks if there are two arguments being passed to sys.argv. If not, then explains usage.
    if len(sys.argv) != 2:
        print('[Error] Usage: py pcap_cw.py [pcapfile.pcap]')
        return
    
    in_location = sys.argv[1] #string we are getting data from - URL or path
    
    try:            
        print('[+] Analysing %s' % sys.argv[1])
        text = ""
        text = parsepcap(in_location)
        print(text)
    
    except FileNotFoundError as err1:
        print(f' File "{in_location}" does not exist. Please choose another file.')
    except Exception as err2:
        print(f'Oh No! - {err2}')
    

if __name__ == '__main__':
    main()

https://github.com/Sly-Lamp/pcap

1

There are 1 answers

2
David Francos Cuartero On

You're setting the return variables inside a loop. That means they're being overridden each loop iteration, wich leads to you receiving only the last value.

An easy solution would be to turn your method into a generator (https://docs.python.org/3/glossary.html#term-generator), that will return each value when iterated, then iterate it on the outside function.


def parsepcap(pcap):
    with open(pcap,'rb') as pcap_in:
        contents = dpkt.pcap.Reader(pcap_in)
        for ts, buf in contents:
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            tcp = ip.data
            dst_ip = socket.inet_ntoa(ip.dst)
            src_ip = socket.inet_ntoa(ip.src)
            yield eth, ip, tcp, dst_ip, src_ip

And when setting the text:

'\n'.join(parsepcap(pcap))

A complete example (with some changes) would be:


import dpkt
import socket
import sys


def parsepcap(pcap):
    with open(pcap, 'rb') as pcap_in:
        for _, buf in dpkt.pcap.Reader(pcap_in):
            eth = dpkt.ethernet.Ethernet(buf)
            ip = eth.data
            tcp = ip.data
            dst_ip = socket.inet_ntoa(ip.dst)
            src_ip = socket.inet_ntoa(ip.src)
            yield eth, ip, tcp, dst_ip, src_ip


def main(pcap_path):
    try:
        print(f'[x] Parsing: {pcap_path}')
        for packet in parsepcap(pcap_path):
            print(f'\t{packet[4]} -> {packet[3]}')
    except FileNotFoundError:
        print(f' File "{pcap_path}" does not exist.')
    except Exception as err2:
        print(f'Oh No! - {err2}')


if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('[Error] Usage: py pcap_cw.py [pcapfile.pcap]')
        sys.exit(1)
    main(sys.argv[1])

With result (I took the liberty of printing only the most "printable" parts, the source and destination IP):


python3 test_3.py foo.pcap
[x] Parsing: foo.pcap
    192.168.0.130 -> 255.255.255.255