
在 twisted python 中的远程桌面协议。
RDPY 是一个纯 Python 实现的微软 RDP(远程桌面协议)协议(客户端和服务端)。RDPY 构建于基于事件驱动的网络引擎 Twisted 之上。RDPY 支持标准的 RDP 安全层、基于 SSL 的 RDP 以及 NLA 认证(通过 ntlmv2 认证协议)。
RDPY 提供了以下 RDP 和 VNC 二进制工具:
RDPY 完全用 Python 实现,但位图解压缩算法出于性能考虑用 C 实现。
以下依赖仅用于 pyqt4 二进制工具:
以基于 Debian 的系统为例:
sudo apt-get install python-qt4
以 OS X 上使用 homebrew 安装 PyQt 为例:
$ brew install qt sip pyqt
$ git clone https://github.com/citronneur/rdpy.git rdpy
$ pip install twisted pyopenssl qt4reactor service_identity rsa pyasn1
$ python rdpy/setup.py install
或者使用 PIP:
$ pip install rdpy
对于 virtualenv,你需要将 qt4 库链接到其中:
$ ln -s /usr/lib/python2.7/dist-packages/PyQt4/ $VIRTUAL_ENV/lib/python2.7/site-packages/
$ ln -s /usr/lib/python2.7/dist-packages/sip.so $VIRTUAL_ENV/lib/python2.7/site-packages/
RDPY 附带了一些非常有用的二进制工具。这些工具兼容 Linux 和 Windows。
rdpy-rdpclient 是一个简单的 RDP Qt4 客户端。
$ rdpy-rdpclient.py [-u username] [-p password] [-d domain] [-r rss_ouput_file] [...] XXX.XXX.XXX.XXX[:3389]
你可以在录制会话场景中使用 rdpy-rdpclient,该场景也用于 rdpy-rdphoneypot。
rdpy-vncclient 是一个简单的 VNC Qt4 客户端。
$ rdpy-vncclient.py [-p password] XXX.XXX.XXX.XXX[:5900]
rdpy-rdpscreenshot 将登录屏幕保存到文件中。
$ rdpy-rdpscreenshot.py [-w width] [-l height] [-o output_file_path] XXX.XXX.XXX.XXX[:3389]
rdpy-vncscreenshot 将第一个屏幕更新保存到文件中。
$ rdpy-vncscreenshot.py [-p password] [-o output_file_path] XXX.XXX.XXX.XXX[:5900]
rdpy-rdpmitm 是一个 RDP 代理,允许你在 RDP 协议上进行中间人攻击。 将会话场景记录到 rss 文件中,该文件可由 rdpy-rssplayer 回放。
$ rdpy-rdpmitm.py -o output_dir [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] [-r (for XP or server 2003 client)] target_host[:target_port]
输出目录用于保存 rss 文件,格式如下:(YYYYMMDDHHMMSS_ip_index.rss) 私钥文件和证书文件是用于 SSL 连接的标准加密文件。RDP 协议可以协商自己的安全层。如果省略这两个参数中的任意一个,服务器将使用标准的 RDP 作为安全层。
rdpy-rdphoneypot 是一个 RDP 蜜罐。使用录制的会话场景通过 RDP 协议重放场景。
$ rdpy-rdphoneypot.py [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] rss_file_path_1 ... rss_file_path_N
私钥文件和证书文件是用于 SSL 连接的标准加密文件。RDP 协议可以协商自己的安全层。如果省略这两个参数中的任意一个,服务器将使用标准的 RDP 作为安全层。 你可以指定多个文件以匹配更多常见的屏幕尺寸。
rdpy-rssplayer 用于回放由 rdpy-rdpmitm 或 rdpy-rdpclient 二进制工具生成的录制会话场景(rss)文件。
$ rdpy-rssplayer.py rss_file_path
RDPY 也可以通过 rdpy.ui.qt4.QRemoteDesktop 类作为 Qt 组件使用。它可以嵌入到你自己的 Qt 应用程序中。在你的应用中必须使用 qt4reactor 以使 Twisted 和 Qt 协同工作。更多详情,请参阅 rdpy-rdpclient 的源代码。
简而言之,RDPY 可以作为一个带有 twisted 引擎的协议库使用。
from rdpy.protocol.rdp import rdp
class MyRDPFactory(rdp.ClientFactory):
def clientConnectionLost(self, connector, reason):
reactor.stop()
def clientConnectionFailed(self, connector, reason):
reactor.stop()
def buildObserver(self, controller, addr):
class MyObserver(rdp.RDPClientObserver):
def onReady(self):
"""
@summary: 当协议栈准备就绪时调用
"""
#发送 'r' 键
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
#鼠标移动到 200x200 像素并点击
self._controller.sendPointerEvent(200, 200, 1, true)
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
"""
@summary: 通知位图更新
@param destLeft: xmin 位置
@param destTop: ymin 位置
@param destRight: xmax 位置(因为 RDP 可能会发送带有填充的位图)
@param destBottom: ymax 位置(因为 RDP 可能会发送带有填充的位图)
@param width: 位图宽度
@param height: 位图高度
@param bitsPerPixel: 每像素位数
@param isCompress: 使用 RLE 压缩
@param data: 位图数据
"""
def onSessionReady(self):
"""
@summary: Windows 会话准备就绪
"""
def onClose(self):
"""
@summary: 当协议栈关闭时调用
"""
return MyObserver(controller)
from twisted.internet import reactor
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389, MyRDPFactory())
reactor.run()
from rdpy.protocol.rdp import rdp
class MyRDPFactory(rdp.ServerFactory):
def buildObserver(self, controller, addr):
class MyObserver(rdp.RDPServerObserver):
def onReady(self):
"""
@summary: 当服务器准备好发送和接收消息时调用
"""
def onKeyEventScancode(self, code, isPressed):
"""
@summary: 当捕获到扫描码格式的键盘事件时调用的事件
@param code: 按键的扫描码
@param isPressed: 如果按键按下则为 True
@see: rdp.RDPServerObserver.onKeyEventScancode
"""
def onKeyEventUnicode(self, code, isPressed):
"""
@summary: 当捕获到 Unicode 格式的键盘事件时调用的事件
@param code: 按键的 Unicode 码
@param isPressed: 如果按键按下则为 True
@see: rdp.RDPServerObserver.onKeyEventUnicode
"""
def onPointerEvent(self, x, y, button, isPressed):
"""
@summary: 鼠标事件时调用的事件
@param x: x 位置
@param y: y 位置
@param button: 1, 2, 3, 4 或 5 按钮
@param isPressed: 如果鼠标按钮按下则为 True
@see: rdp.RDPServerObserver.onPointerEvent
"""
def onClose(self):
"""
@summary: 当人类客户端关闭连接时调用
@see: rdp.RDPServerObserver.onClose
"""
return MyObserver(controller)
from twisted.internet import reactor
reactor.listenTCP(3389, MyRDPFactory())
reactor.run()
from rdpy.protocol.rfb import rfb
class MyRFBFactory(rfb.ClientFactory):
def clientConnectionLost(self, connector, reason):
reactor.stop()
def clientConnectionFailed(self, connector, reason):
reactor.stop()
def buildObserver(self, controller, addr):
class MyObserver(rfb.RFBClientObserver):
def onReady(self):
"""
@summary: 当网络协议栈准备好接收或发送事件时
"""
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
"""
@summary: 实现 RFBClientObserver 接口
@param width: 新图像的宽度
@param height: 新图像的高度
@param x: 新图像的 x 位置
@param y: 新图像的 y 位置
@param pixelFormat: rfb.message.PixelFormat 中的像素格式结构
@param encoding: 编码类型 rfb.message.Encoding
@param data: 符合像素格式和编码的图像数据
"""
def onCutText(self, text):
"""
@summary: 当服务器发送剪切文本事件时
@param text: 接收到的文本
"""
def onBell(self):
"""
@summary: 当服务器发出哔哔声时
"""
def onClose(self):
"""
@summary: 当协议栈关闭时调用
"""
return MyObserver(controller)
from twisted.internet import reactor
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389, MyRFBFactory())
reactor.run()