Python Twisted 中间人攻击实现

2 投票
2 回答
3720 浏览
提问于 2025-04-17 20:18

我需要做的是一种“中间人”的实现:我需要一个服务器,它可以接收来自客户端的连接(这些连接传输的是不同长度的二进制数据),然后把这些数据转发给它连接的另一个服务器(就像一个客户端一样),接着再把从那个服务器收到的数据发送回客户端。

这个服务器实际上就像是客户端和服务器之间的桥梁,负责传递它们之间交换的数据(这是一种流式数据,所以它会不断地从一边接收数据,然后发送到另一边)。
这个服务器是固定的,所以它的地址可以直接写死在代码里;但是当某个客户端断开连接时,这个服务器也必须断开与“真实”服务器的连接。

我一直在寻找解决方案,但找不到这样一个简单问题的例子。

我写的代码实际上是可以工作的,但我还没找到如何在服务器部分放一个引用,告诉它“这是你对应的客户端”,或者在客户端中放一个引用,告诉它“这是你的服务器”。这是我的代码:

#!/usr/bin/env python

from twisted.internet import protocol, reactor
from twisted.protocols import basic

client = None
server = None

class ServerProtocol(protocol.Protocol):
    def connectionMade(self):
        global server
        factory = protocol.ClientFactory()
        factory.protocol = ClientProtocol
        server = self
        reactor.connectTCP('localhost', 1324, factory)

    def dataReceived(self, data):
        global client
        client.transport.write(data)

class ClientProtocol(protocol.Protocol):
    def connectionMade(self):
        global client
        # Here's the instance of the client
        client = self

    def dataReceived(self, data):
        global server
        server.transport.write(data)

def main():
    import sys
    from twisted.python import log
    log.startLogging(sys.stdout)
    factory = protocol.ServerFactory()
    factory.protocol = ServerProtocol

    # Here's the instance of the server
    server = ServerProtocol

    reactor.listenTCP(2593, factory)
    reactor.run()

if __name__ == '__main__':
    main()

现在,关键是这个实例不能放在全局对象中,而应该放在这两个类里面:怎么做呢?

2 个回答

4

我自己解决了这个问题,为了将来参考(或者帮助其他遇到同样问题的人),我把我用来解决它的代码放在这里。

我觉得我自己的解决方案和jedwards提供的方案都可以用;现在我只需要再多研究一下他的方案,以确保我做的事情是正确的:这是我第一次使用Twisted框架,学习别人的解决方案是学习新东西的好方法! :)

#!/usr/bin/env python

from twisted.internet import protocol, reactor
from twisted.protocols import basic

class ServerProtocol(protocol.Protocol):
    def __init__(self):
        self.buffer = None
        self.client = None

    def connectionMade(self):
        factory = protocol.ClientFactory()
        factory.protocol = ClientProtocol
        factory.server = self

        reactor.connectTCP('gameserver16.gamesnet.it', 2593, factory)

    def dataReceived(self, data):
        if (self.client != None):
            self.client.write(data)
        else:
            self.buffer = data

    def write(self, data):
        self.transport.write(data)
        print 'Server: ' + data.encode('hex')

class ClientProtocol(protocol.Protocol):
    def connectionMade(self):
        self.factory.server.client = self
        self.write(self.factory.server.buffer)
        self.factory.server.buffer = ''

    def dataReceived(self, data):
        self.factory.server.write(data)

    def write(self, data):
        self.transport.write(data)
        print 'Client: ' + data.encode('hex')

def main():
    import sys
    from twisted.python import log

    log.startLogging(sys.stdout)

    factory = protocol.ServerFactory()
    factory.protocol = ServerProtocol

    reactor.listenTCP(2593, factory)
    reactor.run()

if __name__ == '__main__':
    main()
3

考虑一下这个方法

#!/usr/bin/env python

import sys
from twisted.internet import reactor
from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol
from twisted.protocols import basic
from twisted.python import log

LISTEN_PORT = 2593
SERVER_PORT = 1234


class ServerProtocol(Protocol):
    def connectionMade(self):
        reactor.connectTCP('localhost', SERVER_PORT, MyClientFactory(self))

    def dataReceived(self, data):
        self.clientProtocol.transport.write(data)

class ClientProtocol(Protocol):
    def connectionMade(self):
        # Pass ServerProtocol a ref. to ClientProtocol
        self.serverProtocol.clientProtocol = self;  

    def dataReceived(self, data):
        self.serverProtocol.transport.write(data)

class MyServerFactory(ServerFactory):
    protocol = ServerProtocol
    def buildProtocol(self, addr):
        # Create ServerProtocol
        p = ServerFactory.buildProtocol(self, addr)
        return p

class MyClientFactory(ClientFactory):
    protocol = ClientProtocol
    def __init__(self, serverProtocol_):
        self.serverProtocol = serverProtocol_

    def buildProtocol(self, addr):
        # Create ClientProtocol
        p = ClientFactory.buildProtocol(self,addr)
        # Pass ClientProtocol a ref. to ServerProtocol
        p.serverProtocol = self.serverProtocol
        return p

def main():
    log.startLogging(sys.stdout)

    reactor.listenTCP(LISTEN_PORT, MyServerFactory())
    reactor.run()

if __name__ == '__main__':
    main()

ServerProtocol 实例会把自己传给 MyClientFactory 的构造函数,这样 MyClientFactory 就知道它要和哪个 ServerProtocol 关联了。

同样,当 ClientProtocol 连接建立后,它会用自己对 ServerProtocol 的引用,告诉 ServerProtocol 它要使用哪个 ClientProtocol。

注意:这段代码没有错误检查,所以如果出现问题(比如,真实的服务器没有在监听),你可能会遇到 NoneType 的错误。

重要的代码行是:

reactor.connectTCP('localhost', SERVER_PORT, MyClientFactory(self))
#...
def __init__(self, serverProtocol_):
    self.serverProtocol = serverProtocol_

在这里,你把 ServerProtocol 的引用传给 MyClientFactory 的构造函数。它会把这个引用存储在一个成员变量中。这样,当客户端工厂创建 ClientProtocol 时,就可以把这个引用传递下去:

# Pass ClientProtocol a ref. to ServerProtocol
p.serverProtocol = self.serverProtocol

然后,一旦你的脚本和 真实 服务器建立了连接,事情就反过来了。ClientProtocol 会把自己的引用给 ServerProtocol:

# Pass ServerProtocol a ref. to ClientProtocol
self.serverProtocol.clientProtocol = self;

最后,两个协议会利用彼此存储的引用来发送接收到的数据:

def dataReceived(self, data):
    self.clientProtocol.transport.write(data)
#...
def dataReceived(self, data):
    self.serverProtocol.transport.write(data)

撰写回答