Projects STRLCPY aardwolf Commits 5949be04
🤬
  • internal desktop buffers present and reachable

  • Loading...
  • skelsec committed 3 years ago
    5949be04
    1 parent 332217c9
  • ■ ■ ■ ■ ■
    aardwolf/commons/queuedata/video.py
    1 1  import enum
     2 +import io
    2 3  from aardwolf.protocol.fastpath.bitmap import TS_BITMAP_FLAG, TS_BITMAP_DATA
    3 4   
    4 5  from aardwolf.commons.queuedata import RDPDATATYPE
    5 6  from aardwolf.commons.queuedata.constants import VIDEO_FORMAT
    6 7  from aardwolf.utils.rectconvert import rectconvert
     8 +try:
     9 + from PIL.ImageQt import ImageQt
     10 +except ImportError:
     11 + print('No Qt installed! Converting to qt will not work')
    7 12   
    8 13  class RDP_VIDEO:
    9 14   def __init__(self):
    skipped 7 lines
    17 22   self.data:bytes = None
    18 23  
    19 24   @staticmethod
    20  - def from_bitmapdata(bitmapdata:TS_BITMAP_DATA, data_format = VIDEO_FORMAT.QT5):
     25 + def from_bitmapdata(bitmapdata:TS_BITMAP_DATA, output_format = VIDEO_FORMAT.QT5):
    21 26   res = RDP_VIDEO()
    22 27   res.type = RDPDATATYPE.VIDEO
    23 28   res.x = bitmapdata.destLeft
    skipped 2 lines
    26 31   res.height = bitmapdata.destBottom - bitmapdata.destTop + 1
    27 32   res.bitsPerPixel = bitmapdata.bitsPerPixel
    28 33   res.is_compressed = TS_BITMAP_FLAG.BITMAP_COMPRESSION in bitmapdata.flags
    29  - res.data = rectconvert(res.width, res.height, res.bitsPerPixel, res.is_compressed, bitmapdata.bitmapDataStream, data_format)
    30  - return res
     34 + image_pil = rectconvert(res.width, res.height, res.bitsPerPixel, res.is_compressed, bitmapdata.bitmapDataStream)
     35 + if output_format == VIDEO_FORMAT.PIL:
     36 + image = image_pil
     37 + 
     38 + elif output_format == VIDEO_FORMAT.RAW:
     39 + image = image_pil.tobytes()
     40 + 
     41 + elif output_format == VIDEO_FORMAT.QT5:
     42 + image = ImageQt(image_pil)
     43 +
     44 + elif output_format == VIDEO_FORMAT.PNG:
     45 + img_byte_arr = io.BytesIO()
     46 + image_pil.save(img_byte_arr, format='PNG')
     47 + image = img_byte_arr.getvalue()
     48 + else:
     49 + raise ValueError('Output format of "%s" is not supported!' % output_format)
     50 + res.data = image
     51 +
     52 + return res, image_pil
    31 53   
    32 54   def __repr__(self):
    33 55   t = '==== RDP_VIDEO ====\r\n'
    skipped 9 lines
  • ■ ■ ■ ■ ■
    aardwolf/connection.py
    skipped 1 lines
    2 2  import traceback
    3 3  import asyncio
    4 4  import typing
     5 +import copy
    5 6  from typing import cast
    6 7  from collections import OrderedDict
    7 8   
    8 9  import asn1tools
     10 +from PIL import Image
    9 11  from aardwolf import logger
    10  -from aardwolf.commons.queuedata.constants import MOUSEBUTTON
     12 +from aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT
     13 +from aardwolf.commons.target import RDPTarget
    11 14  from aardwolf.network.selector import NetworkSelector
    12 15  from aardwolf.commons.credential import RDPCredentialsSecretType
    13 16  from aardwolf.commons.cryptolayer import RDPCryptoLayer
    skipped 29 lines
    43 46  from aardwolf.commons.queuedata import *
    44 47  from aardwolf.commons.authbuilder import AuthenticatorBuilder
    45 48  from aardwolf.channels import Channel
     49 +from aardwolf.commons.iosettings import RDPIOSettings
    46 50   
    47 51   
    48 52  class RDPConnection:
    49  - def __init__(self, target, credentials, iosettings):
     53 + def __init__(self, target:RDPTarget, credentials, iosettings:RDPIOSettings):
    50 54   self.target = target
    51 55   self.credentials = credentials
    52 56   self.authapi = None
    skipped 36 lines
    89 93   self.client_x224_supported_protocols = self.iosettings.supported_protocols
    90 94   self.cryptolayer:RDPCryptoLayer = None
    91 95   self.__fastpath_in_queue = None
     96 + self.__desktop_buffer = None
     97 + self.desktop_buffer_has_data = False
    92 98   
    93 99   self.__vk_to_sc = {
    94 100   'VK_BACK' : 14,
    skipped 217 lines
    312 318   
    313 319   self.__external_reader_task = asyncio.create_task(self.__external_reader())
    314 320   logger.debug('RDP connection sequence done')
     321 + self.__desktop_buffer = Image.new(mode="RGBA", size=(self.iosettings.video_width, self.iosettings.video_height))
    315 322   return True, None
    316 323   except Exception as e:
    317 324   self.disconnected_evt.set()
    skipped 707 lines
    1025 1032   print('WARNING! FRAGMENTATION IS NOT IMPLEMENTED! %s' % fpdu.fpOutputUpdates.fragmentation)
    1026 1033   if fpdu.fpOutputUpdates.updateCode == FASTPATH_UPDATETYPE.BITMAP:
    1027 1034   for bitmapdata in fpdu.fpOutputUpdates.update.rectangles:
    1028  - await self.ext_out_queue.put(RDP_VIDEO.from_bitmapdata(bitmapdata, self.iosettings.video_out_format))
     1035 + self.desktop_buffer_has_data = True
     1036 + res, image = RDP_VIDEO.from_bitmapdata(bitmapdata, self.iosettings.video_out_format)
     1037 + self.__desktop_buffer.paste(image, [res.x, res.y, res.x+res.width, res.y+res.height])
     1038 + await self.ext_out_queue.put(res)
    1029 1039   #else:
    1030 1040   # #print(fpdu.fpOutputUpdates.updateCode)
    1031 1041   # #if fpdu.fpOutputUpdates.updateCode == FASTPATH_UPDATETYPE.CACHED:
    skipped 87 lines
    1119 1129   traceback.print_exc()
    1120 1130   return None, e
    1121 1131   
    1122  - async def send_mouse(self, button, xPos, yPos, is_pressed):
     1132 + async def send_mouse(self, button:MOUSEBUTTON, xPos:int, yPos:int, is_pressed:bool):
    1123 1133   try:
    1124 1134   if xPos < 0 or yPos < 0:
    1125 1135   return True, None
    skipped 32 lines
    1158 1168   
    1159 1169  
    1160 1170   await self.handle_out_data(cli_input, sec_hdr, data_hdr, None, self.__joined_channels['MCS'].channel_id, False)
     1171 + except Exception as e:
     1172 + traceback.print_exc()
     1173 + return None, e
     1174 + 
     1175 + def get_desktop_buffer(self, encoding:VIDEO_FORMAT = VIDEO_FORMAT.PIL):
     1176 + """Makes a copy of the current desktop buffer, converts it and returns the object"""
     1177 + try:
     1178 + image = copy.deepcopy(self.__desktop_buffer)
     1179 + if encoding == VIDEO_FORMAT.PIL:
     1180 + return image
     1181 + elif encoding == VIDEO_FORMAT.RAW:
     1182 + return image.tobytes()
     1183 + elif encoding == VIDEO_FORMAT.QT5:
     1184 + from PIL.ImageQt import ImageQt
     1185 + return ImageQt(image)
     1186 + elif encoding == VIDEO_FORMAT.PNG:
     1187 + img_byte_arr = io.BytesIO()
     1188 + image.save(img_byte_arr, format='PNG')
     1189 + return img_byte_arr.getvalue()
     1190 + else:
     1191 + raise ValueError('Output format of "%s" is not supported!' % encoding)
    1161 1192   except Exception as e:
    1162 1193   traceback.print_exc()
    1163 1194   return None, e
    skipped 125 lines
  • ■ ■ ■ ■
    aardwolf/examples/aardpclient.py
    skipped 379 lines
    380 380   iosettings.video_height = height
    381 381   iosettings.video_bpp_min = 15 #servers dont support 8 any more :/
    382 382   iosettings.video_bpp_max = args.bpp
    383  - iosettings.video_out_format = VIDEO_FORMAT.QT5
     383 + iosettings.video_out_format = VIDEO_FORMAT.PIL
    384 384   iosettings.ducky_file = args.ducky
    385 385  
    386 386   settings = RDPClientConsoleSettings(args.url, iosettings)
    skipped 12 lines
  • ■ ■ ■ ■ ■ ■
    aardwolf/examples/aardpscreenshot.py
    skipped 9 lines
    10 10  from aardwolf.examples.scancommons.internal import *
    11 11  from aardwolf.examples.scancommons.utils import *
    12 12  from aardwolf.commons.queuedata import RDPDATATYPE
    13  -from aardwolf.commons.queuedata.constants import VIDEO_FORMAT
     13 +from aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT
    14 14  from PIL import Image
    15 15  from tqdm import tqdm
    16 16   
    skipped 45 lines
    62 62   self.__total_errors = 0
    63 63   
    64 64   async def __executor(self, tid, target):
    65  - async def get_image(buffer:Image, ext_out_queue):
    66  - try:
    67  - while True:
    68  - data = await ext_out_queue.get()
    69  - if data is None:
    70  - return None, None
    71  - if data.type == RDPDATATYPE.VIDEO:
    72  - buffer.paste(data.data, (data.x, data.y))
    73  -
    74  - return None, None
    75  - 
    76  - except Exception as e:
    77  - print(e)
    78  - return None, e
    79  -
    80 65   connection = None
    81 66   try:
    82  - async with self.rdp_mgr.create_connection_newtarget(target, self.iosettings) as connection:
    83  - _, err = await connection.connect()
    84  - if err is not None:
    85  - raise err
     67 + connection = self.rdp_mgr.create_connection_newtarget(target, self.iosettings)
     68 + _, err = await connection.connect()
     69 + if err is not None:
     70 + raise err
    86 71  
    87  - buffer = Image.new('RGBA', (self.iosettings.video_width, self.iosettings.video_height))
     72 + await asyncio.sleep(self.screentime)
    88 73   
    89  - try:
    90  - await asyncio.wait_for(get_image(buffer, connection.ext_out_queue), self.screentime)
    91  - except Exception as e:
    92  - pass
     74 + except asyncio.CancelledError:
     75 + return
     76 + except Exception as e:
     77 + traceback.print_exc()
     78 + await self.res_q.put(EnumResult(tid, target, None, error = e, status = EnumResultStatus.ERROR))
     79 + finally:
     80 + if connection is not None and connection.desktop_buffer_has_data is True:
     81 + buffer = connection.get_desktop_buffer(VIDEO_FORMAT.PIL)
    93 82  
    94 83   if self.ext_result_q is None:
    95 84   filename = 'screen_%s_%s.png' % (target, tid)
    skipped 2 lines
    98 87   else:
    99 88   await self.res_q.put(EnumResult(tid, target, buffer, status = EnumResultStatus.RESULT))
    100 89   
    101  - except asyncio.CancelledError:
    102  - return
    103  - except Exception as e:
    104  - #traceback.print_exc()
    105  - await self.res_q.put(EnumResult(tid, target, None, error = e, status = EnumResultStatus.ERROR))
    106  - finally:
    107 90   await self.res_q.put(EnumResult(tid, target, None, status = EnumResultStatus.FINISHED))
    108 91  
    109 92   
    skipped 167 lines
    277 260   import argparse
    278 261   import sys
    279 262   
    280  - parser = argparse.ArgumentParser(description='RDP Screen grabber', formatter_class=argparse.RawDescriptionHelpFormatter)
     263 + parser = argparse.ArgumentParser(description='RDP/VNC Screen grabber', formatter_class=argparse.RawDescriptionHelpFormatter)
    281 264   parser.add_argument('-v', '--verbose', action='count', default=0)
    282 265   parser.add_argument('--screentime', type=int, default=5, help='Time to wait for desktop image')
    283 266   parser.add_argument('-w', '--worker-count', type=int, default=50, help='Parallell count')
    284 267   parser.add_argument('-o', '--out-dir', help='Output directory path.')
    285 268   parser.add_argument('--progress', action='store_true', help='Show progress bar')
    286 269   parser.add_argument('-s', '--stdin', action='store_true', help='Read targets from stdin')
     270 + parser.add_argument('--res', default = '1024x768', help='Resolution in "WIDTHxHEIGHT" format. Default: "1024x768"')
    287 271   parser.add_argument('url', help='Connection URL base, target can be set to anything. Example: "rdp+ntlm-password://TEST\\victim:[email protected]"')
    288 272   parser.add_argument('targets', nargs='*', help = 'Hostname or IP address or file with a list of targets')
    289 273   
    skipped 9 lines
    299 283   logging.basicConfig(level=logging.DEBUG)
    300 284   
    301 285   rdp_url = args.url
     286 + width, height = args.res.upper().split('X')
     287 + height = int(height)
     288 + width = int(width)
    302 289   
    303 290   iosettings = RDPIOSettings()
    304 291   iosettings.channels = []
    305  - iosettings.video_out_format = VIDEO_FORMAT.PIL
     292 + iosettings.video_width = width
     293 + iosettings.video_height = height
     294 + iosettings.video_bpp_min = 15 #servers dont support 8 any more :/
     295 + iosettings.video_bpp_max = 32
     296 + iosettings.video_out_format = VIDEO_FORMAT.QT5
    306 297   iosettings.clipboard_use_pyperclip = False
    307 298   
    308 299   enumerator = RDPScreenGrabberScanner(
    skipped 33 lines
  • ■ ■ ■ ■ ■
    aardwolf/utils/rectconvert.py
    skipped 1 lines
    2 2  import io
    3 3  import rle
    4 4  from PIL import Image
    5  -try:
    6  - from PIL.ImageQt import ImageQt
    7  -except ImportError:
    8  - print('No Qt installed! Converting to qt will not work')
    9 5   
    10 6  from aardwolf.commons.queuedata.constants import VIDEO_FORMAT
    11 7   
    skipped 4 lines
    16 12   32: 4,
    17 13  }
    18 14   
    19  -def rectconvert(width, height, bitsPerPixel, isCompress, data, output_format = VIDEO_FORMAT.PIL):
     15 +def rectconvert(width, height, bitsPerPixel, isCompress, data):
    20 16   if bitsPerPixel not in bpp_2_bytes:
    21 17   raise ValueError("bitsPerPixel value of %s is not supported!" % bitsPerPixel)
    22 18   image = bytes(width * height * 4)
    23 19   rle.bitmap_decompress(image, width, height, data, bitsPerPixel, bpp_2_bytes[bitsPerPixel], int(isCompress))
    24  - 
    25  - if output_format == VIDEO_FORMAT.RAW:
    26  - return image
    27  -
    28  - image = Image.frombytes('RGBA', [width, height], image)
    29  - 
    30  - if output_format == VIDEO_FORMAT.PIL:
    31  - return image
    32  -
    33  - if output_format == VIDEO_FORMAT.QT5:
    34  - image = ImageQt(image)
    35  -
    36  - elif output_format == VIDEO_FORMAT.PNG:
    37  - img_byte_arr = io.BytesIO()
    38  - image.save(img_byte_arr, format=output_format.upper())
    39  - image = img_byte_arr.getvalue()
    40  - else:
    41  - raise ValueError('Output format of "%s" is not supported!' % output_format)
    42  - return image
     20 + return Image.frombytes('RGBA', [width, height], image)
  • ■ ■ ■ ■ ■ ■
    aardwolf/utils/rle/rle.c
    skipped 21 lines
    22 22  The code now ALWAYS returns RGB32 format regardless of input bpp. Makes life easier on upper levels.
    23 23  The code also takes non-compressed input (and a flag that indicates wether it's compressed or not.)
    24 24  Non-compressed data will be only converted to RGB32.
     25 +There are some out-of-bounds write possibilities in the RRE decoder.
     26 + 
    25 27  Mod author: Tamas Jos @skelsec
    26 28  */
    27 29   
    skipped 1128 lines
  • ■ ■ ■ ■ ■
    aardwolf/vncconnection.py
    skipped 6 lines
    7 7  # TODO: Add mouse scroll functionality (QT5 client needs to be modified for that)
    8 8   
    9 9  import io
     10 +import copy
    10 11  import asyncio
    11 12  import traceback
    12 13  from struct import pack, unpack
    skipped 11 lines
    24 25  from aardwolf.keyboard import VK_MODIFIERS
    25 26  from aardwolf.keyboard.layoutmanager import KeyboardLayoutManager
    26 27  from aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT
     28 +from aardwolf.commons.iosettings import RDPIOSettings
    27 29   
    28 30  from PIL import Image
    29  -from PIL.ImageQt import ImageQt
     31 +try:
     32 + from PIL.ImageQt import ImageQt
     33 +except ImportError:
     34 + print('No Qt installed! Converting to qt will not work')
     35 + 
    30 36  import rle
    31 37   
    32 38  # https://datatracker.ietf.org/doc/html/rfc6143
    skipped 10 lines
    43 49  ZRLE_ENCODING = 16
    44 50   
    45 51  class VNCConnection:
    46  - def __init__(self, target, credentials, iosettings):
     52 + def __init__(self, target, credentials, iosettings:RDPIOSettings):
    47 53   self.target = target
    48 54   self.credentials = credentials
    49 55   self.authapi = None
    skipped 34 lines
    84 90   self.width = None
    85 91   self.height = None
    86 92   self.__desktop_buffer = None
     93 + self.desktop_buffer_has_data = False
    87 94   
    88 95   self.__vk_to_vnckey = {
    89 96   'VK_BACK' : KEY_BackSpace,
    skipped 136 lines
    226 233   raise err
    227 234   logger.debug('Client init OK')
    228 235   
     236 + logger.debug('Starting internal comm channels')
    229 237   self.__reader_loop_task = asyncio.create_task(self.__reader_loop())
    230 238   self.__external_reader_task = asyncio.create_task(self.__external_reader())
     239 + logger.debug('Sending cliboard ready signal') #to emulate RDP clipboard functionality
     240 + msg = RDP_CLIPBOARD_READY()
     241 + await self.ext_out_queue.put(msg)
    231 242  
    232 243   return True, None
    233 244   except Exception as e:
    skipped 40 lines
    274 285   for sectype in sec_types:
    275 286   self.server_supp_security_types.append(sectype)
    276 287   
    277  - if self.__selected_security_type == 2:
     288 + if self.__selected_security_type == 0:
     289 + logger.debug('Invalid authentication type!!!')
     290 + raise Exception('Invalid authentication type')
     291 +
     292 + elif self.__selected_security_type == 1:
     293 + # nothing to do here
     294 + logger.debug('Selecting NULL auth type')
     295 + 
     296 +
     297 + elif self.__selected_security_type == 2:
    278 298   logger.debug('Selecting default VNC auth type')
    279 299   self.__writer.write(bytes([self.__selected_security_type]))
    280 300   challenge = await self.__reader.readexactly(16)
    skipped 70 lines
    351 371   except Exception as e:
    352 372   return None, e
    353 373  
     374 + async def send_key_virtualkey(self, vk:str, is_pressed:bool, is_extended:bool, scancode_hint:int = None):
     375 + try:
     376 + if indata.vk_code is not None:
     377 + vk_code = indata.vk_code
     378 + else:
     379 + vk_code = self.__keyboard_layout.scancode_to_vk(indata.keyCode)
     380 + print('Got VK: %s' % vk_code)
     381 + if vk_code is None:
     382 + print('Could not map SC to VK! SC: %s' % indata.keyCode)
     383 + if vk_code is not None and vk_code in self.__vk_to_vnckey:
     384 + keycode = self.__vk_to_vnckey[vk_code]
     385 + print('AAAAAAAA %s' % hex(keycode))
     386 + 
     387 + 
     388 + if vk in self.__vk_to_sc:
     389 + scancode = self.__vk_to_sc[vk]
     390 + is_extended = True
     391 + print('EXT')
     392 + else:
     393 + scancode = scancode_hint
     394 + return await self.send_key_char(scancode, is_pressed, is_extended)
     395 + except Exception as e:
     396 + traceback.print_exc()
     397 + return None, e
     398 + 
     399 + async def send_key_scancode(self, scancode, is_pressed, is_extended):
     400 + try:
     401 + keycode = self.__keyboard_layout.scancode_to_char(indata.keyCode, modifiers)
     402 + print(keycode)
     403 + if keycode is None:
     404 + print('Failed to resolv key! SC: %s VK: %s' % (indata.keyCode, vk_code))
     405 + #continue
     406 + elif keycode is not None and len(keycode) == 1:
     407 + keycode = ord(keycode)
     408 + print('Keycode %s resolved to: %s' % (indata.keyCode , repr(keycode)))
     409 + elif keycode is not None and len(keycode) > 1:
     410 + print('LARGE! Keycode %s resolved to: %s' % (indata.keyCode , repr(keycode)))
     411 + #continue
     412 + else:
     413 + print('This key is too special! Can\'t resolve it! SC: %s VK: %s' % (indata.keyCode, vk_code))
     414 + #continue
     415 + 
     416 + return True, None
     417 + except Exception as e:
     418 + traceback.print_exc()
     419 + return None, e
     420 + 
     421 + async def send_key_char(self, char, is_pressed):
     422 + try:
     423 + msg = pack("!BBxxI", 4, int(is_pressed), char)
     424 + self.__writer.write(msg)
     425 + return True, None
     426 + except Exception as e:
     427 + traceback.print_exc()
     428 + return None, e
     429 + 
     430 + async def send_mouse(self, button:MOUSEBUTTON, xPos:int, yPos:int, is_pressed:bool):
     431 + try:
     432 + if xPos < 0 or yPos < 0:
     433 + return True, None
     434 + 
     435 + button =0
     436 + if button == MOUSEBUTTON.MOUSEBUTTON_LEFT:
     437 + button = 1
     438 + elif button == MOUSEBUTTON.MOUSEBUTTON_MIDDLE:
     439 + button = 2
     440 + elif button == MOUSEBUTTON.MOUSEBUTTON_RIGHT:
     441 + button = 3
     442 +
     443 + buttonmask = 0
     444 + if is_pressed is True:
     445 + if button == 1: buttonmask &= ~1
     446 + if button == 2: buttonmask &= ~2
     447 + if button == 3: buttonmask &= ~4
     448 + if button == 4: buttonmask &= ~8
     449 + if button == 5: buttonmask &= ~16
     450 + else:
     451 + if button == 1: buttonmask |= 1
     452 + if button == 2: buttonmask |= 2
     453 + if button == 3: buttonmask |= 4
     454 + if button == 4: buttonmask |= 8
     455 + if button == 5: buttonmask |= 16
     456 +
     457 + msg = pack("!BBHH", 5, buttonmask, xPos, yPos)
     458 + self.__writer.write(msg)
     459 + return True, None
     460 + except Exception as e:
     461 + traceback.print_exc()
     462 + return None, e
     463 + 
     464 + def get_desktop_buffer(self, encoding:VIDEO_FORMAT = VIDEO_FORMAT.PIL):
     465 + """Makes a copy of the current desktop buffer, converts it and returns the object"""
     466 + try:
     467 + image = self.__desktop_buffer.copy()
     468 + if encoding == VIDEO_FORMAT.PIL:
     469 + return image
     470 + elif encoding == VIDEO_FORMAT.RAW:
     471 + return image.tobytes()
     472 + elif encoding == VIDEO_FORMAT.QT5:
     473 + return ImageQt(image)
     474 + elif encoding == VIDEO_FORMAT.PNG:
     475 + img_byte_arr = io.BytesIO()
     476 + image.save(img_byte_arr, format='PNG')
     477 + return img_byte_arr.getvalue()
     478 + else:
     479 + raise ValueError('Output format of "%s" is not supported!' % encoding)
     480 + except Exception as e:
     481 + traceback.print_exc()
     482 + return None, e
     483 + 
    354 484   async def __external_reader(self):
    355 485   # This coroutine handles keyboard/mouse/clipboard etc input from the user
    356 486   # It wraps the data in it's appropriate format then dispatches it to the server
    skipped 72 lines
    429 559   else:
    430 560   # I hope you know what you're doing here...
    431 561   keycode = int.from_bytes(bytes.fromhex(keycode), byteorder = 'big', signed = False)
    432  - msg = pack("!BBxxI", 4, int(indata.is_pressed), indata.char)
    433  - self.__writer.write(msg)
     562 + await self.send_key_char(keycode, indata.is_pressed)
    434 563  
    435 564   elif indata.type == RDPDATATYPE.MOUSE:
    436 565   #PointerEvent
    skipped 1 lines
    438 567   if indata.xPos < 0 or indata.yPos < 0:
    439 568   continue
    440 569  
    441  - button =0
    442  - if indata.button == MOUSEBUTTON.MOUSEBUTTON_LEFT:
    443  - button = 1
    444  - elif indata.button == MOUSEBUTTON.MOUSEBUTTON_MIDDLE:
    445  - button = 2
    446  - elif indata.button == MOUSEBUTTON.MOUSEBUTTON_RIGHT:
    447  - button = 3
    448  -
    449  - buttonmask = 0
    450  - if indata.is_pressed is True:
    451  - if button == 1: buttonmask &= ~1
    452  - if button == 2: buttonmask &= ~2
    453  - if button == 3: buttonmask &= ~4
    454  - if button == 4: buttonmask &= ~8
    455  - if button == 5: buttonmask &= ~16
    456  - else:
    457  - if button == 1: buttonmask |= 1
    458  - if button == 2: buttonmask |= 2
    459  - if button == 3: buttonmask |= 4
    460  - if button == 4: buttonmask |= 8
    461  - if button == 5: buttonmask |= 16
    462  - 
    463  -
    464  - #print('sending mouse!')
    465  - msg = pack("!BBHH", 5, buttonmask, indata.xPos, indata.yPos)
    466  - self.__writer.write(msg)
     570 + await self.send_mouse(indata.button, indata.xPos, indata.yPos, indata.is_pressed)
    467 571   
    468 572   elif indata.type == RDPDATATYPE.CLIPBOARD_DATA_TXT:
    469 573   try:
    skipped 30 lines
    500 604   async def __send_rect(self, x, y, width, height, image:Image):
    501 605   try:
    502 606   #updating desktop buffer to have a way to copy rectangles later
     607 + self.desktop_buffer_has_data = True
    503 608   if self.width == width and self.height == height:
    504 609   self.__desktop_buffer = image
    505 610   else:
    skipped 22 lines
    528 633   rect.data = image
    529 634   await self.ext_out_queue.put(rect)
    530 635   
    531  - 
    532 636   except Exception as e:
    533 637   await self.terminate()
    534 638   return None, e
    535  - 
    536  -
    537 639   
    538 640   async def __reader_loop(self):
    539 641   try:
    skipped 97 lines
    637 739  
    638 740   elif msgtype == 3:
    639 741   # Server side has updated the clipboard
     742 +
     743 + # Signaling clipboard data (to simulate RDP functionality)
     744 + msg = RDP_CLIPBOARD_NEW_DATA_AVAILABLE()
     745 + await self.ext_out_queue.put(msg)
     746 + 
     747 + # processing clipboard data
    640 748   hdr = await self.__reader.readexactly(7)
    641 749   (_,_,_, cliplen ) = unpack("!BBBI", hdr)
    642 750   cliptext = await self.__reader.readexactly(cliplen)
    643  - cliptext = cliptext.decode('latin-1')
     751 + cliptext = cliptext.decode('latin-1') #latin-1 is per RFC
    644 752   logger.info('Got clipboard test: %s' % repr(cliptext))
    645 753   if self.__use_pyperclip is True:
    646 754   import pyperclip
    skipped 80 lines
  • ■ ■ ■ ■
    dtest3.txt
    1  -STRING the quick brown fox jumps over the lazy dog
     1 +STRING ',./;'\[]=-`<<>?:"|{}+_)(*&^%$#@!~>
    2 2   
Please wait...
Page is in error, reload to recover