Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.3.0</Version>
<Version>0.4.0</Version>
<Authors>Manfred Heil Junior</Authors>
<Company>MT - Manfred Tecnologia</Company>
<Product>MapNet - MT</Product>
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Página do programa: **https://mapnet.manfred.com.br**. Download da versão mais

Software livre, sob licença [GPL-3.0](LICENSE).

Não é scanner de vulnerabilidade: o programa não testa senha, não contorna autenticação e não explora vulnerabilidade. Para achar os equipamentos, ele manda ping, pedidos ARP e consultas de nome (DNS, NetBIOS e mDNS) aos endereços da sub-rede escolhida. Com a opção **Verificar portas** ligada, ele também abre e fecha uma conexão TCP em cada porta da lista, nos equipamentos que encontrou, sem enviar dados.
Não é scanner de vulnerabilidade: o programa não testa senha, não contorna autenticação e não explora vulnerabilidade. Para achar os equipamentos, ele manda ping, pedidos ARP e consultas de nome (DNS, NetBIOS e mDNS) aos endereços da sub-rede escolhida. Com a opção **Verificar portas** ligada, ele também abre e fecha uma conexão TCP em cada porta da lista, nos equipamentos que encontrou, sem enviar dados. Com **Identificar serviços**, que vem marcado junto com as portas, ele pede a página inicial dos serviços web que estiverem abertos (só um GET, sem senha), lê o certificado HTTPS e a primeira linha de SSH, FTP e SMTP, e faz uma busca UPnP na rede local.

**Uso autorizado.** O MapNet - MT serve para inventariar equipamentos em redes nas quais você tem autorização para fazer a verificação. Confira o intervalo de endereços antes de iniciar e respeite as regras da rede e dos dispositivos examinados.

