python實作圖片嗅探工具——自編driftnet
- 前言
- 一、資料包嗅探
- 二、圖片捕獲
- 三、圖片顯示及主函式
- 寫在最后
很多人學習python,不知道從何學起,
很多人學習python,掌握了基本語法過后,不知道在哪里尋找案例上手,
很多已經做案例的人,卻不知道如何去學習更加高深的知識,
那么針對這三類人,我給大家提供一個好的學習平臺,免費領取視頻教程,電子書籍,以及課程的源代碼!
QQ群:961562169
前言
想必嘗試過中間人攻擊(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值),
具體思路如下:
- 找到有上網資料的資料包
if p.haslayer(Raw): # 找出有上網資料的
- 判斷是否為含有圖片的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 = https://www.cnblogs.com/41280a/archive/2020/09/29/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'\x0d\x0a\x0d\x0a')+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 = https://www.cnblogs.com/41280a/archive/2020/09/29/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('\n請選擇:')
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如何實作滾動條自動下滑
最后,感謝各位大大的耐心閱讀~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/139161.html
標籤:其他
