Addded README
Code cleanup
This commit is contained in:
23
README.md
Normal file
23
README.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# PyVNCs
|
||||||
|
|
||||||
|
Simple command line multiplatform python VNC Server.
|
||||||
|
|
||||||
|
This is a simple command line VNC server, aimed to quick remote support situations.
|
||||||
|
|
||||||
|
This VNC Server is proven to work on:
|
||||||
|
- Linux (Xorg, Wayland not supported so far)
|
||||||
|
- Mac OS
|
||||||
|
- Windows (7 and onwards)
|
||||||
|
|
||||||
|
Supported encodings:
|
||||||
|
- raw
|
||||||
|
- zlib
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- Add support for more (efficient) encodings
|
||||||
|
- VEnCrypt support (WIP)
|
||||||
|
- Add support for uPnP (for incomming connections on supported nat gateways)
|
||||||
|
|
||||||
|
FAQ:
|
||||||
|
Q: Why a VNC server on python?
|
||||||
|
A: Because python is fun!
|
||||||
88
lib/auth/vencrypt.py
Normal file
88
lib/auth/vencrypt.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
from lib import log
|
||||||
|
from time import sleep
|
||||||
|
import ssl
|
||||||
|
import select
|
||||||
|
from struct import *
|
||||||
|
|
||||||
|
class VeNCrypt():
|
||||||
|
|
||||||
|
subtypes = [
|
||||||
|
256, # Plain
|
||||||
|
#258, # TLSVnc # FIXME: not yet implemented
|
||||||
|
#259, # TLSPlain # FIXME: not yet implemented
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, sock):
|
||||||
|
self.getbuff = lambda _: None
|
||||||
|
self.sock = sock
|
||||||
|
self.client_subtype = None
|
||||||
|
self.pem_file = None
|
||||||
|
log.debug(__name__, "initialized")
|
||||||
|
|
||||||
|
# send version
|
||||||
|
version = b'\x00\x02' # 0.2
|
||||||
|
sock.send(version)
|
||||||
|
data = sock.recv(2)
|
||||||
|
if data != version:
|
||||||
|
sock.send(b'\x01')
|
||||||
|
sock.close()
|
||||||
|
raise Exception("unknown vencrypt version")
|
||||||
|
|
||||||
|
sock.send(b'\x00')
|
||||||
|
|
||||||
|
def send_subtypes(self):
|
||||||
|
# send subtypes
|
||||||
|
data = pack('!B', len(self.subtypes))
|
||||||
|
for i in self.subtypes:
|
||||||
|
data += pack('!I', i)
|
||||||
|
log.debug(__name__, "subtype", i)
|
||||||
|
|
||||||
|
self.sock.send(data)
|
||||||
|
|
||||||
|
# get client choosen subtype
|
||||||
|
data = self.sock.recv(4)
|
||||||
|
(data,) = unpack('!I', data)
|
||||||
|
log.debug("client subtype", data)
|
||||||
|
self.client_subtype = data
|
||||||
|
|
||||||
|
def auth_plain(self, userlist={}):
|
||||||
|
data = self.sock.recv(8)
|
||||||
|
user_length, pass_length = unpack('!II', data)
|
||||||
|
username = self.sock.recv(user_length).decode()
|
||||||
|
password = self.sock.recv(pass_length).decode()
|
||||||
|
#log.debug("user", username, password)
|
||||||
|
|
||||||
|
if userlist.get(username) == password:
|
||||||
|
self.sock.send(pack("!I", 0))
|
||||||
|
log.debug(__name__, "Auth OK")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
log.debug(__name__, "Invalid auth")
|
||||||
|
sleep(3)
|
||||||
|
self.sock.send(pack("!I", 1))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def auth_tls_plain(self, userlist={}):
|
||||||
|
#TODO: implement TLS plain
|
||||||
|
log.debug(__name__, 'Using TLSPlain')
|
||||||
|
|
||||||
|
self.sock.sendall(pack("!I", 1)) # send ACK
|
||||||
|
|
||||||
|
#data = self.getbuff(30)
|
||||||
|
#print("data", data)
|
||||||
|
|
||||||
|
|
||||||
|
#sslctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
|
sslctx = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_SERVER)
|
||||||
|
sslctx.protocol = ssl.PROTOCOL_TLS
|
||||||
|
#sslctx.load_cert_chain(certfile=self.pem_file, keyfile=self.pem_file)
|
||||||
|
# this is quite insecure...
|
||||||
|
sslctx.set_ciphers(":aNULL:kDHE:kEDH:ADH:DH:kECDHE:kEECDH:AECDH:ECDH")
|
||||||
|
|
||||||
|
self.sock.settimeout(30)
|
||||||
|
self.sock = sslctx.wrap_socket(self.sock, server_side=True)
|
||||||
|
self.sock.settimeout(None)
|
||||||
|
|
||||||
|
ret = self.auth_plain(userlist=userlist)
|
||||||
|
return ret
|
||||||
|
|
||||||
45
lib/auth/vnc_auth.py
Normal file
45
lib/auth/vnc_auth.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from time import sleep
|
||||||
|
from struct import *
|
||||||
|
from pyDes import *
|
||||||
|
import os
|
||||||
|
from lib import log
|
||||||
|
|
||||||
|
class VNCAuth():
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.getbuff = lambda _: None
|
||||||
|
|
||||||
|
def _mirrorBits(self, key):
|
||||||
|
newkey = []
|
||||||
|
for ki in range(len(key)):
|
||||||
|
bsrc = key[ki]
|
||||||
|
btgt = 0
|
||||||
|
for i in range(8):
|
||||||
|
if ord(bsrc) & (1 << i):
|
||||||
|
btgt = btgt | (1 << 7-i)
|
||||||
|
newkey.append(btgt)
|
||||||
|
|
||||||
|
return newkey
|
||||||
|
|
||||||
|
def auth(self, sock, password):
|
||||||
|
# el cliente encripta el challenge con la contraseña ingresada como key
|
||||||
|
pw = (password + '\0' * 8)[:8]
|
||||||
|
challenge = os.urandom(16) # challenge
|
||||||
|
sock.send(challenge) # send challenge
|
||||||
|
# obtener desde el cliente el dato encritado
|
||||||
|
data = self.getbuff(30)
|
||||||
|
# la encriptacion de challenge, con pw como key debe dar data
|
||||||
|
|
||||||
|
k = des(self._mirrorBits(pw))
|
||||||
|
crypted = k.encrypt(challenge)
|
||||||
|
|
||||||
|
if data == crypted:
|
||||||
|
# Handshake successful
|
||||||
|
sock.send(pack("!I", 0))
|
||||||
|
log.debug(__name__, "Auth OK")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
log.debug(__name__, "Invalid auth")
|
||||||
|
sleep(3)
|
||||||
|
sock.send(pack("!I", 1))
|
||||||
|
return False
|
||||||
30
lib/clipboardctrl.py
Normal file
30
lib/clipboardctrl.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from struct import *
|
||||||
|
|
||||||
|
class ClipboardController():
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def client_cut_text(self, sock):
|
||||||
|
"""
|
||||||
|
The client has new ISO 8859-1 (Latin-1) text in its cut buffer.
|
||||||
|
Ends of lines are represented by the linefeed / newline character (value 10) alone. No carriage-return (value 13) is needed.
|
||||||
|
|
||||||
|
No. of bytes Type [Value] Description
|
||||||
|
1 U8 6 message-type
|
||||||
|
3 padding
|
||||||
|
4 U32 length
|
||||||
|
length U8 array text
|
||||||
|
"""
|
||||||
|
|
||||||
|
# read padding
|
||||||
|
_ = sock.recv(3)
|
||||||
|
|
||||||
|
# read length
|
||||||
|
length = sock.recv(4)
|
||||||
|
(length, ) = unpack('!I', length)
|
||||||
|
|
||||||
|
# read text
|
||||||
|
text = sock.recv(length)
|
||||||
|
|
||||||
|
return text
|
||||||
@@ -66,3 +66,9 @@ class proc:
|
|||||||
def waitproc(self):
|
def waitproc(self):
|
||||||
while psutil.pid_exists(self.pid):
|
while psutil.pid_exists(self.pid):
|
||||||
time.sleep(.25)
|
time.sleep(.25)
|
||||||
|
|
||||||
|
def reshape(a, cols):
|
||||||
|
|
||||||
|
for i in range(0, int(len(a)/cols), cols):
|
||||||
|
print(i)
|
||||||
|
print(a[i:i+cols])
|
||||||
|
|||||||
39
lib/log.py
39
lib/log.py
@@ -15,14 +15,41 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
logging.basicConfig(level=logging.DEBUG, format='[%(threadName)s] %(message)s')
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s: %(message)s')
|
||||||
logger = logging.getLogger('pyvncs')
|
logger = logging.getLogger('pyvncs')
|
||||||
|
|
||||||
|
def _log(*args, logtype='debug'):
|
||||||
|
|
||||||
|
func = inspect.stack()[2][3]
|
||||||
|
if func[0] != '<':
|
||||||
|
func = "%s():" % func
|
||||||
|
|
||||||
|
_str = func
|
||||||
|
|
||||||
def debug(*args):
|
|
||||||
str = ""
|
|
||||||
for s in args:
|
for s in args:
|
||||||
str = "%s %s" % (str, s)
|
_str = "%s %s" % (_str, s)
|
||||||
str = str.strip()
|
_str = _str.strip()
|
||||||
logger.debug(str)
|
f = getattr(logger, logtype)
|
||||||
|
#logger.debug(str)
|
||||||
|
f(_str)
|
||||||
|
|
||||||
|
def __getattr__(name):
|
||||||
|
|
||||||
|
def method(*args):
|
||||||
|
_str = ''
|
||||||
|
if args:
|
||||||
|
for s in args:
|
||||||
|
_str = "%s %s" % (_str, s)
|
||||||
|
_str = _str.strip()
|
||||||
|
|
||||||
|
_log(_str, logtype=name)
|
||||||
|
|
||||||
|
return method
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
109
lib/rfb_bitmap.py
Normal file
109
lib/rfb_bitmap.py
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import numpy as np
|
||||||
|
from PIL import Image, ImageChops, ImageDraw, ImagePalette
|
||||||
|
from lib import bgr233_palette
|
||||||
|
|
||||||
|
__all__ = ['RfbBitmap']
|
||||||
|
|
||||||
|
class RfbBitmap():
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.bpp = None
|
||||||
|
self.depth = None
|
||||||
|
self.truecolor = None
|
||||||
|
self.primaryOrder = 'rgb'
|
||||||
|
self.dither = False
|
||||||
|
self.red_shift = None
|
||||||
|
self.green_shift = None
|
||||||
|
self.blue_shift = None
|
||||||
|
|
||||||
|
def __quantizetopalette(self, silf, palette, dither=False):
|
||||||
|
"""Converts an RGB or L mode image to use a given P image's palette."""
|
||||||
|
silf.load()
|
||||||
|
|
||||||
|
# use palette from reference image
|
||||||
|
palette.load()
|
||||||
|
if palette.mode != "P":
|
||||||
|
raise ValueError("bad mode for palette image")
|
||||||
|
if silf.mode != "RGB" and silf.mode != "L":
|
||||||
|
raise ValueError(
|
||||||
|
"only RGB or L mode images can be quantized to a palette"
|
||||||
|
)
|
||||||
|
im = silf.im.convert("P", 1 if dither else 0, palette.im)
|
||||||
|
# the 0 above means turn OFF dithering
|
||||||
|
|
||||||
|
# Later versions of Pillow (4.x) rename _makeself to _new
|
||||||
|
try:
|
||||||
|
return silf._new(im)
|
||||||
|
except AttributeError:
|
||||||
|
return silf._makeself(im)
|
||||||
|
|
||||||
|
|
||||||
|
def get_bitmap(self, rectangle):
|
||||||
|
if self.bpp == 32:
|
||||||
|
redBits = 8
|
||||||
|
greenBits = 8
|
||||||
|
blueBits = 8
|
||||||
|
|
||||||
|
# image array
|
||||||
|
a = np.asarray(rectangle).copy()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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")
|
||||||
|
return image
|
||||||
|
|
||||||
|
elif self.bpp == 16: #BGR565
|
||||||
|
greenBits = 5
|
||||||
|
blueBits = 6
|
||||||
|
redBits = 5
|
||||||
|
image = rectangle
|
||||||
|
|
||||||
|
if self.primaryOrder == "bgr": # FIXME: does not work
|
||||||
|
(b, g, r) = image.split()
|
||||||
|
image = Image.merge("RGB", (r, g, b))
|
||||||
|
|
||||||
|
if self.depth == 16:
|
||||||
|
image = image.convert('BGR;16')
|
||||||
|
if self.depth == 15:
|
||||||
|
image = image.convert('BGR;15')
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
elif self.bpp == 8: #bgr233
|
||||||
|
redBits = 3
|
||||||
|
greenBits = 3
|
||||||
|
blueBits = 2
|
||||||
|
image = rectangle
|
||||||
|
|
||||||
|
palette = bgr233_palette.palette
|
||||||
|
if self.primaryOrder == "rgb":
|
||||||
|
#(b, g, r) = image.split()
|
||||||
|
#image = Image.merge("RGB", (r, g, b))
|
||||||
|
|
||||||
|
palette = np.reshape(palette, (-3,3))
|
||||||
|
palette[:,[0, 2]] = palette[:,[2, 0]]
|
||||||
|
palette = palette.flatten()
|
||||||
|
palette = list(palette)
|
||||||
|
|
||||||
|
p = Image.new('P',(16,16))
|
||||||
|
p.putpalette(palette)
|
||||||
|
|
||||||
|
image = self.__quantizetopalette(image, p, dither=self.dither)
|
||||||
|
|
||||||
|
#image = image.convert('RGB', colors=4).quantize(palette=p)
|
||||||
|
#log.debug(image)
|
||||||
|
return image
|
||||||
|
|
||||||
|
else:
|
||||||
|
# unsupported BPP
|
||||||
|
return None
|
||||||
236
pyvncs/server.py
236
pyvncs/server.py
@@ -30,94 +30,52 @@ import numpy as np
|
|||||||
|
|
||||||
from lib import mousectrl
|
from lib import mousectrl
|
||||||
from lib import kbdctrl
|
from lib import kbdctrl
|
||||||
|
from lib import clipboardctrl
|
||||||
from lib.imagegrab import ImageGrab
|
from lib.imagegrab import ImageGrab
|
||||||
|
from lib.rfb_bitmap import RfbBitmap
|
||||||
from lib import log
|
from lib import log
|
||||||
from lib import bgr233_palette
|
|
||||||
|
|
||||||
# encodings support
|
# encodings support
|
||||||
import lib.encodings as encs
|
import lib.encodings as encs
|
||||||
from lib.encodings.common import ENCODINGS
|
from lib.encodings.common import ENCODINGS
|
||||||
|
|
||||||
def hexdump(data):
|
# auth support
|
||||||
str = ""
|
from lib.auth.vnc_auth import VNCAuth
|
||||||
for d in data:
|
from lib.auth.vencrypt import VeNCrypt
|
||||||
str += hex(d)
|
|
||||||
str += "(%s) " % d
|
|
||||||
return str
|
|
||||||
|
|
||||||
def quantizetopalette(silf, palette, dither=False):
|
|
||||||
"""Converts an RGB or L mode image to use a given P image's palette."""
|
|
||||||
|
|
||||||
silf.load()
|
|
||||||
|
|
||||||
# use palette from reference image
|
|
||||||
palette.load()
|
|
||||||
if palette.mode != "P":
|
|
||||||
raise ValueError("bad mode for palette image")
|
|
||||||
if silf.mode != "RGB" and silf.mode != "L":
|
|
||||||
raise ValueError(
|
|
||||||
"only RGB or L mode images can be quantized to a palette"
|
|
||||||
)
|
|
||||||
im = silf.im.convert("P", 1 if dither else 0, palette.im)
|
|
||||||
# the 0 above means turn OFF dithering
|
|
||||||
|
|
||||||
# Later versions of Pillow (4.x) rename _makeself to _new
|
|
||||||
try:
|
|
||||||
return silf._new(im)
|
|
||||||
except AttributeError:
|
|
||||||
return silf._makeself(im)
|
|
||||||
|
|
||||||
class VncServer():
|
class VncServer():
|
||||||
|
|
||||||
class CONFIG:
|
class RFB_SECTYPES:
|
||||||
_8bitdither = False
|
vncauth = 2 # plain VNC auth
|
||||||
|
vencrypt = 19 # VeNCrypt
|
||||||
|
unix = 129 # Unix Login Authentication
|
||||||
|
|
||||||
encoding_object = None
|
encoding_object = None
|
||||||
|
|
||||||
def __init__(self, socket, password):
|
def __init__(self, socket, password=None, auth_type=None, pem_file='', vnc_config = None):
|
||||||
self.RFB_VERSION = '003.008'
|
self.RFB_VERSION = '003.008'
|
||||||
self.RFB_SECTYPES = [
|
|
||||||
2, # VNC auth
|
|
||||||
19 # VeNCrypt
|
|
||||||
]
|
|
||||||
self.initmsg = ("RFB %s\n" % self.RFB_VERSION)
|
self.initmsg = ("RFB %s\n" % self.RFB_VERSION)
|
||||||
self.socket = socket
|
self.socket = socket
|
||||||
self.framebuffer = None
|
self.framebuffer = None
|
||||||
self.password = password
|
self.password = password
|
||||||
self.sectypes = self.RFB_SECTYPES
|
|
||||||
self.cursor_support = False
|
self.cursor_support = False
|
||||||
|
self.auth_type = auth_type
|
||||||
|
self.pem_file = pem_file
|
||||||
|
self.vnc_config = vnc_config
|
||||||
|
|
||||||
|
log.debug("Configured auth type:", self.auth_type)
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
log.debug("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)
|
|
||||||
d = k.encrypt(data)
|
|
||||||
return d
|
|
||||||
|
|
||||||
def decrypt(self, challenge, data):
|
|
||||||
k = des(challenge, ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
|
|
||||||
return k.decrypt(data)
|
|
||||||
|
|
||||||
def mirrorBits(self, key):
|
|
||||||
newkey = []
|
|
||||||
for ki in range(len(key)):
|
|
||||||
bsrc = key[ki]
|
|
||||||
btgt = 0
|
|
||||||
for i in range(8):
|
|
||||||
if ord(bsrc) & (1 << i):
|
|
||||||
btgt = btgt | (1 << 7-i)
|
|
||||||
newkey.append(btgt)
|
|
||||||
|
|
||||||
return newkey
|
|
||||||
|
|
||||||
def sendmessage(self, message):
|
def sendmessage(self, message):
|
||||||
''' sends a RFB message, usually an error message '''
|
''' sends a RFB message, usually an error message '''
|
||||||
sock = self.socket
|
sock = self.socket
|
||||||
message = bytes(message, 'iso8859-1')
|
message = bytes(message, 'iso8859-1')
|
||||||
# 4 bytes lenght and string
|
# 4 bytes lenght and string
|
||||||
buff = pack("I%ds" % (len(message),), len(message), message)
|
buff = pack("I%ds" % (len(message),), len(message), message)
|
||||||
sock.send(message)
|
sock.send(buff)
|
||||||
|
|
||||||
def getbuff(self, timeout):
|
def getbuff(self, timeout):
|
||||||
sock = self.socket
|
sock = self.socket
|
||||||
@@ -149,17 +107,27 @@ class VncServer():
|
|||||||
log.debug("client, server:", client_version, server_version)
|
log.debug("client, server:", client_version, server_version)
|
||||||
|
|
||||||
# security types handshake
|
# security types handshake
|
||||||
sendbuff = pack("B", len(self.sectypes)) # number of security types
|
# sectypes = [
|
||||||
sendbuff += pack('%sB' % len(self.sectypes), *self.sectypes) # send available sec types
|
# self.RFB_SECTYPES.vncauth,
|
||||||
|
# self.RFB_SECTYPES.vencrypt
|
||||||
|
# ]
|
||||||
|
|
||||||
|
sectypes = [
|
||||||
|
self.auth_type
|
||||||
|
]
|
||||||
|
log.debug('sectypes', sectypes)
|
||||||
|
sendbuff = pack("B", len(sectypes)) # number of security types
|
||||||
|
sendbuff += pack('%sB' % len(sectypes), *sectypes) # send available sec types
|
||||||
sock.send(sendbuff)
|
sock.send(sendbuff)
|
||||||
|
|
||||||
|
# get client choosen security type
|
||||||
data = self.getbuff(30)
|
data = self.getbuff(30)
|
||||||
try:
|
try:
|
||||||
sectype = unpack("B", data)[0]
|
sectype = unpack("B", data)[0]
|
||||||
except:
|
except:
|
||||||
sectype = None
|
sectype = None
|
||||||
|
|
||||||
if sectype not in self.sectypes:
|
if sectype not in sectypes:
|
||||||
log.debug("Incompatible security type: %s" % data)
|
log.debug("Incompatible security type: %s" % data)
|
||||||
sock.send(pack("B", 1)) # failed handshake
|
sock.send(pack("B", 1)) # failed handshake
|
||||||
self.sendmessage("Incompatible security type")
|
self.sendmessage("Incompatible security type")
|
||||||
@@ -169,28 +137,57 @@ class VncServer():
|
|||||||
log.debug("sec type data: %s" % data)
|
log.debug("sec type data: %s" % data)
|
||||||
|
|
||||||
# VNC Auth
|
# VNC Auth
|
||||||
if sectype == 2:
|
if sectype == self.RFB_SECTYPES.vncauth:
|
||||||
# el cliente encripta el challenge con la contraseña ingresada como key
|
auth = VNCAuth()
|
||||||
pw = (self.password + '\0' * 8)[:8]
|
auth.getbuff = self.getbuff
|
||||||
challenge = os.urandom(16) # challenge
|
if not auth.auth(sock, self.password):
|
||||||
sock.send(challenge) # send challenge
|
msg = "Auth failed."
|
||||||
# obtener desde el cliente el dato encritado
|
sendbuff = pack("I", len(msg))
|
||||||
data = self.getbuff(30)
|
sendbuff += msg.encode()
|
||||||
# la encriptacion de challenge, con pw como key debe dar data
|
sock.send(sendbuff)
|
||||||
|
sock.close()
|
||||||
k = des(self.mirrorBits(pw))
|
|
||||||
crypted = k.encrypt(challenge)
|
|
||||||
|
|
||||||
if data == crypted:
|
|
||||||
# Handshake successful
|
|
||||||
sock.send(pack("I", 0))
|
|
||||||
log.debug("Auth OK")
|
|
||||||
else:
|
|
||||||
log.debug("Invalid auth")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# VeNCrypt
|
||||||
|
elif sectype == self.RFB_SECTYPES.vencrypt:
|
||||||
|
userlist = {}
|
||||||
|
try:
|
||||||
|
userlist[self.password.split(':')[0]] = self.password.split(':')[1]
|
||||||
|
except Exception as ex:
|
||||||
|
log.debug("Unable to parse username:password combination.\n%s" % ex)
|
||||||
|
sock.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
auth = VeNCrypt(sock)
|
||||||
|
auth.getbuff = self.getbuff
|
||||||
|
auth.send_subtypes()
|
||||||
|
client_subtype = auth.client_subtype
|
||||||
|
|
||||||
|
if client_subtype == 256: # Vencrypt Plain auth
|
||||||
|
if not auth.auth_plain(userlist):
|
||||||
|
sock.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if client_subtype == 259: # Vencrypt TLSPlain auth
|
||||||
|
auth.pem_file = self.pem_file
|
||||||
|
auth.socket = self.socket
|
||||||
|
if not auth.auth_tls_plain(userlist):
|
||||||
|
sock.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
# unsupported subtype
|
||||||
|
log.debug("Unsuported client_subtype", client_subtype)
|
||||||
|
sock.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
#elif sectype == self.RFB_SECTYPES.unix:
|
||||||
|
# log.debug("UNIX!!")
|
||||||
|
|
||||||
#unsupported VNC auth type
|
#unsupported VNC auth type
|
||||||
else:
|
else:
|
||||||
|
log.debug("Unsupported auth type")
|
||||||
|
sock.close()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# get ClientInit
|
# get ClientInit
|
||||||
@@ -216,7 +213,7 @@ class VncServer():
|
|||||||
height = size[1]
|
height = size[1]
|
||||||
self.height = height
|
self.height = height
|
||||||
bpp = 32 # FIXME: get real bpp
|
bpp = 32 # FIXME: get real bpp
|
||||||
depth = 32 # FIXME: get real depth
|
depth = 24 # FIXME: get real depth
|
||||||
self.depth = depth
|
self.depth = depth
|
||||||
self.bpp = bpp
|
self.bpp = bpp
|
||||||
bigendian = 0
|
bigendian = 0
|
||||||
@@ -233,13 +230,14 @@ class VncServer():
|
|||||||
self.green_shift = green_shift
|
self.green_shift = green_shift
|
||||||
blue_shift = 0
|
blue_shift = 0
|
||||||
self.blue_shift = blue_shift
|
self.blue_shift = blue_shift
|
||||||
|
self.rfb_bitmap = RfbBitmap()
|
||||||
|
|
||||||
sendbuff = pack("!HH", width, height)
|
sendbuff = pack("!HH", width, height)
|
||||||
sendbuff += pack("!BBBB", bpp, depth, bigendian, truecolor)
|
sendbuff += pack("!BBBB", bpp, depth, bigendian, truecolor)
|
||||||
sendbuff += pack("!HHHBBB", red_maximum, green_maximum, blue_maximum, red_shift, green_shift, blue_shift)
|
sendbuff += pack("!HHHBBB", red_maximum, green_maximum, blue_maximum, red_shift, green_shift, blue_shift)
|
||||||
sendbuff += pack("!xxx") # padding
|
sendbuff += pack("!xxx") # padding
|
||||||
|
|
||||||
desktop_name = "Test VNC"
|
desktop_name = self.vnc_config.win_title
|
||||||
desktop_name_len = len(desktop_name)
|
desktop_name_len = len(desktop_name)
|
||||||
|
|
||||||
sendbuff += pack("!I", desktop_name_len)
|
sendbuff += pack("!I", desktop_name_len)
|
||||||
@@ -261,6 +259,7 @@ class VncServer():
|
|||||||
|
|
||||||
mousecontroller = mousectrl.MouseController()
|
mousecontroller = mousectrl.MouseController()
|
||||||
kbdcontroller = kbdctrl.KeyboardController()
|
kbdcontroller = kbdctrl.KeyboardController()
|
||||||
|
clipboardcontroller = clipboardctrl.ClipboardController()
|
||||||
|
|
||||||
self.primaryOrder = "rgb"
|
self.primaryOrder = "rgb"
|
||||||
self.encoding = ENCODINGS.raw
|
self.encoding = ENCODINGS.raw
|
||||||
@@ -358,6 +357,10 @@ class VncServer():
|
|||||||
mousecontroller.process_event(sock.recv(5, socket.MSG_WAITALL))
|
mousecontroller.process_event(sock.recv(5, socket.MSG_WAITALL))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if data[0] == 6: # ClientCutText
|
||||||
|
text = clipboardcontroller.client_cut_text(sock)
|
||||||
|
log.debug("ClientCutText:", text)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
data2 = sock.recv(4096)
|
data2 = sock.recv(4096)
|
||||||
log.debug("RAW Server received data:", repr(data[0]) , data+data2)
|
log.debug("RAW Server received data:", repr(data[0]) , data+data2)
|
||||||
@@ -421,66 +424,17 @@ class VncServer():
|
|||||||
sock.settimeout(None)
|
sock.settimeout(None)
|
||||||
|
|
||||||
if self.bpp == 32 or self.bpp == 16 or self.bpp == 8:
|
if self.bpp == 32 or self.bpp == 16 or self.bpp == 8:
|
||||||
|
bitmap = self.rfb_bitmap
|
||||||
|
bitmap.bpp = self.bpp
|
||||||
|
bitmap.depth = self.depth
|
||||||
|
bitmap.dither = self.vnc_config.eightbitdither
|
||||||
|
bitmap.primaryOrder = self.primaryOrder
|
||||||
|
bitmap.truecolor = self.truecolor
|
||||||
|
bitmap.red_shift = self.red_shift
|
||||||
|
bitmap.green_shift = self.green_shift
|
||||||
|
bitmap.blue_shift = self.blue_shift
|
||||||
|
|
||||||
if self.bpp == 32:
|
image = bitmap.get_bitmap(rectangle)
|
||||||
redBits = 8
|
|
||||||
greenBits = 8
|
|
||||||
blueBits = 8
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
if self.bpp == 16: #BGR565
|
|
||||||
greenBits = 5
|
|
||||||
blueBits = 6
|
|
||||||
redBits = 5
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
image = quantizetopalette(image, p, dither=self.CONFIG._8bitdither)
|
|
||||||
|
|
||||||
#image = image.convert('RGB', colors=4).quantize(palette=p)
|
|
||||||
#log.debug(image)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# send image with client defined encoding
|
# send image with client defined encoding
|
||||||
sendbuff.extend(self.encoding_object.send_image(x, y, w, h, image))
|
sendbuff.extend(self.encoding_object.send_image(x, y, w, h, image))
|
||||||
|
|||||||
16
test.py
16
test.py
@@ -1,16 +0,0 @@
|
|||||||
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")
|
|
||||||
@@ -7,11 +7,11 @@ from threading import Thread
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
import sys
|
import sys
|
||||||
import socket
|
import socket
|
||||||
|
import ssl
|
||||||
import signal
|
import signal
|
||||||
from lib import log
|
from lib import log
|
||||||
|
|
||||||
_debug = log.debug
|
_debug = log.debug
|
||||||
#_debug = print
|
|
||||||
|
|
||||||
def signal_handler(signal, frame):
|
def signal_handler(signal, frame):
|
||||||
_debug("Exiting on %s signal..." % signal)
|
_debug("Exiting on %s signal..." % signal)
|
||||||
@@ -19,79 +19,84 @@ def signal_handler(signal, frame):
|
|||||||
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
sleep(1)
|
|
||||||
for t in threads:
|
|
||||||
if not t.isAlive():
|
|
||||||
_debug("ControlThread removing dead", t)
|
|
||||||
threads.remove(t)
|
|
||||||
|
|
||||||
class ClientThread(Thread):
|
class ClientThread(Thread):
|
||||||
def __init__(self, sock, ip, port):
|
def __init__(self, sock, ip, port, vnc_config):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.port = port
|
self.port = port
|
||||||
self.sock = sock
|
self.sock = sock
|
||||||
self.setDaemon(True)
|
self.setDaemon(True)
|
||||||
|
self.vnc_config = vnc_config
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
_debug("ClientThread died")
|
_debug("ClientThread died")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
_debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
|
_debug("[+] New server socket thread started for " + self.ip + ":" + str(self.port))
|
||||||
#_debug("Thread", self)
|
server = pyvncs.server.VncServer(self.sock,
|
||||||
server = pyvncs.server.VncServer(self.sock, VNC_PASSWORD)
|
auth_type=self.vnc_config.auth_type,
|
||||||
server.CONFIG._8bitdither = CONFIG._8bitdither
|
password=self.vnc_config.vnc_password,
|
||||||
|
pem_file=self.vnc_config.pem_file,
|
||||||
|
vnc_config=self.vnc_config
|
||||||
|
)
|
||||||
|
#server.vnc_config.eightbitdither = self.vnc_config.eightbitdither
|
||||||
status = server.init()
|
status = server.init()
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
_debug("Error negotiating client init")
|
_debug("Error negotiating client init")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
server.protocol()
|
server.protocol()
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
global CONFIG, TCP_IP, TCP_PORT, VNC_PASSWORD, threads, controlthread
|
class vnc_config:
|
||||||
class CONFIG:
|
pass
|
||||||
_8bitdither = False
|
|
||||||
|
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
parser.add_argument("-l", "--listen-address", dest="TCP_IP",
|
parser.add_argument("-l", "--listen-address", dest="listen_addr",
|
||||||
help="Listen in this address, default: %s" % ("0.0.0.0"), required=False, default='0.0.0.0')
|
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",
|
parser.add_argument("-p", "--port", dest="listen_port",
|
||||||
help="Listen in this port, default: %s" % ("5901"), type=int, required=False, default='5901')
|
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("-A", "--auth-type",
|
||||||
|
help="Sets VNC authentication type (supported: 2(vnc), 19(vencrypt))",
|
||||||
|
required=False,
|
||||||
|
type=int,
|
||||||
|
default=2,
|
||||||
|
dest="auth_type"
|
||||||
|
)
|
||||||
|
parser.add_argument("-C", "--cert-file",
|
||||||
|
help="SSL PEM file",
|
||||||
|
required=False,
|
||||||
|
type=str,
|
||||||
|
default='',
|
||||||
|
dest='pem_file'
|
||||||
|
)
|
||||||
|
parser.add_argument("-P", "--vncpassword", help="Sets authentication 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("-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")
|
parser.add_argument("-O", "--output-file", help="Redirects all debug output to file", required=False, dest="outfile")
|
||||||
|
parser.add_argument("-t", "--title", help="VNC Window title", required=False, dest="win_title", default="pyvncs")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.OUTFILE is not None:
|
if args.outfile is not None:
|
||||||
fsock = open(args.OUTFILE, 'w')
|
try:
|
||||||
|
fsock = open(args.outfile, 'w')
|
||||||
|
except Exception as ex:
|
||||||
|
print("Error:", ex, file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
sys.stdout = sys.stderr = fsock
|
sys.stdout = sys.stderr = fsock
|
||||||
|
|
||||||
# Multithreaded Python server
|
vnc_config.vnc_password = args.vnc_password
|
||||||
TCP_IP = '0.0.0.0' if not hasattr(args,"TCP_IP") else args.TCP_IP
|
vnc_config.eightbitdither = args.dither
|
||||||
TCP_PORT = '5901' if not hasattr(args,"TCP_PORT") else args.TCP_PORT
|
vnc_config.auth_type = args.auth_type
|
||||||
VNC_PASSWORD = args.VNC_PASSWORD
|
vnc_config.pem_file = args.pem_file
|
||||||
CONFIG._8bitdither = args.dither
|
vnc_config.win_title = args.win_title
|
||||||
|
|
||||||
sockServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sockServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sockServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
sockServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
sockServer.bind((TCP_IP, TCP_PORT))
|
sockServer.bind((args.listen_addr, args.listen_port))
|
||||||
|
|
||||||
controlthread = ControlThread(threads)
|
|
||||||
controlthread.start()
|
|
||||||
threads.append(controlthread)
|
|
||||||
|
|
||||||
_debug("Multithreaded Python server : Waiting for connections from TCP clients...")
|
_debug("Multithreaded Python server : Waiting for connections from TCP clients...")
|
||||||
_debug("Runing on:", sys.platform)
|
_debug("Runing on:", sys.platform)
|
||||||
@@ -111,21 +116,16 @@ def main(argv):
|
|||||||
while True:
|
while True:
|
||||||
sockServer.listen(4)
|
sockServer.listen(4)
|
||||||
(conn, (ip,port)) = sockServer.accept()
|
(conn, (ip,port)) = sockServer.accept()
|
||||||
newthread = ClientThread(conn, ip, port)
|
newthread = ClientThread(sock=conn, ip=ip, port=port, vnc_config=vnc_config)
|
||||||
newthread.setDaemon(True)
|
newthread.setDaemon(True)
|
||||||
newthread.start()
|
newthread.start()
|
||||||
threads.append(newthread)
|
|
||||||
#print(threads)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
threads = []
|
|
||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# quit
|
# quit
|
||||||
_debug("Exiting on ctrl+c...")
|
_debug("Exiting on ctrl+c...")
|
||||||
#for t in threads:
|
|
||||||
# _debug("Killing", t)
|
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
Reference in New Issue
Block a user