diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..43ae0e2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+__pycache__/
+*.py[cod]
diff --git a/ctrlsrv.py b/ctrlsrv.py
new file mode 100755
index 0000000..cd7fcd3
--- /dev/null
+++ b/ctrlsrv.py
@@ -0,0 +1,327 @@
+#!/usr/bin/env python3
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import pyvncs
+from lib import log
+from lib import common
+from argparse import ArgumentParser
+from threading import Thread
+import time
+import sys
+import socket
+import signal
+import readline
+import traceback
+
+#_debug = log.debug
+_debug = print
+
+if common.isWindows():
+ _debug("Wintendo...")
+ import win32ts
+ import win32security
+ import win32con
+ import win32api
+ import ntsecuritycon
+ import win32process
+ import win32event
+
+def signal_handler(signal, frame):
+ _debug("Exiting on signal %s..." % signal)
+ sys.exit(0)
+
+signal.signal(signal.SIGINT, signal_handler)
+
+config = {
+}
+
+class ControlThread(Thread):
+ def __init__(self, threads):
+ Thread.__init__(self)
+ self.threads = threads
+ self.setDaemon(True)
+
+ def run(self):
+ # elimina los threads muertos
+ while True:
+ time.sleep(1)
+ for t in threads:
+ if not t.isAlive():
+ _debug("ControlThread removing dead", t)
+ threads.remove(t)
+
+class VNCThread(Thread):
+ def __init__(self, port, password):
+ Thread.__init__(self)
+ self.ip = None
+ self.port = port
+ self.sock = None
+ self.password = password
+ self.setDaemon(True)
+
+ def __del__(self):
+ _debug("VNCThread died")
+
+
+ def run(self):
+
+ TCP_IP = '0.0.0.0'
+ _debug("[+] Listen...")
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.sock.bind((TCP_IP, int(self.port)))
+ self.sock.listen(4)
+ (conn, (ip,port)) = self.sock.accept()
+
+ _debug("[+] New server socket started for " + ip + ":" + str(port))
+ #_debug("Thread", self)
+ server = pyvncs.server.VncServer(conn, self.password)
+ #server.CONFIG._8bitdither = CONFIG._8bitdither
+ status = server.init()
+
+ if not status:
+ _debug("Error negotiating client init")
+ return False
+ server.protocol()
+
+
+class ClientThread(Thread):
+ def __init__(self, sock, ip, port, config):
+ Thread.__init__(self)
+ self.ip = ip
+ self.port = port
+ self.sock = sock
+ self.setDaemon(True)
+ self.config = config
+
+ def __del__(self):
+ _debug("ClientThread died")
+
+ def run(self):
+ #_debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
+ #_debug("Thread", self)
+ f = self.sock.makefile('rw')
+
+ f.write("AUTH:>")
+ f.flush()
+ passwd = f.readline().strip("\n")
+ if passwd != config["PASSWORD"]:
+ time.sleep(1)
+ f.write("!NO AUTH")
+ f.flush()
+ _debug("NO AUTH '%s' != '%s'" % (passwd, config["PASSWORD"]))
+ self.sock.close()
+ return
+
+ while True:
+ f.write("OK:>")
+ f.flush()
+ try:
+ data = f.readline()
+ cmd = data.strip()
+ if not data: break
+
+ if cmd == "_DEBUG":
+ sys.stdout = sys.stderr = f
+ f.write("OK\n")
+
+ elif cmd == "PING":
+ f.write("PONG\n")
+
+ elif cmd == "QUIT":
+ f.write("BYE\n")
+ self.sock.close()
+ return
+
+ elif cmd.startswith("STARTVNC"):
+ params = cmd.split()
+ if len(params) != 3:
+ f.write("!NOT_PARAMS\n")
+ f.flush()
+ continue
+ _debug("START VNC !!!")
+ newthread = VNCThread(params[1], params[2])
+ newthread.setDaemon(True)
+ newthread.start()
+
+ elif cmd == "_WINSESSIONS" and common.isWindows():
+ winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
+ print(winsessions, file=f)
+
+ elif cmd == "_WINCONSOLE" and common.isWindows():
+ winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
+ print(winsessions, file=f)
+ active = win32ts.WTSGetActiveConsoleSessionId()
+ print(active, file=f)
+ token = win32ts.WTSQueryUserToken(active)
+ print(token, file=f)
+ duplicated = win32security.DuplicateTokenEx(token, win32con.MAXIMUM_ALLOWED, win32con.NULL, win32security.TokenPrimary, win32security.SECURITY_ATTRIBUTES())
+ print("duplicated", token, file=f)
+
+ elif cmd == "_TEST" and common.isWindows():
+ winsessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
+ print(winsessions, file=f)
+ active = win32ts.WTSGetActiveConsoleSessionId()
+ print(active, file=f)
+ token = win32ts.WTSQueryUserToken(active)
+ print("token", token, file=f)
+ ntoken = win32security.DuplicateTokenEx(token, 3 , win32con.MAXIMUM_ALLOWED , win32security.TokenPrimary , win32security.SECURITY_ATTRIBUTES() )
+ print("ntoken", ntoken, file=f)
+ #th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32con.MAXIMUM_ALLOWED)
+ #print("th", th, file=f)
+ shell_as2(ntoken, True, "cmd")
+
+ elif cmd == "_EVAL":
+ while True:
+ f.write("EV:>")
+ f.flush()
+ data = f.readline()
+ cmd = data.strip()
+ if cmd == "": continue
+ if cmd == "_QUIT": break
+ if not data: break
+
+ try:
+ eval(data)
+ except:
+ print("ERROR:", sys.exc_info()[0], file=f)
+ print(traceback.format_exc(), file=f)
+ f.flush()
+
+ _debug("eval:", cmd.strip())
+ f.flush()
+
+ elif cmd == "_ERROR":
+ a = 1/0
+
+ else:
+ f.write("!NO_CMD %s\n" % cmd)
+
+ _debug("command:", cmd.strip())
+ f.flush()
+ except:
+ print("ERROR:", sys.exc_info()[0], file=f)
+ print(traceback.format_exc(), file=f)
+ f.flush()
+
+def get_all_privs(th):
+ # Try to give ourselves some extra privs (only works if we're admin):
+ # SeBackupPrivilege - so we can read anything
+ # SeDebugPrivilege - so we can find out about other processes (otherwise OpenProcess will fail for some)
+ # SeSecurityPrivilege - ??? what does this do?
+
+ # Problem: Vista+ support "Protected" processes, e.g. audiodg.exe. We can't see info about these.
+ # Interesting post on why Protected Process aren't really secure anyway: http://www.alex-ionescu.com/?p=34
+
+ privs = win32security.GetTokenInformation(th, ntsecuritycon.TokenPrivileges)
+ for privtuple in privs:
+ privs2 = win32security.GetTokenInformation(th, ntsecuritycon.TokenPrivileges)
+ newprivs = []
+ for privtuple2 in privs2:
+ if privtuple2[0] == privtuple[0]:
+ newprivs.append((privtuple2[0], 2)) # SE_PRIVILEGE_ENABLED
+ else:
+ newprivs.append((privtuple2[0], privtuple2[1]))
+
+ # Adjust privs
+ privs3 = tuple(newprivs)
+ win32security.AdjustTokenPrivileges(th, False, privs3)
+def shell_as(th, enable_privs = 0):
+ #t = thread(th)
+ #print(t.as_text())
+ new_tokenh = win32security.DuplicateTokenEx(th, 3 , win32con.MAXIMUM_ALLOWED , win32security.TokenPrimary , win32security.SECURITY_ATTRIBUTES() )
+ print("new_tokenh: %s" % new_tokenh)
+ print("Impersonating...")
+ if enable_privs:
+ get_all_privs(new_tokenh)
+ commandLine = "cmd"
+ si = win32process.STARTUPINFO()
+ print("pysecdump: Starting shell with required privileges...")
+ (hProcess, hThread, dwProcessId, dwThreadId) = win32process.CreateProcessAsUser(
+ new_tokenh,
+ None, # AppName
+ commandLine, # Command line
+ None, # Process Security
+ None, # ThreadSecurity
+ 1, # Inherit Handles?
+ win32process.NORMAL_PRIORITY_CLASS,
+ None, # New environment
+ None, # Current directory
+ si) # startup info.
+ win32event.WaitForSingleObject( hProcess, win32event.INFINITE );
+ print("pysecdump: Quitting")
+
+def shell_as2(new_tokenh, enable_privs = 0, commandLine = "cmd"):
+ print("new_tokenh: %s" % new_tokenh)
+ print("Impersonating...")
+ if enable_privs:
+ get_all_privs(new_tokenh)
+ si = win32process.STARTUPINFO()
+ print("pysecdump: Starting shell with required privileges...")
+ (hProcess, hThread, dwProcessId, dwThreadId) = win32process.CreateProcessAsUser(
+ new_tokenh,
+ None, # AppName
+ commandLine, # Command line
+ None, # Process Security
+ None, # ThreadSecurity
+ 1, # Inherit Handles?
+ win32process.NORMAL_PRIORITY_CLASS,
+ None, # New environment
+ None, # Current directory
+ si) # startup info.
+ win32event.WaitForSingleObject( hProcess, win32event.INFINITE )
+ print("pysecdump: Quitting")
+
+def main(argv):
+ global threads, config
+ parser = ArgumentParser()
+ parser.add_argument("-l", "--listen-address", dest="TCP_IP",
+ help="Listen in this address, default: %s" % ("0.0.0.0"), required=False, default='0.0.0.0')
+ parser.add_argument("-p", "--port", dest="TCP_PORT",
+ help="Listen in this port, default: %s" % ("5899"), type=int, required=False, default='5899')
+ parser.add_argument("-P", "--password", help="Sets password", required=True, dest="PASSWORD")
+
+ args = parser.parse_args()
+
+ config["PASSWORD"] = args.PASSWORD
+ config["PORT"] = args.TCP_PORT
+
+ sockServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sockServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sockServer.bind((args.TCP_IP, args.TCP_PORT))
+
+ controlthread = ControlThread(threads)
+ controlthread.start()
+ threads.append(controlthread)
+
+ #_debug("Multithreaded Python server : Waiting for connections from TCP clients...")
+ _debug("Runing on:", sys.platform)
+ while True:
+ sockServer.listen(4)
+ (conn, (ip,port)) = sockServer.accept()
+ newthread = ClientThread(conn, ip, port, config)
+ newthread.setDaemon(True)
+ newthread.start()
+ threads.append(newthread)
+ #print(threads)
+
+
+
+if __name__ == "__main__":
+ threads = []
+ main(sys.argv)
diff --git a/lib/bgr233_palette.py b/lib/bgr233_palette.py
new file mode 100644
index 0000000..8fa16b2
--- /dev/null
+++ b/lib/bgr233_palette.py
@@ -0,0 +1,260 @@
+# BGR233 palette
+
+palette = [
+ 0, 0, 0,
+ 36, 0, 0,
+ 73, 0, 0,
+ 109, 0, 0,
+ 146, 0, 0,
+ 182, 0, 0,
+ 219, 0, 0,
+ 255, 0, 0,
+ 0, 36, 0,
+ 36, 36, 0,
+ 73, 36, 0,
+ 109, 36, 0,
+ 146, 36, 0,
+ 182, 36, 0,
+ 219, 36, 0,
+ 255, 36, 0,
+ 0, 73, 0,
+ 36, 73, 0,
+ 73, 73, 0,
+ 109, 73, 0,
+ 146, 73, 0,
+ 182, 73, 0,
+ 219, 73, 0,
+ 255, 73, 0,
+ 0, 109, 0,
+ 36, 109, 0,
+ 73, 109, 0,
+ 109, 109, 0,
+ 146, 109, 0,
+ 182, 109, 0,
+ 219, 109, 0,
+ 255, 109, 0,
+ 0, 146, 0,
+ 36, 146, 0,
+ 73, 146, 0,
+ 109, 146, 0,
+ 146, 146, 0,
+ 182, 146, 0,
+ 219, 146, 0,
+ 255, 146, 0,
+ 0, 182, 0,
+ 36, 182, 0,
+ 73, 182, 0,
+ 109, 182, 0,
+ 146, 182, 0,
+ 182, 182, 0,
+ 219, 182, 0,
+ 255, 182, 0,
+ 0, 219, 0,
+ 36, 219, 0,
+ 73, 219, 0,
+ 109, 219, 0,
+ 146, 219, 0,
+ 182, 219, 0,
+ 219, 219, 0,
+ 255, 219, 0,
+ 0, 255, 0,
+ 36, 255, 0,
+ 73, 255, 0,
+ 109, 255, 0,
+ 146, 255, 0,
+ 182, 255, 0,
+ 219, 255, 0,
+ 255, 255, 0,
+ 0, 0, 85,
+ 36, 0, 85,
+ 73, 0, 85,
+ 109, 0, 85,
+ 146, 0, 85,
+ 182, 0, 85,
+ 219, 0, 85,
+ 255, 0, 85,
+ 0, 36, 85,
+ 36, 36, 85,
+ 73, 36, 85,
+ 109, 36, 85,
+ 146, 36, 85,
+ 182, 36, 85,
+ 219, 36, 85,
+ 255, 36, 85,
+ 0, 73, 85,
+ 36, 73, 85,
+ 73, 73, 85,
+ 109, 73, 85,
+ 146, 73, 85,
+ 182, 73, 85,
+ 219, 73, 85,
+ 255, 73, 85,
+ 0, 109, 85,
+ 36, 109, 85,
+ 73, 109, 85,
+ 109, 109, 85,
+ 146, 109, 85,
+ 182, 109, 85,
+ 219, 109, 85,
+ 255, 109, 85,
+ 0, 146, 85,
+ 36, 146, 85,
+ 73, 146, 85,
+ 109, 146, 85,
+ 146, 146, 85,
+ 182, 146, 85,
+ 219, 146, 85,
+ 255, 146, 85,
+ 0, 182, 85,
+ 36, 182, 85,
+ 73, 182, 85,
+ 109, 182, 85,
+ 146, 182, 85,
+ 182, 182, 85,
+ 219, 182, 85,
+ 255, 182, 85,
+ 0, 219, 85,
+ 36, 219, 85,
+ 73, 219, 85,
+ 109, 219, 85,
+ 146, 219, 85,
+ 182, 219, 85,
+ 219, 219, 85,
+ 255, 219, 85,
+ 0, 255, 85,
+ 36, 255, 85,
+ 73, 255, 85,
+ 109, 255, 85,
+ 146, 255, 85,
+ 182, 255, 85,
+ 219, 255, 85,
+ 255, 255, 85,
+ 0, 0, 170,
+ 36, 0, 170,
+ 73, 0, 170,
+ 109, 0, 170,
+ 146, 0, 170,
+ 182, 0, 170,
+ 219, 0, 170,
+ 255, 0, 170,
+ 0, 36, 170,
+ 36, 36, 170,
+ 73, 36, 170,
+ 109, 36, 170,
+ 146, 36, 170,
+ 182, 36, 170,
+ 219, 36, 170,
+ 255, 36, 170,
+ 0, 73, 170,
+ 36, 73, 170,
+ 73, 73, 170,
+ 109, 73, 170,
+ 146, 73, 170,
+ 182, 73, 170,
+ 219, 73, 170,
+ 255, 73, 170,
+ 0, 109, 170,
+ 36, 109, 170,
+ 73, 109, 170,
+ 109, 109, 170,
+ 146, 109, 170,
+ 182, 109, 170,
+ 219, 109, 170,
+ 255, 109, 170,
+ 0, 146, 170,
+ 36, 146, 170,
+ 73, 146, 170,
+ 109, 146, 170,
+ 146, 146, 170,
+ 182, 146, 170,
+ 219, 146, 170,
+ 255, 146, 170,
+ 0, 182, 170,
+ 36, 182, 170,
+ 73, 182, 170,
+ 109, 182, 170,
+ 146, 182, 170,
+ 182, 182, 170,
+ 219, 182, 170,
+ 255, 182, 170,
+ 0, 219, 170,
+ 36, 219, 170,
+ 73, 219, 170,
+ 109, 219, 170,
+ 146, 219, 170,
+ 182, 219, 170,
+ 219, 219, 170,
+ 255, 219, 170,
+ 0, 255, 170,
+ 36, 255, 170,
+ 73, 255, 170,
+ 109, 255, 170,
+ 146, 255, 170,
+ 182, 255, 170,
+ 219, 255, 170,
+ 255, 255, 170,
+ 0, 0, 255,
+ 36, 0, 255,
+ 73, 0, 255,
+ 109, 0, 255,
+ 146, 0, 255,
+ 182, 0, 255,
+ 219, 0, 255,
+ 255, 0, 255,
+ 0, 36, 255,
+ 36, 36, 255,
+ 73, 36, 255,
+ 109, 36, 255,
+ 146, 36, 255,
+ 182, 36, 255,
+ 219, 36, 255,
+ 255, 36, 255,
+ 0, 73, 255,
+ 36, 73, 255,
+ 73, 73, 255,
+ 109, 73, 255,
+ 146, 73, 255,
+ 182, 73, 255,
+ 219, 73, 255,
+ 255, 73, 255,
+ 0, 109, 255,
+ 36, 109, 255,
+ 73, 109, 255,
+ 109, 109, 255,
+ 146, 109, 255,
+ 182, 109, 255,
+ 219, 109, 255,
+ 255, 109, 255,
+ 0, 146, 255,
+ 36, 146, 255,
+ 73, 146, 255,
+ 109, 146, 255,
+ 146, 146, 255,
+ 182, 146, 255,
+ 219, 146, 255,
+ 255, 146, 255,
+ 0, 182, 255,
+ 36, 182, 255,
+ 73, 182, 255,
+ 109, 182, 255,
+ 146, 182, 255,
+ 182, 182, 255,
+ 219, 182, 255,
+ 255, 182, 255,
+ 0, 219, 255,
+ 36, 219, 255,
+ 73, 219, 255,
+ 109, 219, 255,
+ 146, 219, 255,
+ 182, 219, 255,
+ 219, 219, 255,
+ 255, 219, 255,
+ 0, 255, 255,
+ 36, 255, 255,
+ 73, 255, 255,
+ 109, 255, 255,
+ 146, 255, 255,
+ 182, 255, 255,
+ 219, 255, 255,
+ 255, 255, 255
+]
diff --git a/lib/common.py b/lib/common.py
new file mode 100644
index 0000000..6af18f8
--- /dev/null
+++ b/lib/common.py
@@ -0,0 +1,68 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import sys
+from multiprocessing import Process, Value
+import subprocess
+import os
+import time
+import mmap
+import psutil
+
+def isWindows():
+ return sys.platform.startswith("win")
+
+def isOSX():
+ return sys.platform.startswith("darwin")
+
+def isLinux():
+ return sys.platform.startswith("linux")
+
+class proc:
+ def __init__(self):
+ self.pid = Value('i', 0)
+ self._process = None
+
+ def __del__(self):
+ pass
+
+ def _setpid(self, pid):
+ self.pid.value = pid
+
+ def getpid(self):
+ return self.pid.value
+
+ def _newproc(self, cmd):
+ pr = subprocess.Popen(cmd)
+ #print("Launched forkproc Process ID:", str(pr.pid))
+ self._setpid(pr.pid)
+
+ def run(self, *cmd):
+ self._process = Process(target=self._newproc, args=(cmd))
+ self._process.start()
+ self._process.join()
+ return self.pid.value
+
+ def terminate(self):
+ if psutil.pid_exists(self.pid.value):
+ p = psutil.Process(self.pid.value)
+ p.terminate()
+ self._process.terminate()
+
+ def waitproc(self):
+ while psutil.pid_exists(self.pid):
+ time.sleep(.25)
diff --git a/lib/const.py b/lib/const.py
new file mode 100644
index 0000000..235e4c5
--- /dev/null
+++ b/lib/const.py
@@ -0,0 +1,29 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import sys
+
+# constants workaround
+class _const:
+ class ConstError(TypeError): pass
+ def __setattr__(self, name, value):
+ if name in self.__dict__.keys():
+ raise (self.ConstError, "Can't rebind const(%s)" % name)
+ self.__dict__[name]=value
+
+sys.modules[__name__]=_const()
+
diff --git a/lib/encodings/__init__.py b/lib/encodings/__init__.py
index 4d4f35d..2ee2e55 100644
--- a/lib/encodings/__init__.py
+++ b/lib/encodings/__init__.py
@@ -16,5 +16,7 @@
# along with this program. If not, see .
# at least, raw encoding is needed by the rfb protocol
-from . import common as enc
+from . import common
from . import raw
+from . import zlib
+from . import cursor
diff --git a/lib/encodings/common.py b/lib/encodings/common.py
new file mode 100644
index 0000000..4b421b6
--- /dev/null
+++ b/lib/encodings/common.py
@@ -0,0 +1,29 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+encodings = {}
+
+class ENCODINGS:
+ raw = 0
+ zlib = 6
+ # supported pseudo-encodings
+ cursor = -239
+
+encodings_priority = [
+ ENCODINGS.zlib,
+ ENCODINGS.raw
+]
diff --git a/lib/encodings/cursor.py b/lib/encodings/cursor.py
new file mode 100644
index 0000000..6fbe3a6
--- /dev/null
+++ b/lib/encodings/cursor.py
@@ -0,0 +1,36 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+from . import common
+from struct import *
+from lib import log
+import zlib
+
+class Encoding:
+ name = 'Cursor'
+ id = -239
+ description = 'Cursor pseudo encoding'
+ enabled = True
+ pseudoEncoding = True
+
+ cursor_sent = False
+
+ def __init__(self):
+ log.debug("Initialized", __name__)
+
+common.encodings[common.ENCODINGS.cursor] = Encoding
+log.debug("Loaded encoding: %s (%s)" % (__name__, Encoding.id))
diff --git a/lib/encodings/raw.py b/lib/encodings/raw.py
new file mode 100644
index 0000000..fbfb36b
--- /dev/null
+++ b/lib/encodings/raw.py
@@ -0,0 +1,46 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+from . import common
+from struct import *
+from lib import log
+
+class Encoding:
+ _buff = None
+
+ name = 'raw'
+ id = 0
+ description = 'Raw VNC encoding'
+ enabled = True
+ firstUpdateSent = False
+
+ def __init__(self):
+ log.debug("Initialized", __name__)
+
+ def send_image(self, x, y, w, h, image):
+ self._buff = bytearray()
+ rectangles = 1
+ self._buff.extend(pack("!BxH", 0, rectangles)) # message type 0 == FramebufferUpdate
+ self._buff.extend(pack("!HHHH", x, y, w, h))
+ self._buff.extend(pack(">i", self.id))
+ self._buff.extend( image.tobytes() )
+
+ return self._buff
+
+common.encodings[common.ENCODINGS.raw] = Encoding
+
+log.debug("Loaded encoding: %s (%s)" % (__name__, Encoding.id))
diff --git a/lib/encodings/zlib.py b/lib/encodings/zlib.py
new file mode 100644
index 0000000..ec25348
--- /dev/null
+++ b/lib/encodings/zlib.py
@@ -0,0 +1,54 @@
+from . import common
+from struct import *
+from lib import log
+import zlib
+
+
+class Encoding:
+ name = 'zlib'
+ id = 6
+ description = 'zlib VNC encoding'
+ enabled = True
+ firstUpdateSent = False
+ _compressObj = None
+
+ def __init__(self):
+ log.debug("Initialized", __name__)
+ self._compressObj = zlib.compressobj(
+ zlib.Z_DEFAULT_COMPRESSION, # level: 0-9
+ zlib.DEFLATED, # method: must be DEFLATED
+ zlib.MAX_WBITS, # window size in bits:
+ # -15..-8: negate, suppress header
+ # 8..15: normal
+ # 16..30: subtract 16, gzip header
+ zlib.DEF_MEM_LEVEL, # mem level: 1..8/9
+ zlib.Z_DEFAULT_STRATEGY # strategy:
+ # 0 = Z_DEFAULT_STRATEGY
+ # 1 = Z_FILTERED
+ # 2 = Z_HUFFMAN_ONLY
+ # 3 = Z_RLE
+ # 4 = Z_FIXED
+ )
+
+ def send_image(self, x, y, w, h, image):
+ sendbuff = bytearray()
+
+ rectangles = 1
+ sendbuff.extend(pack("!BxH", 0, rectangles)) # message type 0 == FramebufferUpdate
+ sendbuff.extend(pack("!HHHH", x, y, w, h))
+ sendbuff.extend(pack(">i", self.id))
+
+ #log.debug("Compressing...")
+ zlibdata = self._compressObj.compress( image.tobytes() )
+ zlibdata += self._compressObj.flush(zlib.Z_FULL_FLUSH)
+ #log.debug("LEN", len(zlibdata))
+
+ l = pack("!I", len(zlibdata) )
+ sendbuff.extend( l ) # send length
+ sendbuff.extend( zlibdata ) # send compressed data
+
+ return sendbuff
+
+common.encodings[common.ENCODINGS.zlib] = Encoding
+
+log.debug("Loaded encoding: %s (%s)" % (__name__, Encoding.id))
diff --git a/lib/imagegrab.py b/lib/imagegrab.py
new file mode 100644
index 0000000..6ff18de
--- /dev/null
+++ b/lib/imagegrab.py
@@ -0,0 +1,41 @@
+import sys
+from PIL import Image
+from lib import log
+
+if sys.platform == "linux" or sys.platform == "linux2":
+ log.debug("ImageGrab: running on Linux")
+ from Xlib import display, X
+ # take screen images, that's not the best way, so here
+ # we use directly use xlib to take the screenshot.
+ class ImageGrab():
+ def grab():
+ dsp = display.Display()
+ root = dsp.screen().root
+ geom = root.get_geometry()
+ w = geom.width
+ h = geom.height
+ raw = root.get_image(0, 0, w ,h, X.ZPixmap, 0xffffffff)
+ image = Image.frombytes("RGB", (w, h), raw.data, "raw", "BGRX")
+ return image
+
+elif sys.platform == "darwin":
+ log.debug("ImageGrab: running on darwin")
+ import Quartz.CoreGraphics as CG
+ class ImageGrab():
+ def grab():
+ screenshot = CG.CGWindowListCreateImage(CG.CGRectInfinite, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault)
+ width = CG.CGImageGetWidth(screenshot)
+ height = CG.CGImageGetHeight(screenshot)
+ bytesperrow = CG.CGImageGetBytesPerRow(screenshot)
+
+ pixeldata = CG.CGDataProviderCopyData(CG.CGImageGetDataProvider(screenshot))
+
+ i = Image.frombytes("RGBA", (width, height), pixeldata)
+ (b, g, r, x) = i.split()
+ i = Image.merge("RGBX", (r, g, b, x))
+
+ return i
+
+else:
+ log.debug("ImageGrab: running on Unknown!")
+ from PIL import ImageGrab
diff --git a/lib/kbdctrl.py b/lib/kbdctrl.py
new file mode 100644
index 0000000..5a9456a
--- /dev/null
+++ b/lib/kbdctrl.py
@@ -0,0 +1,47 @@
+import sys
+from struct import pack, unpack
+from pynput import keyboard
+from lib import log
+from lib.kbdmap import *
+
+class KeyboardController:
+ kbdmap = kbdmap
+ kbdkey = ''
+ downflag = None
+ key = None
+ controller = None
+ kbd = None
+
+ def __init__(self):
+ self.kbd = keyboard
+ self.controller = self.kbd.Controller()
+
+ def process_event(self, data):
+ # B = U8, L = U32
+ (self.downflag, self.key) = unpack("!BxxL", data)
+ log.debug("KeyEvent", self.downflag, hex(self.key))
+
+ # special key
+ if self.key in self.kbdmap:
+ self.kbdkey = self.kbdmap[self.key]
+ log.debug("SPECIAL KEY", self.kbdkey)
+ else: # normal key
+ try:
+ self.kbdkey = self.kbd.KeyCode.from_char(chr(self.key))
+ except:
+ self.kbdkey = None
+
+ # debug keypress to stdout
+ try:
+ log.debug("KEY:", self.kbdkey)
+ except:
+ log.debug("KEY: (unprintable)")
+
+ # send the actual keyboard event
+ try:
+ if self.downflag:
+ self.controller.press(self.kbdkey)
+ else:
+ self.controller.release(self.kbdkey)
+ except:
+ log.debug("Error sending key")
diff --git a/lib/kbdmap.py b/lib/kbdmap.py
new file mode 100644
index 0000000..e49e30e
--- /dev/null
+++ b/lib/kbdmap.py
@@ -0,0 +1,79 @@
+import sys
+from pynput import keyboard
+
+__all__ = ['kbdmap']
+
+kbdmap = {
+ 0xff08: keyboard.Key.backspace,
+ 0xff09: keyboard.Key.tab,
+ 0xff0d: keyboard.Key.enter,
+ 0xff1b: keyboard.Key.esc,
+ 0xff63: keyboard.Key.insert if hasattr(keyboard.Key, "insert") else None,
+ 0xffff: keyboard.Key.delete,
+ 0xff50: keyboard.Key.home,
+ 0xff57: keyboard.Key.end,
+ 0xff55: keyboard.Key.page_up,
+ 0xff56: keyboard.Key.page_down,
+ 0xff51: keyboard.Key.left,
+ 0xff52: keyboard.Key.up,
+ 0xff53: keyboard.Key.right,
+ 0xff54: keyboard.Key.down,
+ 0xffbe: keyboard.Key.f1,
+ 0xffbf: keyboard.Key.f2,
+ 0xffc0: keyboard.Key.f3,
+ 0xffc1: keyboard.Key.f4,
+ 0xffc2: keyboard.Key.f5,
+ 0xffc3: keyboard.Key.f6,
+ 0xffc4: keyboard.Key.f7,
+ 0xffc5: keyboard.Key.f8,
+ 0xffc6: keyboard.Key.f9,
+ 0xffc7: keyboard.Key.f10,
+ 0xffc8: keyboard.Key.f11,
+ 0xffc9: keyboard.Key.f12,
+ 0xffca: keyboard.Key.f13,
+ 0xffcb: keyboard.Key.f14,
+ 0xffcc: keyboard.Key.f15,
+ 0xffcd: keyboard.Key.f16,
+ 0xffce: keyboard.Key.f17,
+ 0xffcf: keyboard.Key.f18,
+ 0xffd0: keyboard.Key.f19,
+ 0xffd1: keyboard.Key.f20,
+ 0xffe1: keyboard.Key.shift_l,
+ 0xffe2: keyboard.Key.shift_r,
+ 0xffe3: keyboard.Key.ctrl_l,
+ 0xffe4: keyboard.Key.ctrl_r,
+ 0xffe7: None, # "KEY_MetaLeft"
+ 0xffe8: None, # "KEY_MetaRight"
+ #0xffe9: keyboard.Key.cmd_l,
+ 0xffe9: keyboard.Key.alt,
+ 0xffea: keyboard.Key.alt_gr, # "KEY_AltRight"
+ 0xff14: keyboard.Key.scroll_lock if hasattr(keyboard.Key, "scroll_lock") else None,
+ 0xff15: keyboard.Key.print_screen if hasattr(keyboard.Key, "print_screen") else None, # "KEY_Sys_Req"
+ 0xff7f: keyboard.Key.num_lock if hasattr(keyboard.Key, "num_lock") else None,
+ 0xffe5: keyboard.Key.caps_lock,
+ 0xff13: keyboard.Key.pause if hasattr(keyboard.Key, "pause") else None,
+ 0xffeb: keyboard.Key.cmd_r, # "KEY_Super_L"
+ 0xffec: keyboard.Key.cmd_r, # "KEY_Super_R"
+ 0xffed: None, # "KEY_Hyper_L"
+ 0xffee: None, # "KEY_Hyper_R"
+ 0xffb0: None, # "KEY_KP_0"
+ 0xffb1: None, # "KEY_KP_1"
+ 0xffb2: None, # "KEY_KP_2"
+ 0xffb3: None, # "KEY_KP_3"
+ 0xffb4: None, # "KEY_KP_4"
+ 0xffb5: None, # "KEY_KP_5"
+ 0xffb6: None, # "KEY_KP_6"
+ 0xffb7: None, # "KEY_KP_7"
+ 0xffb8: None, # "KEY_KP_8"
+ 0xffb9: None, # "KEY_KP_9"
+ 0xff8d: None, # "KEY_KP_Enter"
+ 0x002f: "/", # KEY_ForwardSlash
+ 0x005c: "\\", # KEY_BackSlash
+ 0x0020: keyboard.Key.space, # "KEY_SpaceBar"
+ 0xff7e: keyboard.Key.alt_gr, # altgr, at least on a mac (?)
+ #0xfe03: keyboard.Key.alt_l,
+ 0xfe03: keyboard.Key.cmd_l,
+}
+
+if sys.platform == "darwin":
+ kbdmap[0xffe2] = keyboard.Key.shift
diff --git a/lib/log.py b/lib/log.py
new file mode 100644
index 0000000..5c43b42
--- /dev/null
+++ b/lib/log.py
@@ -0,0 +1,28 @@
+# coding=utf-8
+# pyvncs
+# Copyright (C) 2017-2018 Matias Fernandez
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+import logging
+logging.basicConfig(level=logging.DEBUG, format='[%(threadName)s] %(message)s')
+logger = logging.getLogger('pyvncs')
+
+
+def debug(*args):
+ str = ""
+ for s in args:
+ str = "%s %s" % (str, s)
+ str = str.strip()
+ logger.debug(str)
diff --git a/lib/mousectrl.py b/lib/mousectrl.py
new file mode 100644
index 0000000..3fc9179
--- /dev/null
+++ b/lib/mousectrl.py
@@ -0,0 +1,60 @@
+from struct import pack, unpack
+from pynput import mouse
+from lib import log
+
+class MouseController():
+ def __init__(self):
+ self.buttonmask = 0
+ self.buttons = [0, 0, 0, 0, 0, 0, 0, 0]
+ self.left_pressed = 0
+ self.right_pressed = 0
+ self.middle_pressed = 0
+
+ def process_event(self, data):
+ (self.buttonmask, x, y) = unpack("!BHH", data)
+ self.buttons[0] = self.buttonmask & int("0000001", 2) # left button
+ self.buttons[1] = self.buttonmask & int("0000010", 2) # middle button
+ self.buttons[2] = self.buttonmask & int("0000100", 2) # right button
+ self.buttons[3] = self.buttonmask & int("0001000", 2) # scroll up
+ self.buttons[4] = self.buttonmask & int("0010000", 2) # scroll down
+
+ # set mouse position
+ mouse.Controller().position = (x, y)
+
+ # process mouse button events
+ if self.buttons[0] and not self.left_pressed:
+ log.debug("LEFT PRESSED")
+ mouse.Controller().press(mouse.Button.left)
+ self.left_pressed = 1
+ elif not self.buttons[0] and self.left_pressed:
+ log.debug("LEFT RELEASED")
+ mouse.Controller().release(mouse.Button.left)
+ self.left_pressed = 0
+
+ if self.buttons[1] and not self.middle_pressed:
+ log.debug("MIDDLE PRESSED")
+ mouse.Controller().press(mouse.Button.middle)
+ self.middle_pressed = 1
+ elif not self.buttons[1] and self.middle_pressed:
+ log.debug("MIDDLE RELEASED")
+ mouse.Controller().release(mouse.Button.middle)
+ self.middle_pressed = 0
+
+ if self.buttons[2] and not self.right_pressed:
+ log.debug("RIGHT PRESSED")
+ mouse.Controller().press(mouse.Button.right)
+ self.right_pressed = 1
+ elif not self.buttons[2] and self.right_pressed:
+ log.debug("RIGHT RELEASED")
+ mouse.Controller().release(mouse.Button.right)
+ self.right_pressed = 0
+
+ if self.buttons[3]:
+ log.debug("SCROLLUP PRESSED")
+ mouse.Controller().scroll(0, 2)
+
+ if self.buttons[4]:
+ log.debug("SCROLLDOWN PRESSED")
+ mouse.Controller().scroll(0, -2)
+
+ #log.debug("PointerEvent", buttonmask, x, y)
diff --git a/lib/oshelpers/windows.py b/lib/oshelpers/windows.py
new file mode 100644
index 0000000..765107c
--- /dev/null
+++ b/lib/oshelpers/windows.py
@@ -0,0 +1,31 @@
+import ctypes
+import sys
+from elevate import elevate
+
+def is_admin():
+ try:
+ return ctypes.windll.shell32.IsUserAnAdmin()
+ except:
+ return False
+
+def run_as_admin_old(argv=None):
+ shell32 = ctypes.windll.shell32
+ if argv is None and shell32.IsUserAnAdmin():
+ return True
+
+ if argv is None:
+ argv = sys.argv
+ if hasattr(sys, '_MEIPASS'):
+ # Support pyinstaller wrapped program.
+ arguments = argv[1:]
+ else:
+ arguments = argv
+ argument_line = u' '.join(arguments)
+ executable = sys.executable
+ ret = shell32.ShellExecuteW(None, u"runas", executable, argument_line, None, 1)
+ if int(ret) <= 32:
+ return False
+ return None
+
+def run_as_admin():
+ elevate()
diff --git a/pyvncs/__pycache__/server.cpython-36.pyc b/pyvncs/__pycache__/server.cpython-36.pyc
deleted file mode 100644
index 96d4937..0000000
Binary files a/pyvncs/__pycache__/server.cpython-36.pyc and /dev/null differ
diff --git a/pyvncs/server.py b/pyvncs/server.py
index da7d137..7de6298 100644
--- a/pyvncs/server.py
+++ b/pyvncs/server.py
@@ -19,16 +19,24 @@ from struct import *
from pyDes import *
from time import sleep
from pynput import mouse, keyboard
+from PIL import Image, ImageChops, ImageDraw, ImagePalette
import socket
import select
import os
import sys
import random
-import zlib
import numpy as np
-from lib.encodings import *
+from lib import mousectrl
+from lib import kbdctrl
+from lib.imagegrab import ImageGrab
+from lib import log
+from lib import bgr233_palette
+
+# encodings support
+import lib.encodings as encs
+from lib.encodings.common import ENCODINGS
def hexdump(data):
str = ""
@@ -38,7 +46,7 @@ def hexdump(data):
return str
def quantizetopalette(silf, palette, dither=False):
- """Convert an RGB or L mode image to use a given P image's palette."""
+ """Converts an RGB or L mode image to use a given P image's palette."""
silf.load()
@@ -59,50 +67,13 @@ def quantizetopalette(silf, palette, dither=False):
except AttributeError:
return silf._makeself(im)
-
-from PIL import Image, ImageChops, ImageDraw, ImagePalette
-if sys.platform == "linux" or sys.platform == "linux2":
- from Xlib import display, X
- # take screen images, that's not the best way, so here
- # we use directly use xlib to take the screenshot.
- class ImageGrab():
- def grab():
- dsp = display.Display()
- root = dsp.screen().root
- geom = root.get_geometry()
- w = geom.width
- h = geom.height
- raw = root.get_image(0, 0, w ,h, X.ZPixmap, 0xffffffff)
- image = Image.frombytes("RGB", (w, h), raw.data, "raw", "BGRX")
- return image
-
-elif sys.platform == "darwin":
- import Quartz.CoreGraphics as CG
- class ImageGrab():
- def grab():
- screenshot = CG.CGWindowListCreateImage(CG.CGRectInfinite, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault)
- width = CG.CGImageGetWidth(screenshot)
- height = CG.CGImageGetHeight(screenshot)
- bytesperrow = CG.CGImageGetBytesPerRow(screenshot)
-
- pixeldata = CG.CGDataProviderCopyData(CG.CGImageGetDataProvider(screenshot))
-
- i = Image.frombytes("RGBA", (width, height), pixeldata)
- (b, g, r, x) = i.split()
- i = Image.merge("RGBX", (r, g, b, x))
-
- return i
-
-else:
- from PIL import ImageGrab
-
-
-
class VncServer():
class CONFIG:
_8bitdither = False
+ encoding_object = None
+
def __init__(self, socket, password):
self.RFB_VERSION = '003.008'
self.RFB_SECTYPES = [
@@ -114,75 +85,10 @@ class VncServer():
self.framebuffer = None
self.password = password
self.sectypes = self.RFB_SECTYPES
- self.remotecursor = False
-
- self.BGR233 = [0, 0, 0, 0, 0, 85, 0, 0, 170, 0, 0, 255, 36, 0, 0,
- 36, 0, 85, 36, 0, 170, 36, 0, 255, 73, 0, 0, 73, 0,
- 85, 73, 0, 170, 73, 0, 255, 109, 0, 0, 109, 0, 85,
- 109, 0, 170, 109, 0, 255, 146, 0, 0, 146, 0, 85, 146,
- 0, 170, 146, 0, 255, 182, 0, 0, 182, 0, 85, 182, 0,
- 170, 182, 0, 255, 219, 0, 0, 219, 0, 85, 219, 0, 170,
- 219, 0, 255, 255, 0, 0, 255, 0, 85, 255, 0, 170, 255,
- 0, 255, 0, 36, 0, 0, 36, 85, 0, 36, 170, 0, 36, 255,
- 36, 36, 0, 36, 36, 85, 36, 36, 170, 36, 36, 255, 73,
- 36, 0, 73, 36, 85, 73, 36, 170, 73, 36, 255, 109, 36,
- 0, 109, 36, 85, 109, 36, 170, 109, 36, 255, 146, 36,
- 0, 146, 36, 85, 146, 36, 170, 146, 36, 255, 182, 36,
- 0, 182, 36, 85, 182, 36, 170, 182, 36, 255, 219, 36,
- 0, 219, 36, 85, 219, 36, 170, 219, 36, 255, 255, 36,
- 0, 255, 36, 85, 255, 36, 170, 255, 36, 255, 0, 73, 0,
- 0, 73, 85, 0, 73, 170, 0, 73, 255, 36, 73, 0, 36, 73,
- 85, 36, 73, 170, 36, 73, 255, 73, 73, 0, 73, 73, 85,
- 73, 73, 170, 73, 73, 255, 109, 73, 0, 109, 73, 85,
- 109, 73, 170, 109, 73, 255, 146, 73, 0, 146, 73, 85,
- 146, 73, 170, 146, 73, 255, 182, 73, 0, 182, 73, 85,
- 182, 73, 170, 182, 73, 255, 219, 73, 0, 219, 73, 85,
- 219, 73, 170, 219, 73, 255, 255, 73, 0, 255, 73, 85,
- 255, 73, 170, 255, 73, 255, 0, 109, 0, 0, 109, 85, 0,
- 109, 170, 0, 109, 255, 36, 109, 0, 36, 109, 85, 36,
- 109, 170, 36, 109, 255, 73, 109, 0, 73, 109, 85, 73,
- 109, 170, 73, 109, 255, 109, 109, 0, 109, 109, 85, 109,
- 109, 170, 109, 109, 255, 146, 109, 0, 146, 109, 85,
- 146, 109, 170, 146, 109, 255, 182, 109, 0, 182, 109,
- 85, 182, 109, 170, 182, 109, 255, 219, 109, 0, 219,
- 109, 85, 219, 109, 170, 219, 109, 255, 255, 109, 0,
- 255, 109, 85, 255, 109, 170, 255, 109, 255, 0, 146, 0,
- 0, 146, 85, 0, 146, 170, 0, 146, 255, 36, 146, 0, 36,
- 146, 85, 36, 146, 170, 36, 146, 255, 73, 146, 0, 73,
- 146, 85, 73, 146, 170, 73, 146, 255, 109, 146, 0, 109,
- 146, 85, 109, 146, 170, 109, 146, 255, 146, 146, 0, 146,
- 146, 85, 146, 146, 170, 146, 146, 255, 182, 146, 0, 182,
- 146, 85, 182, 146, 170, 182, 146, 255, 219, 146, 0, 219,
- 146, 85, 219, 146, 170, 219, 146, 255, 255, 146, 0, 255,
- 146, 85, 255, 146, 170, 255, 146, 255, 0, 182, 0, 0,
- 182, 85, 0, 182, 170, 0, 182, 255, 36, 182, 0, 36, 182,
- 85, 36, 182, 170, 36, 182, 255, 73, 182, 0, 73, 182,
- 85, 73, 182, 170, 73, 182, 255, 109, 182, 0, 109, 182,
- 85, 109, 182, 170, 109, 182, 255, 146, 182, 0, 146,
- 182, 85, 146, 182, 170, 146, 182, 255, 182, 182, 0, 182,
- 182, 85, 182, 182, 170, 182, 182, 255, 219, 182, 0, 219,
- 182, 85, 219, 182, 170, 219, 182, 255, 255, 182, 0, 255,
- 182, 85, 255, 182, 170, 255, 182, 255, 0, 219, 0, 20,
- 219, 85, 0, 219, 170, 0, 219, 255, 36, 219, 0, 36, 219,
- 85, 36, 219, 170, 36, 219, 255, 73, 219, 0, 73, 219,
- 85, 73, 219, 170, 73, 219, 255, 109, 219, 0, 109, 219,
- 85, 109, 219, 170, 109, 219, 255, 146, 219, 0, 146, 219,
- 85, 146, 219, 170, 146, 219, 255, 182, 219, 0, 182, 219,
- 85, 182, 219, 170, 182, 219, 255, 219, 219, 0, 219, 219,
- 85, 219, 219, 170, 219, 219, 255, 255, 219, 0, 255, 219,
- 85, 255, 219, 170, 255, 219, 255, 0, 255, 0, 0, 255, 85,
- 0, 255, 170, 0, 255, 255, 36, 255, 0, 36, 255, 85, 36,
- 255, 170, 36, 255, 255, 73, 255, 0, 73, 255, 85, 73,
- 255, 170, 73, 255, 255, 109, 255, 0, 109, 255, 85, 109,
- 255, 170, 109, 255, 255, 146, 255, 0, 146, 255, 85,
- 146, 255, 170, 146, 255, 255, 182, 255, 0, 182, 255, 85,
- 182, 255, 170, 182, 255, 255, 219, 255, 0, 219, 255, 85,
- 219, 255, 170, 219, 255, 255, 255, 255, 0, 255, 255, 85,
- 255, 255, 170, 255, 255, 255]
-
-
+ self.cursor_support = False
+
def __del__(self):
- print("VncServer died")
+ log.debug("VncServer died")
def encrypt(self, key, data):
k = des(key, ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
@@ -221,7 +127,7 @@ class VncServer():
data = sock.recv(1024)
except socket.timeout:
data = None
- print("getbuff() timeout")
+ log.debug("getbuff() timeout")
return data
@@ -232,15 +138,15 @@ class VncServer():
# RFB version handshake
data = self.getbuff(30)
- print("init received: '%s'" % data)
+ log.debug("init received: '%s'" % data)
server_version = float(self.RFB_VERSION)
try:
client_version = float(data[4:11])
except:
- print("Error parsing client version")
+ log.debug("Error parsing client version")
return False
- print("client, server:", client_version, server_version)
+ log.debug("client, server:", client_version, server_version)
# security types handshake
sendbuff = pack("B", len(self.sectypes)) # number of security types
@@ -254,13 +160,13 @@ class VncServer():
sectype = None
if sectype not in self.sectypes:
- print("Incompatible security type: %s" % data)
+ log.debug("Incompatible security type: %s" % data)
sock.send(pack("B", 1)) # failed handshake
self.sendmessage("Incompatible security type")
sock.close()
return False
- print("sec type data: %s" % data)
+ log.debug("sec type data: %s" % data)
# VNC Auth
if sectype == 2:
@@ -278,9 +184,9 @@ class VncServer():
if data == crypted:
# Handshake successful
sock.send(pack("I", 0))
- print("Auth OK")
+ log.debug("Auth OK")
else:
- print("Invalid auth")
+ log.debug("Invalid auth")
return False
#unsupported VNC auth type
@@ -289,7 +195,7 @@ class VncServer():
# get ClientInit
data = self.getbuff(30)
- print("Clientinit (shared flag)", repr(data))
+ log.debug("Clientinit (shared flag)", repr(data))
self.ServerInit()
@@ -300,9 +206,9 @@ class VncServer():
sock = self.socket
screen = ImageGrab.grab()
- print("screen", repr(screen))
+ log.debug("screen", repr(screen))
size = screen.size
- print("size", repr(size))
+ log.debug("size", repr(size))
del screen
width = size[0]
@@ -339,105 +245,29 @@ class VncServer():
sendbuff += pack("!I", desktop_name_len)
sendbuff += desktop_name.encode()
- print("width", repr(width))
- print("height", repr(height))
+ log.debug("width", repr(width))
+ log.debug("height", repr(height))
sock.send(sendbuff)
def protocol(self):
self.socket.settimeout(None) # set nonblocking socket
-
screen = ImageGrab.grab()
size = screen.size
width = size[0]
height = size[1]
del screen
+
+ mousecontroller = mousectrl.MouseController()
+ kbdcontroller = kbdctrl.KeyboardController()
self.primaryOrder = "rgb"
- self.encoding = enc.ENCODINGS.raw
- buttonmask = 0
- buttons = [0, 0, 0, 0, 0, 0, 0, 0]
- left_pressed = 0
- right_pressed = 0
- middle_pressed = 0
+ self.encoding = ENCODINGS.raw
+ self.encoding_object = encs.common.encodings[self.encoding]()
-
- kbdmap = {
- 0xff08: keyboard.Key.backspace,
- 0xff09: keyboard.Key.tab,
- 0xff0d: keyboard.Key.enter,
- 0xff1b: keyboard.Key.esc,
- 0xff63: keyboard.Key.insert if hasattr(keyboard.Key, "insert") else None,
- 0xffff: keyboard.Key.delete,
- 0xff50: keyboard.Key.home,
- 0xff57: keyboard.Key.end,
- 0xff55: keyboard.Key.page_up,
- 0xff56: keyboard.Key.page_down,
- 0xff51: keyboard.Key.left,
- 0xff52: keyboard.Key.up,
- 0xff53: keyboard.Key.right,
- 0xff54: keyboard.Key.down,
- 0xffbe: keyboard.Key.f1,
- 0xffbf: keyboard.Key.f2,
- 0xffc0: keyboard.Key.f3,
- 0xffc1: keyboard.Key.f4,
- 0xffc2: keyboard.Key.f5,
- 0xffc3: keyboard.Key.f6,
- 0xffc4: keyboard.Key.f7,
- 0xffc5: keyboard.Key.f8,
- 0xffc6: keyboard.Key.f9,
- 0xffc7: keyboard.Key.f10,
- 0xffc8: keyboard.Key.f11,
- 0xffc9: keyboard.Key.f12,
- 0xffca: keyboard.Key.f13,
- 0xffcb: keyboard.Key.f14,
- 0xffcc: keyboard.Key.f15,
- 0xffcd: keyboard.Key.f16,
- 0xffce: keyboard.Key.f17,
- 0xffcf: keyboard.Key.f18,
- 0xffd0: keyboard.Key.f19,
- 0xffd1: keyboard.Key.f20,
- 0xffe1: keyboard.Key.shift_l,
- 0xffe2: keyboard.Key.shift_r,
- 0xffe3: keyboard.Key.ctrl_l,
- 0xffe4: keyboard.Key.ctrl_r,
- 0xffe7: None, # "KEY_MetaLeft"
- 0xffe8: None, # "KEY_MetaRight"
- 0xffe9: keyboard.Key.cmd_l,
- 0xffea: keyboard.Key.alt_gr, # "KEY_AltRight"
- 0xff14: keyboard.Key.scroll_lock if hasattr(keyboard.Key, "scroll_lock") else None,
- 0xff15: keyboard.Key.print_screen if hasattr(keyboard.Key, "print_screen") else None, # "KEY_Sys_Req"
- 0xff7f: keyboard.Key.num_lock if hasattr(keyboard.Key, "num_lock") else None,
- 0xffe5: keyboard.Key.caps_lock,
- 0xff13: keyboard.Key.pause if hasattr(keyboard.Key, "pause") else None,
- 0xffeb: keyboard.Key.cmd_r, # "KEY_Super_L"
- 0xffec: keyboard.Key.cmd_r, # "KEY_Super_R"
- 0xffed: None, # "KEY_Hyper_L"
- 0xffee: None, # "KEY_Hyper_R"
- 0xffb0: None, # "KEY_KP_0"
- 0xffb1: None, # "KEY_KP_1"
- 0xffb2: None, # "KEY_KP_2"
- 0xffb3: None, # "KEY_KP_3"
- 0xffb4: None, # "KEY_KP_4"
- 0xffb5: None, # "KEY_KP_5"
- 0xffb6: None, # "KEY_KP_6"
- 0xffb7: None, # "KEY_KP_7"
- 0xffb8: None, # "KEY_KP_8"
- 0xffb9: None, # "KEY_KP_9"
- 0xff8d: None, # "KEY_KP_Enter"
- 0x002f: "/", # KEY_ForwardSlash
- 0x005c: "\\", # KEY_BackSlash
- 0x0020: keyboard.Key.space, # "KEY_SpaceBar"
- 0xff7e: keyboard.Key.alt_gr, # altgr, at least on a mac (?)
- 0xfe03: keyboard.Key.alt_l,
- }
-
- if sys.platform == "darwin":
- kbdmap[0xffe2] = keyboard.Key.shift
-
while True:
- #print(".", end='', flush=True)
+ #log.debug(".", end='', flush=True)
r,_,_ = select.select([self.socket],[],[],0)
if r == []:
#no data
@@ -448,10 +278,10 @@ class VncServer():
try:
data = sock.recv(1) # read first byte
except socket.timeout:
- #print("timeout")
+ #log.debug("timeout")
continue
except Exception as e:
- print("exception '%s'" % e)
+ log.debug("exception '%s'" % e)
sock.close()
break
@@ -462,141 +292,75 @@ class VncServer():
if data[0] == 0: # client SetPixelFormat
data2 = sock.recv(19, socket.MSG_WAITALL)
- print("Client Message Type: Set Pixel Format (0)")
+ log.debug("Client Message Type: Set Pixel Format (0)")
(self.bpp, self.depth, self.bigendian, self.truecolor, self.red_maximum,
self.green_maximum, self.blue_maximum,
self.red_shift, self.green_shift, self.blue_shift
) = unpack("!xxxBBBBHHHBBBxxx", data2)
- print("IMG bpp, depth, endian, truecolor", self.bpp, self.depth, self.bigendian, self.truecolor)
- print("SHIFTS", self.red_shift, self.green_shift, self.blue_shift)
- print("MAXS", self.red_maximum, self.green_maximum, self.blue_maximum)
+ log.debug("IMG bpp, depth, endian, truecolor", self.bpp, self.depth, self.bigendian, self.truecolor)
+ log.debug("SHIFTS", self.red_shift, self.green_shift, self.blue_shift)
+ log.debug("MAXS", self.red_maximum, self.green_maximum, self.blue_maximum)
if self.red_shift > self.blue_shift:
self.primaryOrder = "rgb"
else:
self.primaryOrder = "bgr"
- print("Using order: ", self.primaryOrder)
+ log.debug("Using order:", self.primaryOrder)
continue
if data[0] == 2: # SetEncoding
data2 = sock.recv(3)
- print("Client Message Type: SetEncoding (2)")
+ log.debug("Client Message Type: SetEncoding (2)")
(nencodings,) = unpack("!xH", data2)
- print("SetEncoding: total encodings", repr(nencodings))
+ log.debug("SetEncoding: total encodings", repr(nencodings))
data2 = sock.recv(4 * nencodings, socket.MSG_WAITALL)
- #print("len", len(data2))
+ #log.debug("len", len(data2))
self.client_encodings = unpack("!%si" % nencodings, data2)
- #print("data", repr(self.client_encodings), len(self.client_encodings))
+ log.debug("client_encodings", repr(self.client_encodings), len(self.client_encodings))
- if hasattr(enc.ENCODINGS, "cursor") and enc.ENCODINGS.cursor in self.client_encodings:
- print("Remote cursor encoding present")
- self.remotecursor = True
- self.cursorchanged = True
-
- if hasattr(enc.ENCODINGS, "zlib") and enc.ENCODINGS.zlib in self.client_encodings:
- print("Using zlib encoding")
- self.encoding = enc.ENCODINGS.zlib
+ # cursor support?
+ if ENCODINGS.cursor in self.client_encodings:
+ log.debug("client cursor support")
+ self.cursor_support = True
+
+ # which pixel encoding to use?
+ log.debug("encs.common.encodings_priority", encs.common.encodings_priority)
+ for e in encs.common.encodings_priority:
+ log.debug("E", e)
+ if e in self.client_encodings:
+ if self.encoding == e:
+ # don't initialize same encoding again
+ break
+ self.encoding = e
+ log.debug("Using %s encoding" % self.encoding)
+ self.encoding_object = encs.common.encodings[self.encoding]()
+ break
continue
if data[0] == 3: # FBUpdateRequest
data2 = sock.recv(9, socket.MSG_WAITALL)
- #print("Client Message Type: FBUpdateRequest (3)")
+ #log.debug("Client Message Type: FBUpdateRequest (3)")
#print(len(data2))
(incremental, x, y, w, h) = unpack("!BHHHH", data2)
- #print("RFBU:", incremental, x, y, w, h)
+ #log.debug("RFBU:", incremental, x, y, w, h)
self.SendRectangles(sock, x, y, w, h, incremental)
- if self.remotecursor and self.cursorchanged:
- # FIXME: send cursor to remote client
- self.cursorchanged = False
continue
- if data[0] == 4:
- kbdkey = ''
- data2 = sock.recv(7)
- # B = U8, L = U32
- (downflag, key) = unpack("!BxxL", data2)
- print("KeyEvent", downflag, hex(key))
-
- # special key
- if key in kbdmap:
- kbdkey = kbdmap[key]
- else: # normal key
- try:
- kbdkey = keyboard.KeyCode.from_char(chr(key))
- except:
- kbdkey = None
-
- try:
- print("KEY:", kbdkey)
- except:
- print("KEY: (unprintable)")
-
- try:
- if downflag:
- keyboard.Controller().press(kbdkey)
- else:
- keyboard.Controller().release(kbdkey)
- except:
- print("Error sending key")
-
+ if data[0] == 4: # keyboard event
+ kbdcontroller.process_event(sock.recv(7))
continue
if data[0] == 5: # PointerEvent
- data2 = sock.recv(5, socket.MSG_WAITALL)
- (buttonmask, x, y) = unpack("!BHH", data2)
- buttons[0] = buttonmask & int("0000001", 2) # left button
- buttons[1] = buttonmask & int("0000010", 2) # middle button
- buttons[2] = buttonmask & int("0000100", 2) # right button
- buttons[3] = buttonmask & int("0001000", 2) # scroll up
- buttons[4] = buttonmask & int("0010000", 2) # scroll down
-
- mouse.Controller().position = (x, y)
-
- if buttons[0] and not left_pressed:
- print("LEFT PRESSED")
- mouse.Controller().press(mouse.Button.left)
- left_pressed = 1
- elif not buttons[0] and left_pressed:
- print("LEFT RELEASED")
- mouse.Controller().release(mouse.Button.left)
- left_pressed = 0
-
- if buttons[1] and not middle_pressed:
- print("MIDDLE PRESSED")
- mouse.Controller().press(mouse.Button.middle)
- middle_pressed = 1
- elif not buttons[1] and middle_pressed:
- print("MIDDLE RELEASED")
- mouse.Controller().release(mouse.Button.middle)
- middle_pressed = 0
-
- if buttons[2] and not right_pressed:
- print("RIGHT PRESSED")
- mouse.Controller().press(mouse.Button.right)
- right_pressed = 1
- elif not buttons[2] and right_pressed:
- print("RIGHT RELEASED")
- mouse.Controller().release(mouse.Button.right)
- right_pressed = 0
-
- if buttons[3]:
- print("SCROLLUP PRESSED")
- mouse.Controller().scroll(0, 2)
-
- if buttons[4]:
- print("SCROLLDOWN PRESSED")
- mouse.Controller().scroll(0, -2)
-
- #print("PointerEvent", buttonmask, x, y)
+ mousecontroller.process_event(sock.recv(5, socket.MSG_WAITALL))
continue
else:
data2 = sock.recv(4096)
- print("RAW Server received data:", repr(data[0]) , data+data2)
+ log.debug("RAW Server received data:", repr(data[0]) , data+data2)
def GetRectangle(self, x, y, w, h):
@@ -621,7 +385,7 @@ class VncServer():
def SendRectangles(self, sock, x, y, w, h, incremental=0):
# send FramebufferUpdate to client
- #print("start SendRectangles")
+ #log.debug("start SendRectangles")
rectangle = self.GetRectangle(x, y, w, h)
if not rectangle:
rectangle = Image.new("RGB", [w, h], (0,0,0))
@@ -629,19 +393,20 @@ class VncServer():
lastshot = rectangle
sendbuff = bytearray()
- firstUpdateSent = False
+ self.encoding_object.firstUpdateSent = False
# try to send only the actual changes
if self.framebuffer != None and incremental == 1:
diff = ImageChops.difference(rectangle, self.framebuffer)
if diff.getbbox() is None:
+ # no changes...
rectangles = 0
sendbuff.extend(pack("!BxH", 0, rectangles))
try:
sock.sendall(sendbuff)
except:
return False
- sleep(0.3)
+ sleep(0.1)
return
if diff.getbbox() is not None:
@@ -650,126 +415,77 @@ class VncServer():
(x, y, _, _) = diff.getbbox()
w = rectangle.width
h = rectangle.height
- #print("XYWH:", x,y,w,h, "diff", repr(diff.getbbox()))
+ #log.debug("XYWH:", x,y,w,h, "diff", repr(diff.getbbox()))
stimeout = sock.gettimeout()
sock.settimeout(None)
- if self.bpp == 32 or self.bpp == 8:
- if rectangle.mode is not "RGB":
- image = rectangle.convert("RGB")
- else:
- image = rectangle
-
- b = np.asarray(image)
- a = b.copy()
- del b
+ if self.bpp == 32 or self.bpp == 16 or self.bpp == 8:
if self.bpp == 32:
redBits = 8
greenBits = 8
blueBits = 8
- elif self.bpp == 8:
- redBits = 4
- greenBits = 4
- blueBits = 4
- #redMask = ((1 << redBits) - 1) << self.red_shift
- #greenMask = ((1 << greenBits) - 1) << self.green_shift
- #blueMask = ((1 << blueBits) - 1) << self.blue_shift
- #print("redMask", redMask, greenMask, blueMask)
+ # image array
+ a = np.asarray(rectangle).copy()
+
+ if self.primaryOrder == "bgr": # bit shifting
+ blueMask = (1 << blueBits) - 1
+ greenMask = ((1 << greenBits) - 1) << self.green_shift
+ redMask = ((1 << redBits) - 1) << self.red_shift
- if self.primaryOrder == "bgr":
- self.blue_shift = 0
- blueMask = (1 << blueBits) - 1
- self.green_shift = blueBits
- greenMask = ((1 << greenBits) - 1) << self.green_shift
- self.red_shift = self.green_shift + greenBits
- redMask = ((1 << redBits) - 1) << self.red_shift
+ a[..., 0] = ( a[..., 0] ) & blueMask >> self.blue_shift
+ a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift
+ a[..., 2] = ( a[..., 2] ) & redMask >> self.red_shift
+
+ else: # RGB
+ redMask = ((1 << redBits) - 1) << self.red_shift
+ greenMask = ((1 << greenBits) - 1) << self.green_shift
+ blueMask = ((1 << blueBits) - 1) << self.blue_shift
+ a[..., 0] = ( a[..., 0] ) & redMask >> self.red_shift
+ a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift
+ a[..., 2] = ( a[..., 2] ) & blueMask >> self.blue_shift
- else: # RGB
- self.red_shift = 0
- redMask = (1 << redBits) - 1
- self.green_shift = redBits
- greenMask = ((1 << greenBits) - 1) << self.green_shift
- self.blue_shift = self.green_shift + greenBits
- blueMask = ((1 << blueBits) - 1) << self.blue_shift
-
- a[..., 0] = ( a[..., 0] ) & redMask >> self.red_shift
- a[..., 1] = ( a[..., 1] ) & greenMask >> self.green_shift
- a[..., 2] = ( a[..., 2] ) & blueMask >> self.blue_shift
-
- image = Image.fromarray(a)
- del a
-
- if self.primaryOrder == "rgb":
- (b, g, r) = image.split()
- image = Image.merge("RGB", (r, g, b))
- del b,g,r
-
- if self.bpp == 32:
+ image = Image.fromarray(a)
+ if self.primaryOrder == "rgb":
+ (b, g, r) = image.split()
+ image = Image.merge("RGB", (r, g, b))
+ del b,g,r
image = image.convert("RGBX")
- elif self.bpp == 8:
- #FIXME: improve 8 bit routines up!, image looks awful!
- if rectangle.mode is not "RGB":
- image = rectangle.convert("RGB")
- else:
- image = rectangle
+ if self.bpp == 16: #BGR565
+ greenBits = 5
+ blueBits = 6
+ redBits = 5
- (r, g, b) = image.split()
- image = Image.merge("RGB", (g, r, b))
- del b,g,r
+ image = rectangle
+ if self.depth == 16:
+ image = image.convert('BGR;16')
+ if self.depth == 15:
+ image = image.convert('BGR;15')
+
+ elif self.bpp == 8: #bgr233
+ redBits = 3
+ greenBits = 3
+ blueBits = 2
+
+ image = rectangle
+
+ p = Image.new('P',(16,16))
+ p.putpalette(bgr233_palette.palette)
- p = Image.new("P",(16,16))
- p.putpalette(self.BGR233)
image = quantizetopalette(image, p, dither=self.CONFIG._8bitdither)
- image.putpalette(self.BGR233)
- del p
- rectangles = 1
- sendbuff.extend(pack("!BxH", 0, rectangles))
- sendbuff.extend(pack("!HHHH", x, y, w, h))
- sendbuff.extend(pack(">i", self.encoding))
- sendbuff.extend( image.tobytes() )
+
+ #image = image.convert('RGB', colors=4).quantize(palette=p)
+ #log.debug(image)
- if hasattr(enc.ENCODINGS, "zlib") and self.encoding == enc.ENCODINGS.zlib - 9998:
- if not firstUpdateSent:
- firstUpdateSent = True
- compress = zlib.compressobj(
- zlib.Z_DEFAULT_COMPRESSION, # level: 0-9
- zlib.DEFLATED, # method: must be DEFLATED
- -15, # window size in bits:
- # -15..-8: negate, suppress header
- # 8..15: normal
- # 16..30: subtract 16, gzip header
- 1, # mem level: 1..8/9
- zlib.Z_DEFAULT_STRATEGY # strategy:
- # 0 = Z_DEFAULT_STRATEGY
- # 1 = Z_FILTERED
- # 2 = Z_HUFFMAN_ONLY
- # 3 = Z_RLE
- # 4 = Z_FIXED
- )
- rectangles = 1
- sendbuff.extend(pack("!BxH", 0, rectangles))
- sendbuff.extend(pack("!HHHH", x, y, w, h))
- sendbuff.extend(pack(">i", self.encoding))
-
- print("Compressing...")
- zlibdata = compress.compress( image.tobytes() )
- zlibdata += compress.flush()
- l = pack("!I", len(zlibdata) )
-
- sendbuff.extend( l ) # send length
- sendbuff.extend( zlibdata ) # send compressed data
-
- else:
- # send with RAW encoding
- sendbuff.extend(enc.ENCODINGS.raw_send_image(x, y, w, h, image))
+ # send image with client defined encoding
+ sendbuff.extend(self.encoding_object.send_image(x, y, w, h, image))
else:
- print("[!] Unsupported BPP: %s" % self.bpp)
+ log.debug("[!] Unsupported BPP: %s" % self.bpp)
self.framebuffer = lastshot
try:
@@ -778,4 +494,4 @@ class VncServer():
# connection closed?
return False
sock.settimeout(stimeout)
- #print("end SendRectangles")
+ #log.debug("end SendRectangles")
diff --git a/requeriments.txt b/requeriments.txt
index 50504bc..38bbd7e 100644
--- a/requeriments.txt
+++ b/requeriments.txt
@@ -1,2 +1,5 @@
-pyuserinput
-
+pydes
+pynput
+numpy
+Pillow-PIL
+elevate
diff --git a/server.py b/server.py
index 0a6afa2..6101922 100755
--- a/server.py
+++ b/server.py
@@ -5,9 +5,19 @@ import pyvncs
from argparse import ArgumentParser
from threading import Thread
from time import sleep
-
import sys
import socket
+import signal
+from lib import log
+
+_debug = log.debug
+#_debug = print
+
+def signal_handler(signal, frame):
+ _debug("Exiting on %s signal..." % signal)
+ sys.exit(0)
+
+signal.signal(signal.SIGINT, signal_handler)
class ControlThread(Thread):
@@ -22,7 +32,7 @@ class ControlThread(Thread):
sleep(1)
for t in threads:
if not t.isAlive():
- print("ControlThread removing dead", t)
+ _debug("ControlThread removing dead", t)
threads.remove(t)
class ClientThread(Thread):
@@ -34,17 +44,17 @@ class ClientThread(Thread):
self.setDaemon(True)
def __del__(self):
- print("ClientThread died")
+ _debug("ClientThread died")
def run(self):
- print("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
- #print("Thread", self)
+ _debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
+ #_debug("Thread", self)
server = pyvncs.server.VncServer(self.sock, VNC_PASSWORD)
server.CONFIG._8bitdither = CONFIG._8bitdither
status = server.init()
if not status:
- print("Error negotiating client init")
+ _debug("Error negotiating client init")
return False
server.protocol()
@@ -61,11 +71,17 @@ def main(argv):
help="Listen in this port, default: %s" % ("5901"), type=int, required=False, default='5901')
parser.add_argument("-P", "--password", help="Sets password", required=True, dest="VNC_PASSWORD")
parser.add_argument("-8", "--8bitdither", help="Enable 8 bit dithering", required=False, action='store_true', dest="dither")
+ parser.add_argument("-O", "--output-file", help="Redirects all debug output to file", required=False, dest="OUTFILE")
+
args = parser.parse_args()
-
+
+ if args.OUTFILE is not None:
+ fsock = open(args.OUTFILE, 'w')
+ sys.stdout = sys.stderr = fsock
+
# Multithreaded Python server
TCP_IP = '0.0.0.0' if not hasattr(args,"TCP_IP") else args.TCP_IP
- TCP_PORT = '0.0.0.0' if not hasattr(args,"TCP_PORT") else args.TCP_PORT
+ TCP_PORT = '5901' if not hasattr(args,"TCP_PORT") else args.TCP_PORT
VNC_PASSWORD = args.VNC_PASSWORD
CONFIG._8bitdither = args.dither
@@ -77,8 +93,21 @@ def main(argv):
controlthread.start()
threads.append(controlthread)
- print("Multithreaded Python server : Waiting for connections from TCP clients...")
- print("Runing on:", sys.platform)
+ _debug("Multithreaded Python server : Waiting for connections from TCP clients...")
+ _debug("Runing on:", sys.platform)
+ if sys.platform in ['win32', 'win64']:
+ from lib.oshelpers import windows as win32
+ if not win32.is_admin():
+ ret = win32.run_as_admin()
+ if ret is None:
+ log.debug("Respawning with admin rights")
+ sys.exit(0)
+ elif ret is True:
+ # admin rights
+ log.debug("Running with admin rights!")
+ else:
+ print('Error(ret=%d): cannot elevate privilege.' % (ret))
+ sys.exit(1)
while True:
sockServer.listen(4)
(conn, (ip,port)) = sockServer.accept()
@@ -95,8 +124,8 @@ if __name__ == "__main__":
main(sys.argv)
except KeyboardInterrupt:
# quit
- print("Exiting on ctrl+c...")
+ _debug("Exiting on ctrl+c...")
#for t in threads:
- # print("Killing", t)
+ # _debug("Killing", t)
sys.exit()
\ No newline at end of file
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..52c7033
--- /dev/null
+++ b/test.py
@@ -0,0 +1,16 @@
+from lib import common
+import time
+
+if __name__ == '__main__':
+ #c = ["/bin/ls", "-alh"]
+ #c = ["/bin/sleep", "5"]
+ c = ["./test.sh"]
+ r = common.proc()
+ r.run(c)
+ print("PID", r.getpid())
+ #print("Waiting...")
+ #r.waitproc()
+ time.sleep(1)
+ r.terminate()
+ del r
+ print("DONE")
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..9c56fd9
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+for a in $(seq 1 5); do
+echo "$a $$"
+sleep 1
+done
+
diff --git a/winservice.py b/winservice.py
new file mode 100644
index 0000000..dea87ec
--- /dev/null
+++ b/winservice.py
@@ -0,0 +1,79 @@
+import win32service
+import win32serviceutil
+import win32api
+import win32con
+import win32event
+import win32evtlogutil
+import servicemanager
+import os
+import sys
+from lib import const
+from lib import common
+from win32api import OutputDebugString as ODS
+import traceback
+
+
+const.SERVICENAME = "Test Service"
+const.SERVICEDNAME = "Test Service"
+const.SERVICEDESC = "Test Service Description"
+
+const.CHILD = [
+ "C:\\Program Files\\Python36\\python.exe",
+ "C:\\pyvncs\\ctrlsrv.py",
+ "-P",
+ "kaka80"
+]
+
+class service(win32serviceutil.ServiceFramework):
+
+ _svc_name_ = const.SERVICENAME
+ _svc_display_name_ = const.SERVICEDNAME
+ _svc_description_ = const.SERVICEDESC
+
+ def __init__(self, args):
+ win32serviceutil.ServiceFramework.__init__(self, args)
+ self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
+
+ def SvcStop(self):
+ self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
+ win32event.SetEvent(self.hWaitStop)
+
+ def SvcDoRun(self):
+ servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, ''))
+ self.timeout = 3000
+
+ servicemanager.LogInfoMsg("%s - is running 1" % const.SERVICENAME)
+ r = common.proc()
+ try:
+ r.run(const.CHILD)
+ except:
+ servicemanager.LogInfoMsg("ERROR: %s" % sys.exc_info()[0])
+ servicemanager.LogInfoMsg(traceback.format_exc())
+ sys.exit(1)
+
+ newpid = r.getpid()
+ servicemanager.LogInfoMsg("%s - started child with pid %s" % (const.SERVICENAME, newpid))
+
+ while True:
+ # Wait for service stop signal, if I timeout, loop again
+ rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
+ # Check to see if self.hWaitStop happened
+ if rc == win32event.WAIT_OBJECT_0:
+ # Stop signal encountered
+ servicemanager.LogInfoMsg("%s - STOPPED" % const.SERVICENAME)
+ r.terminate()
+ break
+ #else:
+ # servicemanager.LogInfoMsg("%s - still running" % const.SERVICENAME)
+
+
+def ctrlHandler(ctrlType):
+ return True
+
+if __name__ == '__main__':
+ ODS("__main__\n")
+ servicemanager.LogInfoMsg("TEST")
+ appdir = os.path.abspath(os.path.dirname(sys.argv[0]))
+ os.chdir(appdir)
+ win32api.SetConsoleCtrlHandler(ctrlHandler, True)
+ win32serviceutil.HandleCommandLine(service)