# Módulo MDM para o Cliente NoIdle # Executa políticas e comandos remotos import subprocess import time import json import requests import winreg import os from datetime import datetime class MDMExecutor: def __init__(self, api_url, device_id): self.api_url = api_url self.device_id = device_id self.handlers = { 'windows_update': self.execute_windows_update, 'software_install': self.execute_software_install, 'software_uninstall': self.execute_software_uninstall, 'powershell_script': self.execute_powershell_script, 'registry': self.execute_registry, 'reboot': self.execute_reboot, 'cleanup': self.execute_cleanup, 'firewall': self.execute_firewall } def poll_commands(self): """Busca comandos pendentes da API""" try: response = requests.post( f"{self.api_url}/mdm/commands/poll", json={'device_id': self.device_id}, timeout=10 ) if response.status_code == 200: data = response.json() if data.get('success'): return data.get('commands', []) return [] except Exception as e: print(f"❌ Erro ao buscar comandos: {e}") return [] def report_start(self, command_id): """Reporta início de execução""" try: requests.post( f"{self.api_url}/mdm/commands/{command_id}/start", timeout=5 ) except Exception as e: print(f"⚠️ Erro ao reportar início: {e}") def report_result(self, command_id, status, result=None, error_message=None, duration=0): """Reporta resultado da execução""" try: response = requests.post( f"{self.api_url}/mdm/commands/{command_id}/result", json={ 'status': status, 'result': result, 'error_message': error_message, 'duration_seconds': duration }, timeout=10 ) return response.status_code == 200 except Exception as e: print(f"❌ Erro ao reportar resultado: {e}") return False def execute_command(self, command): """Executa um comando MDM""" command_id = command['command_id'] command_type = command['command_type'] command_data = command['command_data'] print(f"🔧 Executando: {command.get('policy_name', command_type)}") # Reportar início self.report_start(command_id) start_time = time.time() try: # Executar comando baseado no tipo if command_type in self.handlers: result = self.handlers[command_type](command_data) duration = int(time.time() - start_time) # Reportar sucesso self.report_result(command_id, 'success', result, None, duration) print(f"✅ Comando executado com sucesso em {duration}s") return True else: error = f"Tipo de comando não suportado: {command_type}" self.report_result(command_id, 'failed', None, error, 0) print(f"❌ {error}") return False except Exception as e: duration = int(time.time() - start_time) error_message = str(e) self.report_result(command_id, 'failed', None, error_message, duration) print(f"❌ Erro ao executar comando: {e}") return False def execute_windows_update(self, config): """Força Windows Update""" print("🔄 Forçando Windows Update...") try: # Script PowerShell para forçar update ps_script = """ $UpdateSession = New-Object -ComObject Microsoft.Update.Session $UpdateSearcher = $UpdateSession.CreateUpdateSearcher() Write-Host "Procurando atualizações..." $SearchResult = $UpdateSearcher.Search("IsInstalled=0") if ($SearchResult.Updates.Count -eq 0) { Write-Host "Nenhuma atualização disponível" exit 0 } Write-Host "Encontradas $($SearchResult.Updates.Count) atualizações" $UpdatesToDownload = New-Object -ComObject Microsoft.Update.UpdateColl foreach ($Update in $SearchResult.Updates) { $UpdatesToDownload.Add($Update) | Out-Null } Write-Host "Baixando atualizações..." $Downloader = $UpdateSession.CreateUpdateDownloader() $Downloader.Updates = $UpdatesToDownload $DownloadResult = $Downloader.Download() Write-Host "Instalando atualizações..." $UpdatesToInstall = New-Object -ComObject Microsoft.Update.UpdateColl foreach ($Update in $SearchResult.Updates) { if ($Update.IsDownloaded) { $UpdatesToInstall.Add($Update) | Out-Null } } $Installer = $UpdateSession.CreateUpdateInstaller() $Installer.Updates = $UpdatesToInstall $InstallResult = $Installer.Install() Write-Host "Resultado: $($InstallResult.ResultCode)" if ($InstallResult.RebootRequired) { Write-Host "REBOOT_REQUIRED" } """ result = subprocess.run( ['powershell', '-ExecutionPolicy', 'Bypass', '-Command', ps_script], capture_output=True, text=True, timeout=1800 # 30 minutos ) output = result.stdout reboot_required = "REBOOT_REQUIRED" in output # Se reboot for necessário e configurado, agendar if reboot_required and config.get('reboot_if_required'): delay = config.get('reboot_delay_minutes', 30) self.schedule_reboot(delay) return { 'updates_found': 'atualizações' in output.lower(), 'reboot_required': reboot_required, 'output': output[:500] # Limitar tamanho } except subprocess.TimeoutExpired: raise Exception("Timeout ao executar Windows Update") except Exception as e: raise Exception(f"Erro no Windows Update: {str(e)}") def execute_software_install(self, config): """Instala software""" software_name = config.get('software_name', 'Unknown') download_url = config.get('download_url') install_args = config.get('install_args', '/S') print(f"📦 Instalando: {software_name}") if not download_url: raise Exception("URL de download não fornecida") try: # Baixar instalador import tempfile temp_dir = tempfile.gettempdir() filename = download_url.split('/')[-1] or 'installer.exe' installer_path = os.path.join(temp_dir, filename) print(f"⬇️ Baixando de {download_url}...") response = requests.get(download_url, timeout=300) response.raise_for_status() with open(installer_path, 'wb') as f: f.write(response.content) print(f"🔧 Instalando...") # Executar instalador result = subprocess.run( [installer_path] + install_args.split(), capture_output=True, text=True, timeout=600 ) # Limpar try: os.remove(installer_path) except: pass return { 'software': software_name, 'exit_code': result.returncode, 'success': result.returncode == 0 } except Exception as e: raise Exception(f"Erro ao instalar {software_name}: {str(e)}") def execute_software_uninstall(self, config): """Desinstala software""" software_name = config.get('software_name') silent = config.get('silent', True) print(f"🗑️ Desinstalando: {software_name}") # Usar wmic para desinstalar ps_script = f""" $software = Get-WmiObject -Class Win32_Product | Where-Object {{ $_.Name -like "*{software_name}*" }} if ($software) {{ $software.Uninstall() Write-Host "Desinstalado: $($software.Name)" }} else {{ Write-Host "Software não encontrado" }} """ result = subprocess.run( ['powershell', '-ExecutionPolicy', 'Bypass', '-Command', ps_script], capture_output=True, text=True, timeout=300 ) return { 'software': software_name, 'output': result.stdout, 'success': 'Desinstalado' in result.stdout } def execute_powershell_script(self, config): """Executa script PowerShell""" script = config.get('script', '') run_as_admin = config.get('run_as_admin', True) timeout_minutes = config.get('timeout_minutes', 30) execution_policy = config.get('execution_policy', 'Bypass') print(f"💻 Executando script PowerShell...") if not script: raise Exception("Script não fornecido") result = subprocess.run( ['powershell', '-ExecutionPolicy', execution_policy, '-Command', script], capture_output=True, text=True, timeout=timeout_minutes * 60 ) return { 'exit_code': result.returncode, 'stdout': result.stdout[:1000], # Limitar tamanho 'stderr': result.stderr[:1000], 'success': result.returncode == 0 } def execute_registry(self, config): """Modifica registro do Windows""" hive = config.get('hive', 'HKLM') key = config.get('key') value_name = config.get('value_name') value_data = config.get('value_data') value_type = config.get('value_type', 'String') print(f"📝 Modificando registro: {hive}\\{key}") # Mapear hive hive_map = { 'HKLM': winreg.HKEY_LOCAL_MACHINE, 'HKCU': winreg.HKEY_CURRENT_USER, 'HKCR': winreg.HKEY_CLASSES_ROOT, 'HKU': winreg.HKEY_USERS } # Mapear tipo type_map = { 'String': winreg.REG_SZ, 'DWORD': winreg.REG_DWORD, 'Binary': winreg.REG_BINARY, 'MultiString': winreg.REG_MULTI_SZ } try: reg_hive = hive_map.get(hive, winreg.HKEY_LOCAL_MACHINE) reg_type = type_map.get(value_type, winreg.REG_SZ) # Abrir ou criar chave reg_key = winreg.CreateKey(reg_hive, key) # Converter valor se necessário if value_type == 'DWORD': value_data = int(value_data) # Setar valor winreg.SetValueEx(reg_key, value_name, 0, reg_type, value_data) winreg.CloseKey(reg_key) return { 'hive': hive, 'key': key, 'value_name': value_name, 'success': True } except Exception as e: raise Exception(f"Erro ao modificar registro: {str(e)}") def execute_reboot(self, config): """Reinicia o sistema""" delay_minutes = config.get('delay_minutes', 5) force = config.get('force', False) message = config.get('message', 'O sistema será reiniciado') print(f"🔄 Agendando reinicialização em {delay_minutes} minutos...") delay_seconds = delay_minutes * 60 force_flag = '/f' if force else '' subprocess.run( ['shutdown', '/r', '/t', str(delay_seconds), '/c', message, force_flag], check=False ) return { 'delay_minutes': delay_minutes, 'scheduled': True } def execute_cleanup(self, config): """Limpa arquivos temporários""" print("🧹 Limpando arquivos temporários...") cleaned = { 'temp': False, 'windows_temp': False, 'browser_cache': False, 'recycle_bin': False } try: if config.get('clear_temp', True): temp_path = os.environ.get('TEMP') # Limpar %TEMP% subprocess.run( ['powershell', '-Command', f'Remove-Item -Path "{temp_path}\\*" -Recurse -Force -ErrorAction SilentlyContinue'], check=False ) cleaned['temp'] = True if config.get('clear_windows_temp', True): # Limpar C:\Windows\Temp subprocess.run( ['powershell', '-Command', 'Remove-Item -Path "C:\\Windows\\Temp\\*" -Recurse -Force -ErrorAction SilentlyContinue'], check=False ) cleaned['windows_temp'] = True if config.get('clear_recycle_bin', True): # Esvaziar lixeira subprocess.run( ['powershell', '-Command', 'Clear-RecycleBin -Force -ErrorAction SilentlyContinue'], check=False ) cleaned['recycle_bin'] = True return cleaned except Exception as e: raise Exception(f"Erro na limpeza: {str(e)}") def execute_firewall(self, config): """Configura regras de firewall""" action = config.get('action', 'add_rule') rule_name = config.get('rule_name') print(f"🛡️ Configurando firewall: {rule_name}") if action == 'add_rule': direction = config.get('direction', 'inbound') protocol = config.get('protocol', 'TCP') port = config.get('port') action_type = config.get('action_type', 'allow') ps_script = f""" New-NetFirewallRule -DisplayName "{rule_name}" ` -Direction {direction} ` -Protocol {protocol} ` -LocalPort {port} ` -Action {action_type} """ result = subprocess.run( ['powershell', '-Command', ps_script], capture_output=True, text=True ) return { 'rule_name': rule_name, 'success': result.returncode == 0, 'output': result.stdout } return {'action': action, 'success': False} def schedule_reboot(self, delay_minutes): """Agenda reinicialização""" delay_seconds = delay_minutes * 60 subprocess.run( ['shutdown', '/r', '/t', str(delay_seconds)], check=False ) print(f"⏰ Reinicialização agendada em {delay_minutes} minutos") # Função para integrar no cliente principal def run_mdm_polling(api_url, device_id, interval_seconds=60): """ Função para ser executada em thread separada no cliente principal Faz polling de comandos MDM periodicamente """ executor = MDMExecutor(api_url, device_id) while True: try: commands = executor.poll_commands() if commands: print(f"📋 Recebidos {len(commands)} comando(s) MDM") for command in commands: executor.execute_command(command) time.sleep(interval_seconds) except KeyboardInterrupt: break except Exception as e: print(f"❌ Erro no polling MDM: {e}") time.sleep(interval_seconds)