init: arp lan scanner
This commit is contained in:
commit
96e75d545d
5 changed files with 177 additions and 0 deletions
14
.github/workflows/ci.yml
vendored
Normal file
14
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- run: cmake -B build
|
||||
- run: cmake --build build --config Release
|
||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
build/
|
||||
.claude/
|
||||
*.obj
|
||||
*.exe
|
||||
*.pdb
|
||||
*.ilk
|
||||
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
project(lanscan CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(lanscan src/main.cpp)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(lanscan ws2_32 iphlpapi)
|
||||
endif()
|
||||
29
README.md
Normal file
29
README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# lanscan
|
||||
|
||||
ARP-сканер локальной сети на C++. находит свою подсеть сам, шлёт ARP-запросы всем хостам параллельно, печатает кто ответил.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
lanscan->>OS: узнать свой IP + маску
|
||||
loop каждый хост в подсети
|
||||
lanscan->>Host: ARP-запрос (кто ты?)
|
||||
Host-->>lanscan: MAC-адрес (если жив)
|
||||
end
|
||||
```
|
||||
|
||||
через `SendARP` из iphlpapi — без сырых сокетов и драйверов, работает без админа.
|
||||
|
||||
## сборка
|
||||
|
||||
```
|
||||
cmake -B build
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
## юзать
|
||||
|
||||
```
|
||||
build\Release\lanscan.exe
|
||||
```
|
||||
|
||||
сам находит подсеть по активному адаптеру и сканирует её целиком.
|
||||
117
src/main.cpp
Normal file
117
src/main.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <cstdio>
|
||||
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
|
||||
struct LocalNet {
|
||||
uint32_t ip = 0;
|
||||
uint32_t mask = 0;
|
||||
bool found = false;
|
||||
};
|
||||
|
||||
static LocalNet find_local_net() {
|
||||
LocalNet net;
|
||||
ULONG size = 0;
|
||||
GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &size);
|
||||
std::vector<uint8_t> buf(size);
|
||||
IP_ADAPTER_ADDRESSES* addrs = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buf.data());
|
||||
if (GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, addrs, &size) != NO_ERROR) {
|
||||
return net;
|
||||
}
|
||||
|
||||
for (IP_ADAPTER_ADDRESSES* a = addrs; a != nullptr; a = a->Next) {
|
||||
if (a->OperStatus != IfOperStatusUp) continue;
|
||||
if (a->IfType == IF_TYPE_SOFTWARE_LOOPBACK) continue;
|
||||
|
||||
for (IP_ADAPTER_UNICAST_ADDRESS* ua = a->FirstUnicastAddress; ua != nullptr; ua = ua->Next) {
|
||||
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ua->Address.lpSockaddr);
|
||||
uint32_t ip = ntohl(sin->sin_addr.s_addr);
|
||||
if ((ip >> 24) == 127) continue;
|
||||
|
||||
uint8_t prefix = ua->OnLinkPrefixLength;
|
||||
if (prefix == 0 || prefix > 32) continue;
|
||||
|
||||
uint32_t mask = prefix == 32 ? 0xFFFFFFFFu : (~0u << (32 - prefix));
|
||||
net.ip = ip;
|
||||
net.mask = mask;
|
||||
net.found = true;
|
||||
return net;
|
||||
}
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
||||
static std::mutex out_mutex;
|
||||
|
||||
static void probe(uint32_t ip_host_order) {
|
||||
uint32_t target = htonl(ip_host_order);
|
||||
uint8_t mac[6];
|
||||
ULONG mac_len = 6;
|
||||
|
||||
DWORD res = SendARP(target, 0, mac, &mac_len);
|
||||
if (res != NO_ERROR) return;
|
||||
|
||||
in_addr addr{};
|
||||
addr.s_addr = target;
|
||||
char ipstr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &addr, ipstr, sizeof(ipstr));
|
||||
|
||||
char macstr[32];
|
||||
snprintf(macstr, sizeof(macstr), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
std::lock_guard<std::mutex> lock(out_mutex);
|
||||
std::cout << ipstr << "\t" << macstr << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
WSADATA wsa;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
|
||||
std::cerr << "WSAStartup failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
LocalNet net = find_local_net();
|
||||
if (!net.found) {
|
||||
std::cerr << "no local ipv4 network found" << std::endl;
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t network = net.ip & net.mask;
|
||||
uint32_t broadcast = network | ~net.mask;
|
||||
uint32_t host_count = broadcast - network;
|
||||
|
||||
if (host_count == 0 || host_count > 65534) {
|
||||
std::cerr << "subnet too big or too small to scan (" << host_count << " hosts)" << std::endl;
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
in_addr netaddr{};
|
||||
netaddr.s_addr = htonl(network);
|
||||
char netstr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &netaddr, netstr, sizeof(netstr));
|
||||
std::cout << "scanning " << netstr << "/" << host_count << " hosts..." << std::endl;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (uint32_t ip = network + 1; ip < broadcast; ++ip) {
|
||||
threads.emplace_back(probe, ip);
|
||||
if (threads.size() >= 64) {
|
||||
for (auto& t : threads) t.join();
|
||||
threads.clear();
|
||||
}
|
||||
}
|
||||
for (auto& t : threads) t.join();
|
||||
|
||||
WSACleanup();
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue