Python之Scanner编写

  嗯!拖延症晚期,已经无可救药,熬了两个晚上的夜,基本也把计算机网络课程设计的网络扫描器的功能实现了。

  其实,写个扫描器也挺好玩的,牵涉到了RAW Socket编程,可以尽情地DIY数据包(当然,不符合数据包规则,比如checksum错误就没办法了),收获颇深。其中,我觉得用C语言写更有利于在编写过程中对加深对计算机网络的理解,特别是数据包细节。但是由于效率问题,还有Python真是太好用了(自从用了python,日常再也不想去碰C/C++了,虽然python也写的挺烂的)。话不多说,言归正传。

  学习信息安全的自然听说过nmap这种网络扫描神器,其中功能选项多,老少咸宜,不仅能满足网络管理员的日常,还能满足网络安全工程师的渗透测试,这个课程设计程度的扫描器自然不会有那么多功能,主要实现利用TCP和UDP的一些特性的进行IP段主机存活情况以及端口扫描。

  基本功能如下:

    1.发送udp包,检测一个极少使用的端口,对回传的ICMP包的进行分析,从而判断主机是否存活。

    2.利用TCP三次握手,通过是否连接成功,来判定端口是否开放,其中采用了多线程加快了扫描速度。

    3.通过RAW Socket的原生编程,对TCP标志位进行人工设置,对回复数据包的标志位进行分析,从而不需要TCP三次握手就可以对端口是否开放进行判定。部分方式如下

      i:通过SYN置1,检测回传的数据包的标志位是否为SYN/ACK

      ii:通过ACK置1,查看是否回传数据包,且数据包的标志位是否为RST

      iii:通过将所有标志位都置0,查看是否回传数据包,且数据包的标志位是否为RST

      iv:通过FIN+URG+PSH置1,查看是否回传数据包,且数据包的标志位是否为RST

  在编写功能之前,有必要写对IP,ICMP,TCP的包头进行解析,直接看代码:

  IP数据包头:

_fields_ = [
        ("ihl",           c_ubyte, 4),
        ("version",       c_ubyte, 4),
        ("tos",           c_ubyte),
        ("len",           c_ushort),
        ("id",            c_ushort),
        ("offset",        c_ushort),
        ("ttl",           c_ubyte),
        ("protocol_num",  c_ubyte),
        ("sum",           c_ushort),
        ("src",           c_ulong),
        ("dst",           c_ulong)
    ]

  ICMP数据包头:

_fields_ = [
        ("type",         c_ubyte),
        ("code",         c_ubyte),
        ("checksum",     c_ushort),
        ("unused",       c_ushort),
        ("next_hop_mtu", c_ushort)
        ]

  TCP数据包头:

 _fields_ = [
        ("src_port",         c_ushort),
        ("dst_port",         c_ushort),
        ("seq",     c_ulong),
        ("ack_seq",       c_ulong),
        ("offset",  c_ubyte),
        ("flag", c_ubyte),
        ("windows", c_ushort),
        ("checksum", c_ushort),
        ("point", c_ushort),
    ]

  TCP数据包头的 offset,flag 并不是真正的数据包结构,但是由于单字节细节处不好处理,直接写成上文那样了,所有结构非一言两语可以说完的,详情可参考《IP/TCP详解》。

  1.先从最简单的TCPconnect多线程端口扫描开始,基于部分防火墙的策略,应该对需要扫描的端口区间进行随机分配算法,来干扰防火墙的判断。当然,由于太懒了,就直接一路扫下去了。具体代码如下:

def portTest(ip,port,num):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    i  = 0
    while i <num:
        try:
            myport = i + port
            s.connect(( "%s" %ip, myport ))
            s.close()
            print "%s:%d is open" % (ip,myport)
        except BaseException,e:    
            pass
        i = i+1

