【Python大師之路】簡單實現TCP代理

by - 凌晨3:44

在滲透測試的時候,常需要用到攔截請求的技巧,來達到監聽和分析請求等。在這個時候,我們可以建立一個中間的"轉運站"來達到上述目的,這就是Proxy的功能,Proxy就像是仲介一樣,使用者透過它向伺服器連線,伺服器也透過它向使用者傳送資料,三者之間關係如下:

 那在Python中要怎麼實現Proxy的功能呢?其實最簡單的解決方法就是用兩個socket,一個連接伺服器;一個連接使用者,當一邊有訊息傳入時,就將訊息傳送至另一邊,那不多說,直接開始設計程式。


main函式:
    if len(sys.argv[1:]) != 5:
        print("使用參數說明: ./tcp_proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]")
        print("範例: ./tcp_proxy.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])

    if "True" in sys.argv[5]:
        receive_first = True
    else:
        receive_first = False

    server_loop(local_host, local_port, remote_host, remote_port, receive_first)
用簡單的sys函數來取得使用者傳入的引數,程式需要5個引數,前兩個是使用者電腦的IP和端口,Proxy將在上面監聽,3、4個引數是伺服器端的IP和端口,將透過該資訊與伺服器建立連線。而最後一個引數是用來指定建立連線後要不要先從伺服器接收資料。

 在main函式中,我們最後呼叫了server_loop函式,該函式是用來處理連線的一個無窮迴圈:
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("[!!] 無法在 %s:%d 上開啟端口" % (local_host, local_port))
        print("[!!] 該端口可能已經被使用")
        sys.exit(0)
    print("[*] 代理啟動於: %s:%d" % (local_host, local_port))

    server.listen(5)

    while True:
        client_socket, addr = server.accept()

        print("[==>] 來自 %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()
server_loop函式會建立一個在本機監聽的socket,然後進入無窮迴圈不斷處理請求,如果有新的連線請求,就創建一個執行緒(不懂執行緒概念的讀者可以參考網路上的一些說明)來處理請求,對於每個連線要求的處理,我們獨立一個函式出來:
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)

        remote_buffer = response_handler(remote_buffer)

        if len(remote_buffer):
            print("[<==] 收到 %d 位元組的資料" % len(remote_buffer))
            client_socket.send(remote_buffer)

    while True:

        local_buffer = receive_from(client_socket)

        if len(local_buffer):
            print("[==>] 傳送 %d 位元組的資料" % len(local_buffer))

 
            local_buffer = request_handler(local_buffer)

            remote_socket.send(local_buffer)
            print("[==>] 傳送完成")


        remote_buffer = receive_from(remote_socket)
        if len(remote_buffer):
            print("[<==] 收到 %d 位元組的資料" % len(remote_buffer))

            remote_buffer = response_handler(remote_buffer)

            client_socket.send(remote_buffer)
            print("[<==] 接收完成")


        if not len(local_buffer) or not len(remote_buffer):
            client_socket.close()
            remote_socket.close()
            print("[*] 傳輸完成,關閉連線")
            break
其實現方式就是不斷檢查有沒有資料要傳輸,如果有的話叫傳到另一邊,而為了增加擴充性,我用request_handler和response_handler函式在傳送前處理一下資料,可以啥事都不做直接return,也可以在函式內對資料做竄改或攔截等。

為了簡潔,把接收數據獨立出來一個函式:
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
到這邊我們的Proxy基本上已經完成了但還有很多功能還沒實現,只能做簡單的測試等,就當一個python socket的練習吧!
完整程式碼下載:Github下載


你可能會喜歡

1 意見