Expand All @@ -25,7 +25,7 @@ As regras do projeto estão no [`AGENTS.md`](AGENTS.md) e o desenho em [`docs/su
| 5 | Ações de manutenção com elevação sob demanda, na aba Manutenção do console (sai como v0.2.4) | `manutencao` | [#18](https://github.com/manfredjr/mapnet/pull/18) | Publicada na v0.2.5 |
| 6 | Painel de detalhe do host, com ping, tracert, navegador, área de trabalho remota e pasta compartilhada (sai como v0.2.5) | `detalhe-host` | [#19](https://github.com/manfredjr/mapnet/pull/19) | Publicada na v0.2.5 |
| 7 | Portas TCP comuns abertas, com lista configurável, desligadas por padrão e com confirmação por rede (sai como v0.3.0) | `portas` | Plano em [#26](https://github.com/manfredjr/mapnet/pull/26), código em [#31](https://github.com/manfredjr/mapnet/pull/31) | Publicada na v0.3.0 |
| 8 | Identificação leve dos serviços nas portas abertas: título da página, certificado HTTPS, banner SSH, FTP e SMTP, e UPnP (sai como v0.4.0) | `identificacao` | Plano em [#32](https://github.com/manfredjr/mapnet/pull/32) | Plano em revisão |
| 8 | Identificação leve dos serviços nas portas abertas: título da página, certificado HTTPS, banner SSH, FTP e SMTP, e UPnP (sai como v0.4.0) | `identificacao-servicos` | Plano em [#32](https://github.com/manfredjr/mapnet/pull/32), código em [#34](https://github.com/manfredjr/mapnet/pull/34) | Em revisão |
| 9 e 10 | Classificação por tipo de equipamento e relatório também em XML e CSV (até a v1.0.0) | a definir | - | A fazer |

A versão 0.2 segue o desenho em [`docs/superpowers/specs/2026-09-25-mapnet-0.2-painel-do-tecnico-design.md`](docs/superpowers/specs/2026-09-25-mapnet-0.2-painel-do-tecnico-design.md).
Expand All @@ -41,7 +41,7 @@ O programa é um arquivo só, `mapnet.exe`. Não precisa instalar nada no comput
1. Abra o `mapnet.exe`.
2. Na faixa verde do topo, escolha a interface de rede. A primeira da lista é a que tem gateway, quase sempre a certa. A coluna **Minha máquina** mostra o computador (nome, grupo de trabalho ou domínio, usuário), a placa (tipo, MAC, velocidade, MTU), os endereços (IPv4, IPv6, gateway, DNS, sufixo e a sub-rede que vai ser varrida) e o DHCP com a validade da concessão. No Wi-Fi, mostra também a rede, a banda, o canal e o sinal.
3. O botão **Consultar IP público**, na faixa do topo, pergunta o IP ao serviço `1.1.1.1`, do Cloudflare, e só roda com o clique. Se o relatório for gravado depois da consulta, o IP público vai junto.
4. Para saber também as portas abertas de cada equipamento, marque **Verificar portas** acima da tabela. A **Lista** vem com `padrão`, as 24 portas comuns (a dica do campo mostra quais são), ou aceita até 100 portas separadas por vírgula, como `80,443,9100`. Os aparelhos com MAC aleatório, quase sempre celulares e notebooks pessoais, ficam de fora, a não ser que você marque **Incluir aparelhos com MAC aleatório**. Na primeira varredura com portas em cada rede, o programa diz o que vai enviar e pede confirmação. Use só em rede que você tem autorização para verificar.
4. Para saber também as portas abertas de cada equipamento, marque **Verificar portas** acima da tabela. A **Lista** vem com `padrão`, as 24 portas comuns (a dica do campo mostra quais são), ou aceita até 100 portas separadas por vírgula, como `80,443,9100`. Os aparelhos com MAC aleatório, quase sempre celulares e notebooks pessoais, ficam de fora, a não ser que você marque **Incluir aparelhos com MAC aleatório**. **Identificar serviços** vem marcado e preenche a coluna **Serviço** com o que o equipamento diz de si: modelo pelo UPnP, título da página, certificado ou banner. Na primeira varredura com portas em cada rede, o programa diz o que vai enviar e pede confirmação. Use só em rede que você tem autorização para verificar.
5. Clique em **Iniciar varredura**. Clicar numa linha da tabela abre, ao lado, o detalhe do host: MAC, fabricante, os nomes pelo DNS reverso, NetBIOS e mDNS, o grupo de trabalho, o ping com o TTL, uma pista do sistema pelo TTL e as portas abertas, com o serviço de cada uma. Os botões do detalhe fazem ping contínuo e tracert no console, abrem http e https no navegador (na 8080, 8000 ou 8443 quando só ela está aberta, e desligados quando as portas web foram verificadas e estão fechadas), a área de trabalho remota e a pasta compartilhada, e copiam os dados. Um novo clique na mesma linha, a tecla Esc ou o botão **Fechar** fecham o detalhe. A tabela vai se enchendo enquanto os hosts respondem, e o mesmo botão vira **Cancelar**. O campo **Filtrar** procura por IP, nome, MAC, fabricante, observação ou número de porta aberta (`9100` lista as impressoras), e cada coluna ordena com um clique no título.
6. O console embaixo registra o andamento e os avisos, e a barra de estado mostra o resumo. O console tem também uma aba para cada ferramenta: Ping (normal ou contínuo), Tracert, DNS com escolha do servidor, ARP, Conexões e Rotas. Os campos de host e de servidor DNS já vêm com o gateway e o DNS da interface. Enquanto a ferramenta roda, **Executar** vira **Parar**. **Copiar** e **Limpar** valem para a aba aberta.
7. No fim, clique em **Salvar relatório** e escolha onde gravar o arquivo HTML. A janela abre em `Documentos\MapNet - MT` com um nome pronto, e da segunda vez abre na pasta usada por último. O programa não grava sozinho: fechar a janela com a varredura sem salvar pergunta antes. O botão **Abrir** abre o relatório salvo no navegador ou a pasta dele.
Expand Down Expand Up @@ -69,6 +69,7 @@ mapnet --ajuda
| `--sem-arp` | Descobre hosts só pelo ping |
| `--portas [lista]` | Verifica as portas TCP dos hosts encontrados, só abrindo e fechando a conexão. Sem lista, usa as 24 portas comuns; com lista, até 100 portas, como `22,80,443`. Desligado sem esta opção |
| `--incluir-mac-aleatorio` | Com `--portas`, inclui os aparelhos com MAC aleatório, que ficam de fora por padrão |
| `--sem-identificar` | Com `--portas`, não identifica os serviços das portas abertas |

O `.exe` é de janela, então o Prompt de Comando devolve o cursor antes de o programa terminar. Para esperar o fim, use `start /wait mapnet --varrer` no Prompt de Comando ou termine a linha com `| Out-Host` no PowerShell. Redirecionar para arquivo (`> saida.txt`) funciona direto, em UTF-8.

Expand Down Expand Up @@ -110,7 +111,8 @@ Se o valor bater com o do `.txt`, o executável é exatamente o que foi publicad
- **Nome:** DNS reverso, NetBIOS (o mesmo do `nbtstat -A`, que traz também o grupo de trabalho ou domínio) e mDNS (nomes `.local` de celulares, Macs, impressoras e aparelhos Linux).
- **Relatório HTML:** arquivo único, com resumo da varredura, cartões de contagem, tabela filtrável e ordenável, detalhe de cada host ao clicar na linha e a lista de fabricantes.

- **Portas (desde a v0.3.0, desligadas por padrão):** para cada host encontrado, abre e fecha a conexão TCP em cada porta da lista, sem enviar nenhum dado, com até 128 conexões ao mesmo tempo, 8 por host e 800 ms de espera por porta. Porta que aceita a conexão aparece como aberta. Este computador e os aparelhos com MAC aleatório ficam de fora, e o motivo aparece no detalhe. O relatório ganha a coluna Portas e diz quais portas foram verificadas. A identificação do serviço (título da página, banner, certificado) fica para a v0.4.0.
- **Portas (desde a v0.3.0, desligadas por padrão):** para cada host encontrado, abre e fecha a conexão TCP em cada porta da lista, sem enviar nenhum dado, com até 128 conexões ao mesmo tempo, 8 por host e 800 ms de espera por porta. Porta que aceita a conexão aparece como aberta. Este computador e os aparelhos com MAC aleatório ficam de fora, e o motivo aparece no detalhe. O relatório ganha a coluna Portas e diz quais portas foram verificadas.
- **Serviços (desde a v0.4.0, com as portas):** só nas portas abertas. Nas web (80, 8000, 8080, 443, 8443), um `GET /` para ler o título e o servidor, sem seguir redirecionamento, e o certificado HTTPS, aceitando autoassinado e vencido. Em SSH, FTP e SMTP, só lê a primeira linha que o servidor manda. O UPnP é um `M-SEARCH` na rede local, e a descrição do equipamento é lida dele mesmo. Telnet fica de fora. No máximo 16 ao mesmo tempo, 3 s por serviço e 64 KB lidos por resposta.

Sub-rede maior que /22 (1022 endereços) é varrida só no bloco /22 em volta do IP do computador, com aviso no relatório. A faixa manual fica para uma próxima versão.

Expand Down
8 changes: 8 additions & 0 deletions docs/superpowers/pendencias.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ Fechado em parte na fatia 3 (0.2.2). No tamanho padrão da janela, IP, Origem, M
| Portas numa rede de verdade | A etapa foi testada com sonda simulada e no modo de demonstração. Não rodou numa rede de verdade no teste do agente | O Manfred varrer a rede da MT com as portas ligadas e conferir, por exemplo, a 80 ou 443 no roteador, a 9100 numa impressora e a 445 num Windows com compartilhamento |
| Confirmação vale para a sessão | A pergunta aparece uma vez por sub-rede enquanto o programa está aberto. Fechar e abrir de novo pergunta outra vez | Guardar a confirmação, se o Manfred achar a pergunta repetitiva no uso |

## Fatia 8: identificação de serviços

| Item | Motivo | O que fecha |
|---|---|---|
| Linha de comando com a identificação ligada por padrão | O plano previa `--identificar` para ligar. Com a decisão de 26/09/2026 de deixar **Identificar serviços** marcado na janela, a linha de comando segue o mesmo padrão: `--portas` já identifica e `--sem-identificar` desliga | O Manfred confirmar ou pedir `--identificar` |
| HTTP pedido pelo IP | O `GET /` vai com o IP no cabeçalho `Host`. Servidor que hospeda vários sites por nome pode mostrar uma página genérica | Usar o nome do DNS reverso no `Host`, se fizer falta no campo |
| Janela da identificação vista só no modo de demonstração | A identificação de verdade rodou na rede do Manfred pela linha de comando, com roteador, câmera, TV e Windows respondendo. A janela foi conferida com dados de exemplo | O Manfred varrer pela janela com as portas ligadas |

## Página de privacidade

| Item | Motivo | O que fecha |
Expand Down
4 changes: 2 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ <h2>O que ele faz</h2>
<div><h3>Todos os equipamentos da sub-rede</h3><p>Por ping e por ARP. Quem bloqueia ping também aparece.</p></div>
<div><h3>Fabricante e nome</h3><p>Pelo MAC, na tabela do IEEE, e por DNS, NetBIOS e mDNS.</p></div>
<div><h3>De onde vem a rede</h3><p>DHCP, gateway, DNS, Wi-Fi e, com um clique, o IP público.</p></div>
<div><h3>Detalhe de cada equipamento</h3><p>Nomes, TTL, portas abertas e atalhos para ping, navegador e área de trabalho remota.</p></div>
<div><h3>Detalhe de cada equipamento</h3><p>Nomes, TTL, portas abertas, serviços e atalhos para ping, navegador e área de trabalho remota.</p></div>
<div><h3>Ferramentas de rede</h3><p>Ping, tracert, DNS, ARP, conexões e rotas, com a saída em português.</p></div>
<div><h3>Manutenção</h3><p>Limpar o cache DNS, renovar o IP, limpar o ARP e resetar o Winsock.</p></div>
<div><h3>Relatório em HTML</h3><p>Arquivo único para guardar com a documentação do cliente.</p></div>
Expand All @@ -74,7 +74,7 @@ <h2>Como usar</h2>
<h2>Uso autorizado</h2>
<p>O MapNet serve para inventariar equipamentos em redes nas quais você tem autorização para fazer a verificação.</p>
<ul>
<li><strong>O que ele envia:</strong> ping, pedidos ARP e consultas de nome (DNS, NetBIOS e mDNS) aos endereços da sub-rede escolhida, as mesmas mensagens que os computadores de uma rede trocam entre si. Se você ligar a verificação de portas, ele também abre e fecha uma conexão TCP em cada porta da lista, só nos equipamentos encontrados e sem enviar dados.</li>
<li><strong>O que ele envia:</strong> ping, pedidos ARP e consultas de nome (DNS, NetBIOS e mDNS) aos endereços da sub-rede escolhida, as mesmas mensagens que os computadores de uma rede trocam entre si. Se você ligar a verificação de portas, ele também abre e fecha uma conexão TCP em cada porta da lista, só nos equipamentos encontrados e sem enviar dados. Com a identificação de serviços, pede a página inicial dos serviços web abertos (só um GET, sem senha), lê o certificado HTTPS e a primeira linha de SSH, FTP e SMTP, e faz uma busca UPnP na rede local.</li>
<li><strong>O que ele não faz:</strong> não testa senhas, não contorna autenticação, não explora vulnerabilidades e não muda configuração de nada na rede. A manutenção mexe só no computador de quem usa, e só com o clique.</li>
<li><strong>Sua parte:</strong> confira o intervalo de endereços antes de iniciar e respeite as regras da rede e dos dispositivos examinados.</li>
</ul>
Expand Down
114 changes: 114 additions & 0 deletions src/mapnet.nucleo/identificacao/etapa-identificacao.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Net;

namespace MapNet.Nucleo;

/// <summary>
/// Etapa de identificação, depois das portas. Só conversa com as portas que a etapa de portas
/// achou abertas, e só nos hosts que ela verificou: nunca sonda porta nem host novo. O UPnP é uma
/// busca só na rede local, e das respostas ficam só as dos hosts verificados.
/// </summary>
public static class EtapaIdentificacao
{
public const string Nome = "Identificação de serviços";

/// <summary>Protocolo usado em cada porta. Porta fora desta lista não é identificada (Telnet fica de fora de propósito).</summary>
public static ProtocoloServico? Protocolo(int porta) => porta switch
{
80 or 8000 or 8080 => ProtocoloServico.Http,
443 or 8443 => ProtocoloServico.Https,
21 or 22 or 25 => ProtocoloServico.Banner,
_ => null,
};

public static string NomeBanner(int porta) => porta switch
{
21 => "FTP",
22 => "SSH",
25 => "SMTP",
_ => "TCP",
};

public static async Task IdentificarAsync(
IReadOnlyList<HostEncontrado> hosts,
IPAddress origem,
OpcoesVarredura opcoes,
IIdentificador identificador,
IProgress<ProgressoVarredura>? progresso,
CancellationToken cancelamento)
{
var verificados = hosts.Where(h => h.PortasVerificadas).ToList();
var tarefas = verificados
.SelectMany(h => h.PortasAbertas.Where(p => Protocolo(p) != null).Select(p => (Host: h, Porta: p)))
.ToList();
var total = tarefas.Count + 1;
var concluidos = 0;
progresso?.Report(new ProgressoVarredura(Nome, 0, total));

using var vagas = new SemaphoreSlim(Math.Max(1, opcoes.IdentificacoesSimultaneas));
var servicos = tarefas.Select(async t =>
{
await vagas.WaitAsync(cancelamento).ConfigureAwait(false);
try
{
var s = await identificador.IdentificarAsync(t.Host.Ip, t.Porta, Protocolo(t.Porta)!.Value, opcoes.TempoIdentificacaoMs, cancelamento)
.ConfigureAwait(false);
if (s != null)
{
lock (t.Host)
{
t.Host.Servicos = [.. t.Host.Servicos, s];
}
}
}
finally
{
vagas.Release();
}

progresso?.Report(new ProgressoVarredura(Nome, Interlocked.Increment(ref concluidos), total, t.Host));
});

var upnp = UpnpAsync(verificados, origem, opcoes, identificador, cancelamento);
await Task.WhenAll(servicos.Append(upnp)).ConfigureAwait(false);
progresso?.Report(new ProgressoVarredura(Nome, Interlocked.Increment(ref concluidos), total));

foreach (var h in verificados)
{
h.Servicos = [.. h.Servicos.OrderBy(s => s.Porta)];
h.ServicosIdentificados = true;
}
}

private static async Task UpnpAsync(
IReadOnlyList<HostEncontrado> verificados,
IPAddress origem,
OpcoesVarredura opcoes,
IIdentificador identificador,
CancellationToken cancelamento)
{
var porIp = verificados.ToDictionary(h => h.IpNumero);
var respostas = await identificador.BuscarUpnpAsync(origem, opcoes.TempoIdentificacaoMs, cancelamento).ConfigureAwait(false);

// Um equipamento responde várias vezes, uma por serviço: fica a primeira de cada IP.
var unicas = respostas
.Where(r => r.Origem.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && porIp.ContainsKey(SubRede.ParaNumero(r.Origem)))
.GroupBy(r => SubRede.ParaNumero(r.Origem))
.Select(g => g.First());

await Task.WhenAll(unicas.Select(async r =>
{
var host = porIp[SubRede.ParaNumero(r.Origem)];
string? modelo = null;
if (Ssdp.DescricaoDoProprio(r) is { } endereco)
{
modelo = Ssdp.Modelo(await identificador.LerDescricaoAsync(endereco, opcoes.TempoIdentificacaoMs, cancelamento).ConfigureAwait(false));
}

var s = new ServicoIdentificado { Porta = 0, Protocolo = "UPnP", Modelo = modelo, Servidor = r.Servidor };
lock (host)
{
host.Servicos = [.. host.Servicos, s];
}
})).ConfigureAwait(false);
}
}
Loading
Loading