import dpkt import socket import geoip2.database import argparse # 加载GeoIP数据库 # 使用geoip2.database.Reader类来读取本地的GeoLite2-City.mmdb数据库文件, # 参数指定了数据库文件在本地磁盘的具体路径,这里是 'D:\GeoLite2-City\GeoLite2-City_20241122\GeoLite2-City.mmdb', # 通过这个对象后续可以查询IP地址对应的地理位置信息。 gi = geoip2.database.Reader(r'D:\GeoLite2-City\GeoLite2-City_20241122\GeoLite2-City.mmdb') # 定义一个函数,用于根据给定的IP地址获取对应的地理位置信息字符串表示 def retGeoStr(ip): try: """ 通过之前加载的GeoIP数据库对象gi的city方法,传入IP地址来查询该IP对应的地理位置详细信息。 返回的rec是一个包含多种地理位置信息的对象,例如城市、国家、地区等相关信息。 """ rec = gi.city(ip) # 从查询结果对象中获取城市名称,如果存在的话。 city = rec.city.name # 从查询结果对象中获取国家的ISO代码,用于标识国家。 country = rec.country.iso_code if city: """ 如果获取到的城市名称不为空,那么地理位置信息字符串就由城市名和国家的ISO代码组成,用逗号和空格隔开, 例如:"北京, CN",方便直观地展示地理位置。 """ geoLoc = city + ', ' + country else: """ 如果城市名称为空,说明可能没有对应具体城市的信息,那么地理位置信息字符串就只使用国家的ISO代码来表示, 例如:"CN"。 """ geoLoc = country return geoLoc except Exception as e: """ 如果在查询地理位置信息过程中出现任何异常(比如IP地址不存在于数据库中、数据库读取错误等情况), 则返回'Unregistered'表示该IP地址对应的地理位置未注册(无法获取到相关信息)。 """ return 'Unregistered' # 定义一个函数,用于读取给定的PCAP文件(网络数据包捕获文件)中的数据包信息,并进行相关处理和打印 def printPcap(pcap): for (ts, buf) in pcap: try: """ 使用dpkt库解析以太网数据包。dpkt.ethernet.Ethernet类用于将二进制的数据包缓冲区(buf)解析为以太网数据包对象(eth)。 """ eth = dpkt.ethernet.Ethernet(buf) # 判断数据包是否为IP数据包,通过比较数据包的类型字段(type)与dpkt.ethernet.ETH_TYPE_IP常量来确定。 # 如果数据包类型不是IP数据包,则跳过当前数据包,继续处理下一个数据包,因为后续代码主要是针对IP数据包进行处理的。 if not eth.type == dpkt.ethernet.ETH_TYPE_IP: continue # Skip non-IP packets ip = eth.data # 将源IP地址从网络字节序转换为点分十进制表示形式,方便打印和查看。 src = socket.inet_ntoa(ip.src) # 将目的IP地址从网络字节序转换为点分十进制表示形式。 dst = socket.inet_ntoa(ip.dst) print('[+] Src:' + src + '--> Dst:' + dst) print('[+] Src:' + retGeoStr(src) + '--> Dst:' + retGeoStr(dst)) except Exception as e: """ 如果在解析数据包或者获取地理位置信息等操作过程中出现错误(比如数据包格式不正确、无法查询到地理位置等情况), 则打印出具体的错误信息,方便排查问题,然后继续处理下一个数据包。 """ print(f"Error processing packet: {e}") def main(): """ 主函数,用于解析命令行参数,打开指定的PCAP文件,并调用printPcap函数来处理文件中的数据包信息。 """ # 创建一个ArgumentParser对象,用于解析命令行参数,并设置相应的描述信息,这里描述了程序的主要功能是处理PCAP文件并打印IP地理位置信息。 parser = argparse.ArgumentParser(description='Process PCAP file and print IP geolocation.') # 添加一个命令行参数选项'-p',指定其目标属性名称为'pcapFile',参数类型为字符串,用于指定PCAP文件名,并添加相应的帮助信息。 parser.add_argument('-p', dest='pcapFile', type=str, help='specify pcap filename') # 解析命令行参数,返回解析后的命名空间对象,其中包含了通过命令行传入的参数值等信息。 options = parser.parse_args() if options.pcapFile is None: """ 如果没有通过命令行指定PCAP文件(即'pcapFile'属性为None),则打印出命令行参数帮助信息,提示用户如何正确使用程序,然后退出程序。 """ parser.print_help() exit(0) # 获取通过命令行指定的PCAP文件名。 pcapFile = options.pcapFile try: # 以二进制只读模式打开指定的PCAP文件,使用 'with' 语句可以确保文件在使用完后自动关闭,避免资源泄漏。 with open(pcapFile, 'rb') as f: # 使用dpkt.pcap.Reader创建一个PCAP文件读取器对象,用于按顺序读取PCAP文件中的数据包。 pcap = dpkt.pcap.Reader(f) printPcap(pcap) except FileNotFoundError: """ 如果指定的PCAP文件不存在,打印出相应的错误信息,告知用户文件不存在,然后以错误码1退出程序,表示程序执行出现错误。 """ print(f"Error: The file '{pcapFile}' does not exist.") exit(1) except dpkt.dpkt.NeedData: """ 如果打开的文件不符合PCAP文件格式要求,或者文件已损坏等导致dpkt库在读取时提示需要更多数据(dpkt.dpkt.NeedData异常), 则打印出相应的错误信息,告知用户文件可能存在问题,然后以错误码1退出程序。 """ print(f"Error: The file '{pcapFile}' is not a valid pcap file or is corrupted.") exit(1) if __name__ == '__main__': main() 运行命令:
运行结果: