【绿冰壶的脚本小屋】第二期:基于icmp协议开发简易ping工具

【绿冰壶的脚本小屋】第二期:基于ICMP协议开发简易ping工具

任务目标:编写程序使用 ICMP 协议探测主机是否存活

任务要求:

1、理解ICMP协议的原理

2、实现代码,尽可能多的实现探测主机是否存活的功能

扩展任务:使用多线程技术提升探测速度

实验环境;python3 pycharm

前言

来到了脚本开发的第二部分,这次我们还是要从认识一个新的网络协议出发,开发相应的python脚本,并且学习如何使用多线程来提高效率.

ICMP协议

ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。ICMP使用IP的基本支持,就像ICMP是一个更高级别的协议,但是,ICMP实际上是IP的一个组成部分,必须由每个IP模块实现。

img

图文来源:百度百科

链接:https://baike.baidu.com/pic/ICMP/572452/0/dbf554ed9529cbccb31cb1d0?fr=lemma&ct=single#aid=0&pic=dbf554ed9529cbccb31cb1d0

icmp工作流程简介

让我们通过手绘的ICMP图了解一下ICMP协议的流程

未命名文件.png

ICMP报文情况

ICMP报文包含在IP数据报中,IP报头在ICMP报文的最前面。一个ICMP报文包括IP报头(至少20字节)、ICMP报头(至少八字节)和ICMP报文(属于ICMP报文的数据部分)。当IP报头中的协议字段值为1时,就说明这是一个ICMP报文。ICMP报头如下图所示。
如下图:

未命名文件 _1_.png

ICMP报文格式具体由RFC 777 [4] ,RFC 792 [2] 规范。

ICMP大概分为两类报文:
一类是通知出错原因 ;一类是用于诊断查询

以下是icmp基本报文类型表:

类型(十进制)内容
0回送应答
3目标不可达
4原点抑制
5重定向或改变路由
8回送请求
9路由器公告
10路由器请求
11超时
17地址子网请求
18地址子网应答

常见的icmp报文

相应请求

我们用的ping操作中就包括了相应请求(类型字段值为8)和应答(类型字段值为0)ICMP报文。
过程:
一台主机向一个节点发送一个类型字段值为8的ICMP报文,如果途中没有异常(如果没有被路由丢弃,目标不回应ICMP或者传输失败),则目标返回类型字段值为0的ICMP报文,说明这台主机存在。

目标不可达,源抑制和超时报文

这三种报文的格式是一样的。
(1)目标不可到达报文(类型值为3)在路由器或者主机不能传递数据时使用。
例如:我们要连接对方一个不存在的系统端口(端口号小于1024)时,将返回类型字段值3、代码字段值为3的ICMP报文。
常见的不可到达类型还有网络不可到达(代码字段值为0)、主机不可达到(代码字段值为1)、协议不可到达(代码字段值为2)等等。
(2)**源抑制报文(类型字段值为4,代码字段值为0)**则充当一个控制流量的角色,通知主机减少数据报流量。由于ICMP没有回复传输的报文,所以只要停止该报文,主机就会逐渐恢复传输速率。
(3)无连接方式网络的问题就是数据报会丢失,或者长时间在网络游荡而找不到目标,或者拥塞导致主机在规定的时间内无法重组数据报分段,这时就要触发ICMP超时报文的产生。
超时报文(类型字段值为11)的代码域有两种取值:代码字段值为0表示传输超时,代码字段值为1表示分段重组超时。

时间戳请求

时间戳请求报文(类型值字段13)和时间戳应答报文(类型值字段14)用于测试两台主机之间数据报来回一次的传输时间。
传输时,主机填充原始时间戳,接受方收到请求后填充接受时间戳后以类型值字段14的报文格式返回,发送方计算这个时间差。
(有些系统不响应这种报文)

脚本开发:实现探测主机存活

我们可以利用python自带的scapy模块中的内容实现ICMP协议的请求和答复

Scapy学习

Scapy是一个可以让用户发送、侦听和解析并伪装网络报文的python程序。这些功能可以用于制作侦测、扫描和攻击网络的工具。

换言之,Scapy 是一个强大的操纵报文的交互程序。它可以伪造或者解析多种协议的报文,还具有发送、捕获、匹配请求和响应这些报文以及更多的功能。Scapy 可以轻松地做到像扫描(scanning)、路由跟踪(tracerouting)、探测(probing)、单元测试(unit tests)、攻击(attacks)和发现网络(network discorvery)这样的传统任务。它可以代替hping,arpspoof,arp-sk,arping,p0f 甚至是部分的Namp,tcpdumptshark 的功能。

scapy信息

官网:scapy

用户手册:scapy docs