def TcpConnect(subnet,port,num=1):
    Port = int(port)
    if num > 8:
        for ip in IPNetwork(subnet):
            for i in range(0,THREADNUM):
                t = threading.Thread(target=portTest, args=(ip,Port+i*num/THREADNUM,num/THREADNUM))  #开了8个线程,
                t.start()
    else:
        for ip in IPNetwork(subnet):    #方便对区段进行扫描
            portTest(ip,Port,num)

  总共开了八个线程,将端口段分成8份进行扫描。通过异常来退出对位打开的端口的连接,但是实际使用中,容易被网络发现,这种方法只能说是最为简单,但是不推荐使用。

  2.利用udp进行扫描。

  这里需要涉及到RAW socket的编程,《python黑帽》里关于udp扫描的代码写的非常好(其它的代码也写的不错,在里面也学习了很多python的技巧)。基本原理就是通过setsocketopt函数来设置网卡的混杂模式。进行嗅探,直接贴里面的代码:

def udp_sender(subnet,magic_message):
    time.sleep(5)
    sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    for ip in IPNetwork(subnet):
        try:
            sender.sendto(magic_message, ("%s" % ip, 65211))  #对每个ip地址进行发包
        except:
            pass

def ICMPecho(subnet):
    if  os.name == "nt":
        socket_protocol = socket.IPPROTO_IP
    else:
        socket_protocol = socket.IPPROTO_ICMP
    sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
    sniffer.bind((host, 0))
    sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
    if os.name == "nt":  #跨平台必备
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
    t = threading.Thread(target=udp_sender, args=(subnet,magic_message))  #线程用于发送数据包
    t.start()
    try:
        while True:
            raw_buffer =  sniffer.recvfrom(65565)[0]  #对收到的数据包进行检测
            ip_header = IP(raw_buffer[0:20])
            if ip_header.protocol == "ICMP":
                offset = ip_header.ihl * 4
                buf = raw_buffer[offset:offset+sizeof(ICMP)]
                icmp_header = ICMP(buf)
                if icmp_header.type == 3 and icmp_header.code == 3:
                    if IPAddress(ip_header.src_address) in IPNetwork(subnet):
                        if raw_buffer[len(raw_buffer) - len(magic_message):] == magic_message:
                            print "Host Up: %s" % ip_header.src_address
    except  KeyboardInterrupt:
        if os.name == "nt":
            sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)  

  3:最好玩的当然是构造数据包,通过自己构造数据包可以做很多非常geek的事情,比如DNS欺骗,ARP欺骗,SYN洪泛等等。先来看看怎么构造标志位。

def createTcpFlag(fin=0,syn=0,rst=0,psh=0,ack=0,urg=0):
    tcp_flags = fin + (syn<<1) + (rst<<2) + (psh<<3) + (ack<<4) + (urg<<5)
    return tcp_flags

  简单的移位操作就可以实现了对符号位的操作了。

   再看看怎么创建TCP数据包头。

def create_tcp_header(source_ip, dest_ip, dest_port,tcp_flag):
    source = random.randrange(32000,62000,1)
    seq = 0
    ack_seq = 0
    doff = 5
    window = socket.htons (8192)
    check = 0 #先将数据包的校验位置0
    urg_ptr = 0
    offset_res = (doff << 4) + 0
    tcp_flags = tcp_flag
    tcp_header = struct.pack(‘!HHLLBBHHH‘, source, dest_port, seq, ack_seq, offset_res, tcp_flags, window, check, urg_ptr)    #TCP头在进行校验和时,需要有一个伪IP头,基本细节如下
    source_address = socket.inet_aton( source_ip )
    dest_address = socket.inet_aton( dest_ip )
    placeholder = 0
    protocol = socket.IPPROTO_TCP
    tcp_length = len(tcp_header)
    psh = struct.pack(‘!4s4sBBH‘, source_address, dest_address, placeholder, protocol, tcp_length);
    psh = psh + tcp_header;
    tcp_checksum = checksum(psh)
    tcp_header = struct.pack(‘!HHLLBBHHH‘, source, dest_port, seq, ack_seq, offset_res, tcp_flags, window, tcp_checksum, urg_ptr)
    return tcp_header

  IP数据包头部基本如上,详情请见文尾的github的地址。可以结合

  create_tcp_header()和createTcpFlag()来操作数据包符号位,以实现基本功能3下的功能了。  
时间: 2024-10-12 20:06:47

