多网卡的情况下发送二层包需要配置网卡
三层包不需要配置接口
发包方法:
sr()
发送三层数据包,等待接收一个或者多个数据包的响应
sr1()
发送三层数据包,只会接收一个数据包的响应
srp()
发送二层数据包,然后一直等待回应
srp1()
发送二层发送数据包,只返回第一个答案
send()
只发送三层数据包,系统自动处理路由和两层信息
sendp()
只发送二层数据包
带p字母的都是发送二层数据包,必须要写以太网头部Ether(),而且如果是多接口一定要指定接口
不带p字母都是发送三层数据包,不需要填Ether头部,不需要指定接口
hwdst表示硬件MAC
verbose=False 表示关闭scapy自身的回显
通过Scapy看ARP结构:
hwdst表示硬件MAC
verbose=False 表示关闭scapy自身的回显
srp返回包结构分析:
Demo:
#!/usr/bin/python3
from scapy.all import *
localmac = '00:0c:29:b6:6b:7d'
localip = '192.168.64.128'
destip = '192.168.64.129'
intername ='eth0'
result_raw = srp(Ether(src=localmac,dst='FF:FF:FF:FF:FF:FF')/ARP(op=1,hwsrc=localmac,hwdst='00:00:00:00:00:00',psrc=localip,pdst=destip),iface = intername,timeout=1,verbose=False)
print("srp返回的类型",type(result_raw));
print("srp返回的信息:",result_raw);
print("=================================");
print("读取tuple中的第一个元素:",result_raw[0]);
print("类型:",type(result_raw[0]));
print("通过res方法将这个scapy内置的类转换成一个由tuple组成的list");
print("=================res======================")
print(result_raw[0].res);
print("=================end======================");
#res返回的是一个list 可是这个list中只有一个tuple阿 [0] [0]指的是什么数据阿
print("通过getlayer(ARP).fields函数将结果转换为字典");
print(result_raw[0].res[0][1].getlayer(ARP).fields)
输出结果:
srp返回的类型
srp返回的信息: (, )
=================================
读取tuple中的第一个元素:
类型:
通过res方法将这个scapy内置的类转换成一个由tuple组成的list
=================res======================
[(>
, >>)]
=================end======================
通过getlayer(ARP).fields函数将结果转换为字典
{'hwtype': 1, 'ptype': 2048, 'hwlen': 6, 'plen': 4, 'op': 2, 'hwsrc': '00:0c:29:05:66:e5', 'psrc': '192.168.64.129', 'hwdst': '00:0c:29:b6:6b:7d', 'pdst': '192.168.64.128'}
根据赛题构造一个ARP包:
scapy:
arpPkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst='192.168.64.129',hwdst="ff:ff:ff:ff:ff:ff")
参数解释:
Ether()以太网包
dst=广播地址
ARP()ARP包
pdst=目标IP地址
hwdst=广播地址
演示scapy中ARP包结构:
发送ARP包:
res = srp1(arpPkt,timeout=1,verbose=False)
参数解释:
srp1 : Send and receive packets at layer 2 and return only the first answer
翻译来就是 在第2层发送和接收数据包,只返回第一个答案
timeout:设置超时时间
verbose:设置scapy的回显,False表示关闭 默认是开启的
查看srp1返回包的类型,这个比较关键
type(res);
可以发现这里返回包结构的数据类型是:scapy.layers.l2.Ether,之前使用srp接收数据包的类型是scapy.plist.SndRcvList
返回包的结构:
>>> res.show()
###[ Ethernet ]###
dst= 00:0c:29:b6:6b:7d
src= 00:0c:29:05:66:e5
type= 0x806
###[ ARP ]###
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= is-at
hwsrc= 00:0c:29:05:66:e5
psrc= 192.168.64.129
hwdst= 00:0c:29:b6:6b:7d
pdst= 192.168.64.130
###[ Padding ]###
load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>>
参数解释:
这里有一点需要注意,这个包是目标机器接收我们发送的arp包后返回来的包,怎么说呢,就是我们目标机器发给我们的一个包
所以这里的参数 pdst是我们自身的ip,hwdst是我们自身的mac地址,不能看包结构名是dst就觉得是目标的
而psrc是目标ip的ip地址,hwsrc是目标机器的mac地址 这点比较重要 太多数据很容易萌萌
验证结果是正确的:
有了上面发送单个IP的基础来看下赛题:
#encoding=utf-8
from scapy.all import *
importsysdefworker():
ip_list=[]for ipFix in range(1,Flag1):
ip= Flag2 +str(ipFix)
arpPkt= Flag6(dst=Flag3)/ARP(pdst=ip, hwdst="ff:ff:ff:ff:ff:ff")
res= Flag5(arpPkt, timeout=1, verbose=False)ifres:#print "IP: " + res.psrc + " MAC: " + res.hwsrc
ip_list.append(res.psrc)returnFlag4if __name__=="__main__":
fp= open('/root/ip.txt','w')
ip_list=worker()for ip inip_list:
fp.write(ip+'\n')print('over scan')
fp.close()#Flag1 = 255#Flag2 = "192.168.48."#Flag3 = "FF:FF:FF:FF:FF:FF" 广播地址#Flag4 = ip_list 返回这个list#Flag5 = srp1发包函数#Flag6 = Ether 二层发包需要添加以太网头部
逻辑分析:1.可写方式的打开一个文件/root/ip.txt2.worker函数分析:1.创建一个空的list2.用for in range 1-255循环3.然后字符串拼接成192.168.1.x的ip4.使用srp1构造arp数据包5.发送arp数据包返回结果存在res中6.如果res中成功接收到值,添加到list中7.循环完1-255返回存活主机的list3.将worker返回的list写入1打开的文件 OK.
为了巩固自己对这个库的认识和py代码能力的掌握,我写了一个小玩具~
多线程arp扫描:
#!/usr/bin/python3
from scapy.all import *
importsysimporttimeimportthreadingimportoptparse#增加多线程 为了线程同步 加了3个全局变量
liveHost_list = [] #存活主机
live_count = 0; #存活主机数量
liveHostPrint_list = [] #打印的时候用
defprintBanner():
banner= '''_ ____ ____ ____
/ \ | _ \| _ \/ ___| ___ __ _ _ __ _ __ ___ _ __
/ _ \ | |_) | |_) \___ \ / __/ _` | '_ \| '_ \ / _ \ '__|
/ ___ \| _
/_/ \_\_| \_\_| |____/ \___\__,_|_| |_|_| |_|\___|_|
v1.0 by r4bbit'''
print(banner);defget_current_time():
year= time.strftime('%Y-%m-%d',time.localtime());
minute= time.strftime('%H-%M-%S',time.localtime());return year+minute #返回时间
defarp_scan(ip):globalliveHostPrint_listgloballive_count ;globalliveHost_list
start_time=time.time();#构造arp数据包
time.sleep(0.001)
arpPkt= Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip,hwdst="ff:ff:ff:ff:ff:ff");#发送arp数据包
resPkt = srp1(arpPkt,timeout=1,verbose=False);#如果resPkt中返回的有包
ifresPkt:#print("[+] " + resPkt.psrc+" is Live"+" MAC:"+resPkt.hwsrc); #这里需要注意 因为是接受的包 所以需要打印出的是发送包的源ip和源mac
liveHost_list.append(resPkt.psrc); #存储到傻吊list中
print_tmp = "IP:"+resPkt.psrc+"MAC:"+resPkt.hwsrc
liveHostPrint_list.append(print_tmp);
live_count+=1;#else:
#print("[-] "+ip+" Not Alive");
defmain():
printBanner();globalliveHost_list;globallive_count;globalliveHostPrint_list;
parser= optparse.OptionParser("usage -i <192.168.1> 主要是我菜不会用netaddr库");
parser.add_option('-i',dest='target_ips',type='string',help='ip no');
(options,arg)=parser.parse_args()if(options.target_ips ==None):print(parser.usage);
exit(0);else:
target_ips=options.target_ips
start_time=time.time();for ip in range(1,255):
ip_str= target_ips+"."+str(ip);
scan_thread= threading.Thread(target=arp_scan,args=(ip_str,));
scan_thread.start();
end_time=time.time();#创建文件夹
#如果文件夹不存在
#扫描完成打印结果 然后存储文件
for ip inliveHostPrint_list:print(ip);print("Scan Done. Live Host Num:%d Use Time:%f s" % (live_count,end_time-start_time));if notos.path.exists(target_ips):
os.mkdir(target_ips)
log_name=get_current_time();
fp= open("./"+target_ips+"/"+log_name+".arp","w");print("Scan Log in"+target_ips);for i inliveHost_list:
fp.write(i+"\n");
fp.close();if __name__ == '__main__':
main()