函数汇总
函数作用
数据包生成数据包
IPIP数据包
TCPTCP数据包,基于IP
ICMPICMP数据包,基于IP
srp(pkt)发送二层数据包,并等待响应。
srp1(pkt)发送第二层数据包,并返回响应的数据包
sr(pkt)发送三层数据包,返回两个结果,分别是接收到响应的数据包和未收到响应的数据包。
send发送数据包,三层
sendp发送数据包,两层
sr1发送三层数据包并接收一个数据包
scapy源码
>>> packet =Ether()/IP(src='192.168.1.113',dst='192.168.1.1')/ICMP()
>>> packet.show()
###[ Ethernet ]### 
  dst= 88:25:93:d2:6e:fa
  src= 00:0c:29:62:44:de
  type= 0x800
###[ IP ]### 
     version= 4
     ihl= None
     tos= 0x0
     len= None
     id= 1
     flags= 
     frag= 0
     ttl= 64
     proto= icmp
     chksum= None
     src= 192.168.1.113
     dst= 192.168.1.1
     \options\
###[ ICMP ]### 
        type= echo-request
        code= 0
        chksum= None
        id= 0x0
        seq= 0x0

>>> p = srp1(packet)
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
>>> p.show()
###[ Ethernet ]### 
  dst= 00:0c:29:62:44:de
  src= 88:25:93:d2:6e:fa
  type= 0x800
###[ IP ]### 
     version= 4
     ihl= 5
     tos= 0x0
     len= 28
     id= 36140
     flags= 
     frag= 0
     ttl= 64
     proto= icmp
     chksum= 0x69f2
     src= 192.168.1.1
     dst= 192.168.1.113
     \options\
###[ ICMP ]### 
        type= echo-reply
        code= 0
        chksum= 0xffff
        id= 0x0
        seq= 0x0
###[ Padding ]### 
           load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

>>> p.getlayer(ICMP)  #获取ICMP层
<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding  load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>
>>> p.getlayer(ICMP).fields['type'] #获取type的值
0

ipaddress模块

同时,python还自带ipaddress模块.不得不说python真是脚本开发yyds

>>> import ipaddress
>>> ip = list(ipaddress.ip_network('192.168.1.0/30'))
>>> ip
[IPv4Address('192.168.1.0'),
 IPv4Address('192.168.1.1'),
 IPv4Address('192.168.1.2'),
 IPv4Address('192.168.1.3')]
>>> for i in ip:
...:     print(i)
...:
192.168.1.0
192.168.1.1
192.168.1.2
192.168.1.3
>>> ip = ipaddress.ip_network('192.168.1.1')
>>> for i in ip:
...:     print(i)
...:
192.168.1.1

以上是我们需要使用到的自带模块的介绍,站在巨人的肩膀上,我们的 脚本编写工作就大大的简化了。

首先定义两个函数,实现icmp请求和主机存活探索

def icmp_rqst(ip_dst,iface=None)
    pkt=Ether()/IP(dst=ip_dst)/ICMP(type=8):
    res=srp1(pkt,timeout=2,verbose=False)
    return res

这个函数实现了icmp请求

pkt变量用于利用scary获取icmp报文.采用srp1的方式返回响应包赋值给res变量,最终将res变量返回。

def icmp_scan(ip_dst)
    req=icmp_request(ip_dst)
    if req:
        type =req.getlayer('ICMP').fields['type']
        print('[+]',ip_dst,':',type,'  Host is up')
    else:
    	pass

这个函数实现了主机存活探索。首先利用编写好的icmp_rqst函数获取icmp报文,然后通过if选择结构。存活的主机则输出host is up 。

扩展:增加扫描子网功能

def main(network)
    network= list(ipaddress.ip_network(network))
    for ip in network:
        icmp_scan(ip)

使用list函数将包含所有子网地址字符串变成每一个子网为一个元素的列表。然后枚举扫描。

list()函数

list()函数 将字符串变成列表

eg:

a = (1,2) #tuple
b = {"1":2,"3":3} #dict
c = {1,2,3}  #set
d = range(2,10,2) #range
print(list(a))
print(list(b))
print(list(c))
print(list(d))

输出:

[1, 2]
['1', '3']
[1, 2, 3]
[2, 4, 6, 8]

扩展:实现多线程提高效率

Python 命令行工具 argparse

argparse在我现在的理解中有三大功能

1用来实现通过命令行传参

2用来实现通过命令行定义函数

3用来定义参数

最基本的用法
import argparse


parser = argparse.ArgumentParser()
parser.parse_args()

当执行了 parse_args() 之后默认情况类似于这样:

img

在执行 parse_args() 之前,所有追加到命令行的参数都不会生效。所以说命令行工具argparse的使用声明一定要在所有需要这个我们通过命令行传入的函数之前

设置默认参数函数
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print args.echo
定义可选参数
import argparse


parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
    print "verbosity turned on"

如果用省略方法 -v赋值的话,最终–verbosity 值会传递到这个完整的参数中去,如果没有后面的 --verbosity 只有 -v 的话,那么值会可以通过 args.v 得到。

Python多线程编程 threading 模块
什么是线程

线程是操作系统能够进行运算调度最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

线程与进程

Python自己没有线程和进程,Python中调用的操作系统的线程和进程。

  1. 操作系统帮助开发者操作硬件。

    1. 程序员写好代码在操作系统上运行(依赖解释器)。

    进程与线程的区别:

    进程是cpu资源分配的最小单元,线程是cpu计算的最小单元。

    一个进程中可以有多个线程。

    对于python来说他的进程和线程和其他语言有差异是有GIL锁。GIL锁保证一个进程中同一时刻只有一个线程被cpu调度。

    python threading模块学习

    本文我们学习创建thread类,传递一个参数的基本方法。

    三部曲:

    首先实例化thread类

    其次start方法开始线程

    最后join方法可以让主线程等待所有的线程执行完毕

    1. def icmp_scan(network):
           threads = []
           length =len(network)
           for ip in network:
               t=threading.Thread(target=icmp_rqst,arg=(str(ip),))
               threads.append(t) #实例化2个Thread类,传递函数及其参数,并将线程对象放入一个列表中
           for i in range(length):
               threads[i].start() #循环 开始线程
           for i in range(length):
               threads[i].join() ##循环 join()方法可以让主线程等待所有的线程都执行完毕。
    
    

最终代码

本代码的多线程优化采取的是创建Thread类,传递一个函数的方法,我们先实例化thread类,然后执行线程。此时icmp_rqst 完成了生成icmp报告并且判断主机是否存活的双重功能。而scan函数用于实例化多个线程并且采用多线程执行。具体请看注释

# -*- coding:utf-8 -*-
from scapy.all import IP, ICMP, srp1,Ether
import threading
import argparse
import ipaddress
import os
import sys
#判断主机是否存活
def icmp_rqst(ip_dst,iface=None):
    pkt=Ether()/IP(dst=ip_dst)/ICMP(type=8)
    res=srp1(pkt,timeout=2,verbose=False)
    if res:
        print('[+]',ip_dst,':',type,'  Host is up')
def icmp_scan(network):
    threads = []
    length =len(network)
    for ip in network:
        t=threading.Thread(target=icmp_rqst,args=(str(ip),))
        threads.append(t)
    for i in range(length):
        threads[i].start()
    for i in range(length):
        threads[i].join()
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('network', help='eg:192.168.1.0/24')
    args = parser.parse_args()
    network= list(ipaddress.ip_network(args.network))
    icmp_scan(network)
if __name__ == '__main__':
  main()

报错解决:安装npcap

测试程序时出现了这个好讨厌的bug

image.png

上网搜了一波 winpcap早已不更新了,解决的办法很简单,安装Npcap

下载地址:

https://nmap.org/npcap/#download

进入官网后,win系统下载红线标注的即可

image.png

下载之后安装即可

安装完重新运行就没有报错啦。

测试结果

找到源程序文件夹。打开设置,找到路径

image.png

在命令行中进入该目录

image.png

由于pycharm运行不知道咋带参数,cmd又没装scapy,所以又跑去装了一波scapy,这里记录一下

scapy安装教程

在github上下载压缩包

下载路径:https://github.com/secdev/scapy

解压后 cd进入其目录,运行(我的python3默认为python。python2为python2)

Python setup.py install

等待安装即可

安装完成后,输入scapy出现下述页面即为安装成功。

image.png

突然想起上一次做红队任务时对同程进行了信息搜集,那就用同程的服务器测试一下我的工具把(嘻嘻嘻同程dbqdbq)

image-20210802160103181.png

先确认服务器能够ping通,再使用我们的工具。返回host is up。

后记

通过这次学习,我深入的了解了以下icmp协议的原理,并且利用icmp协议实现了 简单主机存活工具的开发。这次学习过程中我觉得我做的最好的地方是,我充分了解了代码的 每一块内容,而不是为了急于完成任务而囫囵吞枣。这让我收益颇丰,以后我也会坚持这个好的学习习惯,不仅满足与完成任务,更要深究其原理。提高自己的开发能力,不仅仅满足于做一个脚本小子。

ps:为了记录一下自己的脚本开发学习过程,与大家一同进步,我决定建设一个专栏【绿冰壶的脚本小屋】 以后会经常更新我python网络安全脚本开发的学习记录。欢迎大家关注与我一起学习进步。

第一期

[绿冰壶的脚本小屋]第一期:TCPUDP协议 与socket网络通信,学这一篇就够啦

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值