Python之Scanner编写的相关文章

用python + hadoop streaming 编写分布式程序(二) -- 在集群上运行与监控

写在前面 前文:用python + hadoop streaming 编写分布式程序(一) -- 原理介绍,样例程序与本地调试 为了方便,这篇文章里的例子均为伪分布式运行,一般来说只要集群配置得当,在伪分布式下能够运行的程序,在真实集群上也不会有什么问题. 为了更好地模拟集群环境,我们可以在mapred-site.xml中增设reducer和mapper的最大数目(默认为2,实际可用数目大约是CPU核数-1). 假设你为Hadoop安装路径添加的环境变量叫$HADOOP_HOME(如果是$HAD

Python学习(三):入门篇:Python中怎么编写类

Python中怎么编写类 Last Edit 2013/5/2 先看一个例子: #person.py class person: """class to representaion a person""" def __init__(self,name,age): self.name=name if 0<age<=150: self.age=age else: print 'age is no valid!' def display(s

用python + hadoop streaming 编写分布式程序(三) -- 自定义功能

又是期末又是实训TA的事耽搁了好久……先把写好的放上博客吧 前文: 用python + hadoop streaming 编写分布式程序(一) -- 原理介绍,样例程序与本地调试 用python + hadoop streaming 编写分布式程序(二) -- 在集群上运行与监控 使用额外的文件 假如你跑的job除了输入以外还需要一些额外的文件(side data),有两种选择: 大文件 所谓的大文件就是大小大于设置的local.cache.size的文件,默认是10GB.这个时候可以用-fil

Python使用TCPServer编写(多线程)Socket服务

SocketServer包对socket包进行了包装(封装),使得创建socket服务非常简单. TCPServer+BaseRequestHandler 使用TCPServer和BaseRequestHandler编写socket服务的样例. #-*- coding:utf-8 -*- from SocketServer import TCPServer, BaseRequestHandler import traceback class MyBaseRequestHandlerr(BaseR

Python网络编程——编写一个简单的回显客户端/服务器应用

今天将python中socket模块的基本API学习完后,照着书上的实例编写一个套接字服务器和客户端.采用python3.5版本,在注释中会标明python2和python3的不同之处. 1.代码 (1)服务器端及对应代码解释 1 # ! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # 编写回显服务器 4 5 import socket 6 import sys 7 import argparse 8 9 # 定义常量 10 host = 'l

python练习之编写登录接口

一.需求 编写一个登录接口,要求如下: 不管是输错密码或者用户名,都被视为一次尝试 当输入用户名或者密码错误时,输出错误提示 尝试过三次后,被锁定 二.代码 #!/usr/bin/env python #_*_coding:utf-8_*_ try_time = 0 while try_time < 3:     user = raw_input("input the username:")     if user == 'mangguo': #当用户名正确后,再提示输入密码  

python:使用ftplib编写FTP客户端

Python中的ftplib模块 Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件 FTP的工作流程及基本操作可参考协议RFC959 ftp登陆连接 from ftplib import FTP #加载ftp模块 ftp=FTP() #设置变量 ftp.set_debuglevel(2) #打开调试级别2,显示详细信息 ftp.connect("IP","port") #连接的ftp sever和

Python——类代码编写细节

类代码编写细节 继续学习类.方法和继承. ================================================================================ class语句 以下是class语句的一般形式: class <name>(superclass,...): data = value def method(self,...): self.member = value 在class语句内,任何赋值语句都会产生类属性,而且还有特殊名称方法重载运

python程序的编写简单介绍

一.语句和语法 # 注释 \  转译回车,继续上一行,在一行语句较长的情况下可以使用其来切分成多行,因其可读性差所以不建议使用 : 将两个语句连接到一行,可读性差,不建议使用 : 将代码的头和体分开 语句(代码块)用缩进方式体现不同的代码级别,建议采用4个空格(不要使用tab),因为不同编程语言环境下tab所代表的空格数不一定是4 python文件以模块的方式组织,编写一个.py结尾的文件实际上就写了一个模块 二.变量定义与赋值 a=1:1为内存变量存放于内存中,a为变量的引用,python为动