Scrapy IP()类 编程指南(基础)

Scrapy IP()类 编程指南(基础)

IP简介

工欲善其事,必先利其器,在聊Scapy IP类时,我们先要了解IP是什么。

R-C

IP指的是Internet Protocol(互联网协议)的数据包。Internet Protocol是互联网上用于在网络中传输数据的一种协议。在TCP/IP协议族中,IP层负责数据包的路由和寻址,确保数据能够在网络中正确传递。

IP协议定义了一种在网络中唯一标识设备(主机或路由器)的方式,并提供了一种将数据分割成小的数据包进行传输的机制。每个数据包都包含源和目标设备的IP地址,以便路由器能够正确地将数据包从源传输到目标。

IP协议又分为IPv4IPv6,两个版本,IPv6可以理解为IPv4的升级版,但它们又有所不同,它的出现,是为了解决IPv4地址即将耗尽的问题,日后也会详细说明IPv6,今天我们主要的对象就是IPv4

在IPv4协议中,IP地址主要被分为五个类别,通常称为IP地址的分类。这些分类是基于地址的网络部分的位数,以及主机部分的位数。这五个主要的IP地址分类是:A类、B类、C类、D类和E类。

  1. 类A地址
    • 范围:1.0.0.0 到 126.255.255.255
    • 特点:第一个字节(8位)用于网络部分,剩余的三个字节(24位)用于主机部分。
    • 可用网络:2^7 - 2 = 126,因为0和127保留作为特殊用途。
  2. 类B地址
    • 范围:128.0.0.0 到 191.255.255.255
    • 特点:前两个字节(16位)用于网络部分,后两个字节(16位)用于主机部分。
    • 可用网络:2^14 - 2 = 16,382
  3. 类C地址
    • 范围:192.0.0.0 到 223.255.255.255
    • 特点:前三个字节(24位)用于网络部分,最后一个字节(8位)用于主机部分。
    • 可用网络:2^21 - 2 = 2,097,150
  4. 类D地址
    • 范围:224.0.0.0 到 239.255.255.255
    • 特点:用于多播(Multicast)通信,不分配给单个主机或网络。
  5. 类E地址
    • 范围:240.0.0.0 到 255.255.255.255
    • 特点:保留作为将来使用的实验和开发。

IP报文头

在Scapy中,IP报文头与IP()类直接相关。IP()类用于创建和处理IPv4报文头,它是Scapy中用于构建IPv4数据包的类。使用IP()类,我们可以轻松地定义IPv4数据包的各种属性,如源地址、目标地址、协议类型等。

在这里插入图片描述

版本(Version)

  • 占4位。
  • 指定IP协议的版本,IPv4的版本号为4。

头部长度(IHL - Internet Header Length)

  • 占4位。
  • 指定IPv4头部的长度,以32位字(4字节)为单位。由于IPv4头部中最少有20字节,因此该字段的值至少为5(表示20字节)。

服务类型(Type of Service - TOS)

  • 占8位。
  • 用于指定服务质量,包括优先级、延迟、吞吐量和可靠性。

总长度(Total Length)

  • 占16位。
  • 指定整个IPv4数据包的长度,包括头部和数据。最大长度为65,535字节。

标识(Identification)

  • 占16位。
  • 用于将相关的数据包片段组合成完整的数据包。

标志位(Flags)

  • 占3位。
  • 包含“不分片(Don’t Fragment)”和“更多片段(More Fragments)”标志。

片偏移(Fragment Offset)

  • 占13位。
  • 指定数据包片段在原始数据包中的偏移量。

生存时间(Time to Live - TTL)

  • 占8位。
  • 限制数据包在网络中的生存时间,每经过一个路由器,该字段值减一。当TTL为0时,数据包被丢弃。

协议(Protocol)

  • 占8位。
  • 指定上层协议,例如TCP(6)、UDP(17)、ICMP(1)等。

头部校验和(Header Checksum)

占16位。

用于检测IPv4头部的错误,主要是检测在传输过程中头部信息是否损坏。

源地址(Source Address)

占32位。

指定数据包的源IP地址。

目标地址(Destination Address)

占32位。

指定数据包的目标IP地址。

选项(Options)

可选字段,用于提供一些额外的信息。通常很少被使用,因为IPv4头部本身已经包含了足够的信息。

Scapy IP()使用

而在Scapy中,IP报文头与IP()类直接相关。IP()类用于创建和处理IPv4报文头,它是Scapy中用于构建IPv4数据包的类。

以下是IP()类的一些常用属性:

  1. src:指定源IP地址。
#两种方式指定
IP(src="x.x.x.x")#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.src = "x.x.x.x" #成员属性指定
  1. dst:指定目标IP地址
#两种方式指定
IP(dst="x.x.x.x")#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.dst = "x.x.x.x" #成员属性指定
  1. proto:指定上层协议,例如TCP、UDP等。
#两种方式指定
IP(proto="TCP")#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.proto = "TCP" #成员属性指定
  1. ttl:设置Time-to-Live值。
#两种方式指定
IP(ttl="5")#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.ttl = 5 #成员属性指定
  1. tos:设置Type of Service值。
#两种方式指定
IP(tos=0b10101010)#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.tos = 0b10101010 #成员属性指定
  1. ihl:设置IP报文头长度(通常不需要手动设置)。
#两种方式指定
IP(ihl=5)#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.ihl = 20 #成员属性指定
  1. flags:设置IP标志位。
#两种方式指定
IP(flags="MF")#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.src ="MF"#成员属性指定
  1. options:设置IP报文头的选项字段。
#两种方式指定
IP(options=[(1, 1, b'\x01')])#构造函数指定

ip_packet = IP() #构造空参数
ip_packet.src = [(1, 1, b'\x01')] #成员属性指定

Scapy IP() 实战使用

IP有这么多头部字节,我们用的最多也就是src源地址和dst目的地址,它们决定着,我们的数据包,由谁发起,又要发送到哪里。现在我们使用Scapy来构造一个IP头。

from scapy.layers.inet import IP, ICMP
from scapy.sendrecv import sr1

# 创建一个包含IP和ICMP协议的数据包,也就是ping包
ip_packet = IP(dst="192.168.30.55") / ICMP()

# 发送数据包并等待响应
response = sr1(ip_packet)

# 打印响应信息
response.show()

image-20240126220522919

如果我将src源IP地址,修改在发送,再来查看结果

from scapy.layers.inet import IP, ICMP
from scapy.sendrecv import sr1


ip_packet.src = '123.123.222.111'

send_ip = sr1(ip_packet)

image-20240126223609023

这里可能有人就有疑问了,源IP我明明已经修改为123.123.222.111,为什么还会有响应包?这里就牵扯到交换机的原理以及ARP的知识了。

交换机是一个二层设备,为什么说它是二层设备呢,因为交换机的基础功能就是处理OSI网络模型中的第二层(数据链路层),在数据链路层来说,它更关心是MAC物理地址的交换,它在工作过程中维护一张关键的表格。

MAC地址表:

  • 它是一个用于存储设备MAC地址与物理端口对应关系的表格。当交换机收到一个帧(数据包)时,它会查看帧中的目标MAC地址,并将这个MAC地址与接收到帧的端口进行关联,更新MAC地址表。这样,交换机就知道将数据帧发送到哪个端口,以便正确地转发数据。

MAC地址就是设备的独特标签,IP地址是用于在网络中找该设备的逻辑地址,而怎么通过设备的逻辑地址去找到这台设备呢,这就要通过ARP协议来解决了。ARP协议可以帮助你根据一个逻辑地址找到具体设备的物理设备,以便进行直接通信。在主机系统一般都会存在一个ARP表作用如下:

ARP表:

  • 它用于存储IP地址与对应的MAC地址之间的映射关系。当设备需要与网络中的其他设备通信时,它首先会查看自己的ARP表。如果在ARP表中找不到目标设备的IP地址对应的MAC地址,设备就会发起ARP请求,请求网络中其他设备告知它目标设备的MAC地址。

因为我们没有构造二层Ether包,所以这个数据包的mac地址会以默认接口的MAC地址认作缺省值(默认属性)进行发送,当这个包经过交换机时,交换机会读取该数据包的目的MAC信息,发现目的MAC信息全为F就为广播包,他就会向所有端口发送ARP广播报文,询问谁是192.168.30.55,,找到相应的接口进行二层转发,当数据包到达目的后,目标主机会先检查数据包的源MAC地址,并在本机的ARP表中查找对应的MAC与IP绑定关系,完成接收后,目标主机会根据请求包中的源IP、源MAC构造一个响应包发给源主机

这就解释了,为什么我随便设置的Ip还能得到响应报文的问题。

image-20240126234635509

最后给出一个由ScapyIP()类编写的超级Ping工具:

# coding=utf-8
"""
@Author: 迪奥布斯
@create time : 2024/1/27
@超级ping
"""
import re
import time

from scapy.config import conf
from scapy.layers.inet import IP, ICMP
from scapy.sendrecv import sr, sr1

ips = []


def order_ip(ip):
    ip_source, ip_num = ip.rsplit('.', 1)
    start, end = ip_num.split('-')
    if start > end:
        tmp = start
        start = end
        end = tmp
    ip_list = [ip_source + '.' + str(num) for num in range(int(start), int(end))]
    return ip_list


def num_ip(ip):
    b = ip.split(',')
    a = set(b)
    return list(a)


def ip_run(ip):
    return [ip]


