
بروتوكول سطح المكتب البعيد في Twisted Python

بروتوكول سطح المكتب البعيد (RDP) في بايثون باستخدام Twisted.
RDPY هو تنفيذ خالص بلغة بايثون لبروتوكول Microsoft RDP (بروتوكول سطح المكتب البعيد) (جانب العميل والخادم). تم بناء RDPY فوق محرك الشبكة القائم على الأحداث Twisted. يدعم RDPY طبقة الأمان القياسية لـ RDP، و RDP عبر SSL، ومصادقة NLA (عبر بروتوكول مصادقة ntlmv2).
يوفر RDPY الثنائيات التالية لـ RDP و VNC:
تم تنفيذ RDPY بالكامل بلغة بايثون، باستثناء خوارزمية فك ضغط الصور النقطية (bitmap) والتي تم تنفيذها بلغة C لأغراض الأداء.
التبعيات مطلوبة فقط للثنائيات القائمة على PyQt4:
مثال للأنظمة المبنية على Debian:
sudo apt-get install python-qt4
مثال لتثبيت PyQt على OS X باستخدام Homebrew:
$ 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 مع بعض الثنائيات المفيدة جداً. هذه الثنائيات متوافقة مع لينكس وويندوز.
rdpy-rdpclient هو عميل RDP بسيط يعمل مع Qt4.
$ rdpy-rdpclient.py [-u username] [-p password] [-d domain] [-r rss_ouput_file] [...] XXX.XXX.XXX.XXX[:3389]
يمكنك استخدام rdpy-rdpclient في سيناريو جلسة التسجيل (Recorder Session Scenario)، المستخدم في 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 يسمح لك بتنفيذ هجوم Man In The Middle على بروتوكول 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 هو مصيدة (Honeypot) لـ 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 لإعادة تشغيل ملفات سيناريو الجلسة المسجلة (rss) التي تم إنشاؤها بواسطة ثنائيات rdpy-rdpmitm أو rdpy-rdpclient.
$ rdpy-rssplayer.py rss_file_path
يمكن أيضاً استخدام RDPY كـ Qt widget من خلال فئة rdpy.ui.qt4.QRemoteDesktop. يمكن تضمينها في تطبيق 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: جلسة ويندوز جاهزة
"""
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: حدث يتم استدعاؤه عند اكتشاف حدث لوحة المفاتيح بتنسيق scancode
@param code: رمز scancode للمفتاح
@param isPressed: صحيح إذا كان المفتاح مضغوطاً
@see: rdp.RDPServerObserver.onKeyEventScancode
"""
def onKeyEventUnicode(self, code, isPressed):
"""
@summary: حدث يتم استدعاؤه عند اكتشاف حدث لوحة المفاتيح بتنسيق unicode
@param code: رمز unicode للمفتاح
@param isPressed: صحيح إذا كان المفتاح مضغوطاً
@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: صحيح إذا تم ضغط زر الفأرة
@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: هيكل pixefFormat في 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()