python通过scapy编写arp扫描器

多网卡的情况下发送二层包需要配置网卡

三层包不需要配置接口
发包方法:

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返回的类型 <class 'tuple'>
srp返回的信息: (<Results: TCP:0 UDP:0 ICMP:0 Other:1>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
=================================
读取tuple中的第一个元素: <Results: TCP:0 UDP:0 ICMP:0 Other:1>
类型: <class 'scapy.plist.SndRcvList'>
通过res方法将这个scapy内置的类转换成一个由tuple组成的list
=================res======================
[(<Ether  dst=FF:FF:FF:FF:FF:FF src=00:0c:29:b6:6b:7d type=0x806 |<ARP  op=who-has hwsrc=00:0c:29:b6:6b:7d psrc=192.168.64.128 hwdst=00:00:00:00:00:00 pdst=192.168.64.129 |>>
, <Ether  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.128 |<Padding  load='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00' |>>>)]
=================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);
<class 'scapy.layers.l2.Ether'>

可以发现这里返回包结构的数据类型是: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= 'x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00'
 
>>> 
参数解释:
这里有一点需要注意,这个包是目标机器接收我们发送的arp包后返回来的包,怎么说呢,就是我们目标机器发给我们的一个包
所以这里的参数 pdst是我们自身的ip,hwdst是我们自身的mac地址,不能看包结构名是dst就觉得是目标的
而psrc是目标ip的ip地址,hwsrc是目标机器的mac地址 这点比较重要 太多数据很容易萌萌

验证结果是正确的:

 

有了上面发送单个IP的基础来看下赛题:

#encoding=utf-8
from scapy.all import *
import sys
 
def worker():
    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)
        if res:
            #print "IP: " + res.psrc + "     MAC: " + res.hwsrc
            ip_list.append(res.psrc)
    return Flag4
if __name__=="__main__":
    fp = open('/root/ip.txt','w')
    ip_list = worker()
    for ip in ip_list:
        fp.write(ip+'
')
    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.txt 
2.worker函数分析:
    1.创建一个空的list
    2.用for in range 1-255 循环
    3.然后字符串拼接成192.168.1.x的ip
    4.使用srp1构造arp数据包
    5.发送arp数据包返回结果存在res中
    6.如果res中成功接收到值,添加到list中
    7.循环完1-255 返回存活主机的list
3.将worker返回的list写入1打开的文件 OK.

为了巩固自己对这个库的认识和py代码能力的掌握,我写了一个小玩具~

多线程arp扫描:

#!/usr/bin/python3
from scapy.all import *
import sys
import time
import threading
import optparse
#增加多线程 为了线程同步 加了3个全局变量
liveHost_list = [] #存活主机
live_count = 0; #存活主机数量
liveHostPrint_list = [] #打印的时候用
def printBanner():
    banner = '''
         _    ____  ____  ____                                  
   /   |  _ |  _ / ___|  ___ __ _ _ __  _ __   ___ _ __ 
  / _  | |_) | |_) \___  / __/ _` | '_ | '_  / _  '__|
 / ___ |  _ <|  __/ ___) | (_| (_| | | | | | | |  __/ |   
/_/   \_\_| \_\_|   |____/ \___\__,_|_| |_|_| |_|\___|_|   
                                                                                                       v1.0 by r4bbit
    '''
    print(banner);
 
def get_current_time():
    year = time.strftime('%Y-%m-%d',time.localtime());
    minute = time.strftime('%H-%M-%S',time.localtime());
    return year+minute #返回时间
 
def arp_scan(ip):
    global liveHostPrint_list
    global live_count ;
    global liveHost_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中返回的有包
    if resPkt:
       # 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");
 
 
 
def main():
    printBanner();
    global liveHost_list;
    global live_count;
    global liveHostPrint_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 in liveHostPrint_list:
            print(ip);
        print("Scan Done. Live Host Num:%d Use Time:%f s " % (live_count,end_time-start_time));
        if not os.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 in liveHost_list:
            fp.write(i+"
");
 
        fp.close();
 
if __name__ == '__main__':
    main()

 

原文地址:https://www.cnblogs.com/nul1/p/10990118.html