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/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/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 index 948fa94..4b421b6 100644 --- a/lib/encodings/common.py +++ b/lib/encodings/common.py @@ -15,5 +15,15 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . +encodings = {} + class ENCODINGS: - pass \ No newline at end of file + 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 index 37cb736..6fbe3a6 100644 --- a/lib/encodings/cursor.py +++ b/lib/encodings/cursor.py @@ -16,5 +16,21 @@ # along with this program. If not, see . from . import common +from struct import * +from lib import log +import zlib -common.ENCODINGS.cursor = -239 +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 index 85bfcc8..fbfb36b 100644 --- a/lib/encodings/raw.py +++ b/lib/encodings/raw.py @@ -17,16 +17,30 @@ from . import common from struct import * +from lib import log -def send_image(x, y, w, h, image): - _buff = bytearray() - rectangles = 1 - _buff.extend(pack("!BxH", 0, rectangles)) - _buff.extend(pack("!HHHH", x, y, w, h)) - _buff.extend(pack(">i", common.ENCODINGS.raw)) - _buff.extend( image.tobytes() ) +class Encoding: + _buff = None - return _buff + name = 'raw' + id = 0 + description = 'Raw VNC encoding' + enabled = True + firstUpdateSent = False -common.ENCODINGS.raw = 0 -common.ENCODINGS.raw_send_image = send_image \ No newline at end of file + 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/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 a87805a..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 8c5c215..7de6298 100644 --- a/pyvncs/server.py +++ b/pyvncs/server.py @@ -19,17 +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 = "" @@ -39,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() @@ -60,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 = [ @@ -115,73 +85,8 @@ 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): log.debug("VncServer died") @@ -348,95 +253,19 @@ class VncServer(): 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: #log.debug(".", end='', flush=True) r,_,_ = select.select([self.socket],[],[],0) @@ -476,7 +305,7 @@ class VncServer(): self.primaryOrder = "rgb" else: self.primaryOrder = "bgr" - log.debug("Using order: ", self.primaryOrder) + log.debug("Using order:", self.primaryOrder) continue @@ -488,16 +317,25 @@ class VncServer(): data2 = sock.recv(4 * nencodings, socket.MSG_WAITALL) #log.debug("len", len(data2)) self.client_encodings = unpack("!%si" % nencodings, data2) - #log.debug("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: - log.debug("Remote cursor encoding present") - self.remotecursor = True - self.cursorchanged = True - - if hasattr(enc.ENCODINGS, "zlib") and enc.ENCODINGS.zlib in self.client_encodings: - log.debug("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 @@ -510,89 +348,14 @@ class VncServer(): #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) - log.debug("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: - log.debug("KEY:", kbdkey) - except: - log.debug("KEY: (unprintable)") - - try: - if downflag: - keyboard.Controller().press(kbdkey) - else: - keyboard.Controller().release(kbdkey) - except: - log.debug("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: - log.debug("LEFT PRESSED") - mouse.Controller().press(mouse.Button.left) - left_pressed = 1 - elif not buttons[0] and left_pressed: - log.debug("LEFT RELEASED") - mouse.Controller().release(mouse.Button.left) - left_pressed = 0 - - if buttons[1] and not middle_pressed: - log.debug("MIDDLE PRESSED") - mouse.Controller().press(mouse.Button.middle) - middle_pressed = 1 - elif not buttons[1] and middle_pressed: - log.debug("MIDDLE RELEASED") - mouse.Controller().release(mouse.Button.middle) - middle_pressed = 0 - - if buttons[2] and not right_pressed: - log.debug("RIGHT PRESSED") - mouse.Controller().press(mouse.Button.right) - right_pressed = 1 - elif not buttons[2] and right_pressed: - log.debug("RIGHT RELEASED") - mouse.Controller().release(mouse.Button.right) - right_pressed = 0 - - if buttons[3]: - log.debug("SCROLLUP PRESSED") - mouse.Controller().scroll(0, 2) - - if buttons[4]: - log.debug("SCROLLDOWN PRESSED") - mouse.Controller().scroll(0, -2) - - #log.debug("PointerEvent", buttonmask, x, y) + mousecontroller.process_event(sock.recv(5, socket.MSG_WAITALL)) continue else: @@ -630,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: @@ -656,119 +420,70 @@ class VncServer(): 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 - #log.debug("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)) - - log.debug("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: log.debug("[!] Unsupported BPP: %s" % self.bpp) diff --git a/requeriments.txt b/requeriments.txt index dbfde75..38bbd7e 100644 --- a/requeriments.txt +++ b/requeriments.txt @@ -1,4 +1,5 @@ pydes pynput numpy - +Pillow-PIL +elevate diff --git a/server.py b/server.py index 02a9f95..6101922 100755 --- a/server.py +++ b/server.py @@ -10,8 +10,8 @@ import socket import signal from lib import log -#_debug = log.debug -_debug = print +_debug = log.debug +#_debug = print def signal_handler(signal, frame): _debug("Exiting on %s signal..." % signal) @@ -95,6 +95,19 @@ def main(argv): _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()