Files
NoIdle/CLIENT_MDM.py

473 lines
16 KiB
Python
Raw Normal View History

# 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)