• Python之端口扫描器编写


      其实,写个扫描器也挺好玩的,牵涉到了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数据包头部基本如上。可以结合

      create_tcp_header()和createTcpFlag()来操作数据包符号位,以实现基本功能3下的功能了。


      
  • 相关阅读:
    白话经典算法系列之中的一个 冒泡排序的三种实现
    Spring3.0 AOP 具体解释
    Android中Preference的使用以及监听事件分析
    Java中的匿名内部类
    【剑指offer】二叉树中和为某一值的路径
    Haskell 差点儿无痛苦上手指南
    Oracle Minus 取差集
    Windows Mobile 6.0 SDK和中文模拟器下载
    caffe源代码分析--math_functions.cu代码研究
    代码阅读分析工具Understand 2.0试用
  • 原文地址:https://www.cnblogs.com/0xJDchen/p/5954806.html
Copyright © 2020-2023  润新知