def check_ip_format(ip):
    patterns = {
   
        "ipv4": (re.compile(r'^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$'), ip_run),
        "ip_range": (re.compile(
            r'^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}-(25[1-5]|2[0-4]\d|1\d\d|[1-9]?\d)$'),
                     order_ip),
        "multiple_ips": (re.compile(
            r'(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}-(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)|(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})(?:,(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}-(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)|(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))*$'
        ), num_ip),
    }
    matched = False

    for pattern_type, (pattern, fun) in patterns.items():
        if pattern.match(ip):
            ips.append(fun(ip))
            matched = True
            break
    if not matched:
        print("输入的IP无效,请重新输入!")
        exit()


def send_icmp(ip, interface):
    # 构建 ICMP 请求包
    ping_pkt = IP(dst=ip) / ICMP(id=1000)

    try:
        # 发送 ICMP 请求并等待响应 n
        time.sleep(0.5)
        start_time = time.time()
        ip_response = sr1(ping_pkt, timeout=1, verbose=False, iface=interface)
        end_time = time.time()
        if ip_response is not None:
            ttl_sys = {
   255: 'Ulinx系统(交换机、路由器)',
                       128: 'Windos系统',
                       64: 'MacOS/Linux'}
            for ttl_num, sys_name in ttl_sys.items():
                if ip_response[IP].ttl == ttl_num:
                    sys = sys_name
            print(
                f'{
     ip_response[IP].src} ---> 可达,{
     ip_response[IP].ttl} ---> 跳数,它是{
     sys},花费 {
     (end_time - start_time) * 1000:0.1f} 毫秒')
        else:
            print(f'{
     ip} ---> 无响应')

    except Exception as e:
        print(f'发生错误: {
     e}')
        exit()


def start_main(ip, num, interface):
    check_ip_format(ip)
    if len(ips[0]) < 2:
        a = num * len(ips[0])
        b = a - num
        while not a == b:
            send_icmp(ips[0][0], interface)
            a -= 1
    elif len(ips[0]) >= 2:
        a = num * len(ips[0])
        for ip_num in ips[0]:
            b = a - num
            while not a == b:
                send_icmp(ip_num, interface)
                a -= 1


def tiShi():
    print("*" * 80)
    print("目前可以实现功能,多IP范围ping主机判断主机系统")
    print("IP输入格式为:")
    print("单个IP:192.168.0.1")
    print("范围IP:192.168.0.1-255")
    print("多个IP:192.168.0.1,192.168.0.2")
    print("ping的次数,默认为4次")
    print("指定网卡接口:不指定为默认网卡")
    print("指定接口为网卡名称,例如:windows的网卡名称“以太网”或“以太网1,linux的网卡名”eth0“或”ens0“")
    print("*" * 80)


def user_input():
    tiShi()
    while True:
        target_ip = input('请输入你要ping的ip:')
        target_num = input('请输入要ping的次数:').strip()
        if not target_num or not target_num.isdigit():
            ai = 4
            print(f"输入为空,设置默认值为: {
     ai}", end='')
        else:
            try:
                ai = int(target_num)
                print(f"转换后的整数值为: {
     ai}", end='')
            except ValueError:
                print("输入无效,无法转换为整数。", end='')
        print(target_num)
        target_interface = input("请输入网卡名称:")
        if target_interface == '':
            target_interface = conf.iface
            print('输入为空为默认网卡:', conf.iface.name)
        start_main(target_ip, ai, target_interface)


if __name__ == '__main__':
    user_input()


相关推荐

  1. QML基础类型之Size - 编程指南

    2024-01-28 14:50:05       40 阅读
  2. C++模板编程—学习C++库的编程基础

    2024-01-28 14:50:05       11 阅读
  3. 突破编程_C++_基础教程(基础知识)

    2024-01-28 14:50:05       29 阅读
  4. AI学习指南-学习AI需要的编程基础

    2024-01-28 14:50:05       9 阅读
  5. 突破编程_C++_基础教程(指针(一))

    2024-01-28 14:50:05       25 阅读
  6. C++ 和对象:面向对象编程基础

    2024-01-28 14:50:05       9 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-28 14:50:05       17 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-28 14:50:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-28 14:50:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-28 14:50:05       18 阅读

热门阅读

  1. 1170. 排队布局(差分约束,spfa,负环)

    2024-01-28 14:50:05       37 阅读
  2. python 调用dll

    2024-01-28 14:50:05       32 阅读
  3. Vue组件

    2024-01-28 14:50:05       31 阅读
  4. 使用spark mllib训练中文文本分类器的

    2024-01-28 14:50:05       38 阅读
  5. 39. 组合总和

    2024-01-28 14:50:05       38 阅读
  6. VLM 系列——BLIP——论文解读

    2024-01-28 14:50:05       43 阅读