add: simple port scanner
This commit is contained in:
parent
1393bd6396
commit
2ab28e681d
|
|
@ -0,0 +1,16 @@
|
|||
cmake_minimum_required(VERSION 3.31)
|
||||
project(simplePortScanner)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
find_package(Boost REQUIRED COMPONENTS program_options)
|
||||
|
||||
|
||||
|
||||
add_executable(simplePortScanner main.cpp
|
||||
src/PortScanner.hpp
|
||||
src/PortScanner.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(simplePortScanner
|
||||
Boost::program_options
|
||||
)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# Simple Port Scanner (C++)
|
||||
|
||||
An asynchronous TCP port scanner implemented in C++ using boost::asio
|
||||
|
||||
## Features
|
||||
- Configurable port ranges (e.g. 1-1024, 1-65535)
|
||||
- Adjustable concurrency level
|
||||
- Connection timeouts
|
||||
- Clean and readable CLI output
|
||||
|
||||
## Educational Value
|
||||
- Asynchronous IO using boost::asio
|
||||
- TCP socket programming
|
||||
- Concurrency control
|
||||
- Basic network reconnaissance techniques
|
||||
|
||||
## Build
|
||||
|
||||
### Requirements
|
||||
- C++20
|
||||
- Boost library
|
||||
- CMake >= 3.16
|
||||
|
||||
### Build Instructions
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
./port_scanner -i 127.0.0.1 -p 1-1024 -t 100 -e 2
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#include "src/PortScanner.hpp"
|
||||
#include <boost/program_options.hpp>
|
||||
namespace po = boost::program_options;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help,h", "produce help message")
|
||||
("dname,i", po::value<std::string>()->default_value("127.0.0.1"), "set domain name or IP address")
|
||||
("ports,p", po::value<std::string>()->default_value("1-1024"), "set a port range from 1 to n")
|
||||
("threads,t", po::value<int>()->default_value(100), "max concurrent threads")
|
||||
("expiry_time,e", po::value<uint8_t>()->default_value(2)->value_name("sec"), "timeout in seconds")
|
||||
("verbose,v", "verbose output");
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
std::cout << desc << "\n";
|
||||
std::cout << "Examples:\n"
|
||||
<< " Scan common ports on localhost:\n"
|
||||
<< " ./port_scanner -i 127.0.0.1 -p 1-1024\n\n"
|
||||
<< " Full TCP port scan:\n"
|
||||
<< " ./port_scanner -i 192.168.1.1 -p 65535 -t 200\n\n"
|
||||
<< " Scan with custom timeout:\n"
|
||||
<< " ./port_scanner -i example.com -p 80-443 -e 5\n\n"
|
||||
<< " Postscriptum:\n"
|
||||
<< " Scan only systems you own or have explicit permission to test.\n";
|
||||
return 0;
|
||||
}
|
||||
std::string ip = vm["dname"].as<std::string>();
|
||||
std::string port = vm["ports"].as<std::string>();
|
||||
int threads = vm["threads"].as<int>();
|
||||
uint8_t expiry_time = vm["expiry_time"].as<uint8_t>();
|
||||
|
||||
PortScanner scanner;
|
||||
scanner.set_options(ip, port, threads, expiry_time);
|
||||
scanner.start();
|
||||
scanner.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
#include "PortScanner.hpp"
|
||||
|
||||
const std::unordered_map<uint16_t, std::string> PortScanner::basicPorts{
|
||||
{21, "FTP"},
|
||||
{22, "SSH"},
|
||||
{23, "TelNet"},
|
||||
{25, "SMTP"},
|
||||
{53, "DNS"},
|
||||
{67, "DHCP server"},
|
||||
{68, "DHCP client"},
|
||||
{80, "HTTP"},
|
||||
{110, "POP3"},
|
||||
{143, "IMAP"},
|
||||
{161, "SNMP"},
|
||||
{443, "HTTPS"},
|
||||
{445, "SMB"},
|
||||
{465, "SMTPS"},
|
||||
{993, "IMAPS"},
|
||||
{1080, "SOCKS"},
|
||||
{1521, "ORACLE DB"},
|
||||
{3306, "MySQL"},
|
||||
{3389, "RDP"},
|
||||
{5432, "PostgreSQL"},
|
||||
{6379, "Redis"},
|
||||
};
|
||||
|
||||
void PortScanner::parse_port(std::string& port) {
|
||||
auto t = std::find(port.begin(), port.end(), '-');
|
||||
if (t == port.end()) {
|
||||
startPort = 1;
|
||||
endPort = std::stoi(port);
|
||||
return;
|
||||
}
|
||||
auto it = port.begin();
|
||||
std::string s = "", e = "";
|
||||
while (it != port.end()) {
|
||||
if (*it == '-') {
|
||||
break;
|
||||
}
|
||||
s += *it;
|
||||
++it;
|
||||
}
|
||||
++it;
|
||||
while (it != port.end()) {
|
||||
e += *it;
|
||||
++it;
|
||||
}
|
||||
|
||||
int start = std::stoi(s);
|
||||
int end = std::stoi(e);
|
||||
//check a valid bounds
|
||||
if (start == 0 || end > MAX_PORT || start > end) {
|
||||
startPort = 1;
|
||||
endPort = MAX_PORT;
|
||||
}
|
||||
else {
|
||||
startPort = static_cast<uint16_t>(start);
|
||||
endPort = static_cast<uint16_t>(end);
|
||||
}
|
||||
}
|
||||
PortScanner::PortScanner(std::string& domainName, std::string& port, int max_threads, std::uint8_t expiry_time) {
|
||||
this->domainName = std::move(domainName);
|
||||
this->MAX_THREADS = max_threads;
|
||||
this->expiry_time = expiry_time;
|
||||
|
||||
parse_port(port);
|
||||
auto result = resolver.resolve(this->domainName, "");
|
||||
endpoint = *result.begin();
|
||||
|
||||
setup_queue();
|
||||
|
||||
}
|
||||
|
||||
void PortScanner::setup_queue() {
|
||||
q = std::queue<uint16_t>();
|
||||
for (int i = startPort; i <= endPort; i++) {
|
||||
q.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PortScanner::set_options(std::string& domainName, std::string& port, int max_threads, std::uint8_t expiry_time) {
|
||||
this->domainName = std::move(domainName);
|
||||
this->MAX_THREADS = max_threads;
|
||||
this->expiry_time = expiry_time;
|
||||
parse_port(port);
|
||||
|
||||
|
||||
auto result = resolver.resolve(this->domainName, "");
|
||||
endpoint = *result.begin();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PortScanner::set_max_port(std::uint16_t port) {
|
||||
endPort = port;
|
||||
}
|
||||
void PortScanner::set_max_threads(int value) {
|
||||
MAX_THREADS = value;
|
||||
}
|
||||
|
||||
void PortScanner::set_ip_address(std::string ip) {
|
||||
domainName = std::move(ip);
|
||||
|
||||
}
|
||||
|
||||
void PortScanner::set_expiry_time(std::uint8_t value) {
|
||||
expiry_time = value;
|
||||
}
|
||||
|
||||
void PortScanner::start() {
|
||||
setup_queue();
|
||||
for (int i = 0; i < MAX_THREADS; i++) {
|
||||
boost::asio::post(strand, [this]() {
|
||||
scan();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void PortScanner::run() {
|
||||
printf("PORT\tSTATE\tSERVICE\tBANNER\n");
|
||||
io.run();
|
||||
printf("\nResult:\n");
|
||||
printf(" Open ports: %d\n", open_ports);
|
||||
printf(" Closed ports: %d\n", closed_ports);
|
||||
printf(" Filtered ports: %d\n", filtered_ports);
|
||||
}
|
||||
|
||||
void PortScanner::scan() {
|
||||
if (q.empty() || cnt >= MAX_THREADS) return;
|
||||
|
||||
uint16_t port = q.front();
|
||||
q.pop();
|
||||
++cnt;
|
||||
|
||||
auto socket = std::make_shared<tcp::socket>(io);
|
||||
auto timer = std::make_shared<boost::asio::steady_timer>(io);
|
||||
auto complete = std::make_shared<bool>(false);
|
||||
|
||||
tcp::endpoint endpoint(this->endpoint.address(), port);
|
||||
|
||||
timer->expires_after(std::chrono::seconds(expiry_time));
|
||||
|
||||
timer->async_wait(boost::asio::bind_executor(strand, [this, complete, socket, port](boost::system::error_code ec) {
|
||||
if (!ec && !*complete) {
|
||||
*complete = true;
|
||||
socket->close();
|
||||
printf("%i\t%s\t%s\t%s\n", port, "FILTERED", "NULL", "NULL");
|
||||
++filtered_ports;
|
||||
--cnt;
|
||||
scan();
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
socket->async_connect(endpoint, boost::asio::bind_executor(strand, [this,socket, timer, port, complete](boost::system::error_code ec) {
|
||||
if (*complete) return;
|
||||
*complete = true;
|
||||
timer->cancel();
|
||||
|
||||
std::string service = "---";
|
||||
auto banner = std::make_shared<std::string>("---");
|
||||
auto it = basicPorts.find(port);
|
||||
if (it != basicPorts.end()) {
|
||||
service = it->second;
|
||||
}
|
||||
|
||||
if (!ec) {
|
||||
auto buf = std::make_shared<std::array<char, 128>>();
|
||||
|
||||
socket->async_read_some(boost::asio::buffer(*buf),boost::asio::bind_executor(strand,
|
||||
[this, port, buf, banner, service](boost::system::error_code ec, std::size_t n) {
|
||||
if (!ec && n > 0) {
|
||||
banner->assign(buf->data(), n);
|
||||
}
|
||||
printf("%i\t%sOPEN%s\t%s\t%s\n", port, GREEN,RESET, service.c_str(), banner->c_str());
|
||||
++open_ports;
|
||||
--cnt;
|
||||
scan();
|
||||
}));
|
||||
|
||||
}
|
||||
else{
|
||||
printf("%i\t%sCLOSED%s\t%s\t%s\n", port, RED, RESET, service.c_str(), banner->c_str());
|
||||
++closed_ports;
|
||||
--cnt;
|
||||
scan();
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef PORTSCANNER_HPP
|
||||
#define PORTSCANNER_HPP
|
||||
#include <boost/asio.hpp>
|
||||
#include <bits/stdc++.h>
|
||||
#define RED "\033[31m"
|
||||
#define GREEN "\033[32m"
|
||||
#define RESET "\033[0m"
|
||||
using boost::asio::ip::tcp;
|
||||
class PortScanner {
|
||||
private:
|
||||
static const std::unordered_map<uint16_t, std::string> basicPorts;
|
||||
static const uint16_t MAX_PORT = 65535;
|
||||
boost::asio::io_context io;
|
||||
boost::asio::ip::tcp::resolver resolver{io};
|
||||
boost::asio::ip::tcp::endpoint endpoint;
|
||||
boost::asio::strand<boost::asio::io_context::executor_type> strand{io.get_executor()};
|
||||
std::queue<std::uint16_t> q;
|
||||
int cnt = 0;
|
||||
int MAX_THREADS = 0;
|
||||
int open_ports = 0;
|
||||
int closed_ports = 0;
|
||||
int filtered_ports = 0;
|
||||
|
||||
std::string domainName;
|
||||
std::uint16_t startPort = 1;
|
||||
std::uint16_t endPort = MAX_PORT;
|
||||
|
||||
std::uint8_t expiry_time;
|
||||
|
||||
void scan();
|
||||
void setup_queue();
|
||||
void parse_port(std::string& port);
|
||||
public:
|
||||
PortScanner(std::string& ip_address, std::string& port, int max_threads, std::uint8_t expiry_time);
|
||||
PortScanner(){}
|
||||
~PortScanner() {
|
||||
|
||||
}
|
||||
|
||||
void set_options(std::string& domainName, std::string& port, int max_threads, std::uint8_t expiry_time);
|
||||
void set_max_port(std::uint16_t port);
|
||||
void set_max_threads(int value);
|
||||
void set_ip_address(std::string ip);
|
||||
void set_expiry_time(std::uint8_t value);
|
||||
|
||||
void start();
|
||||
void run();
|
||||
};
|
||||
|
||||
#endif //PORTSCANNER_HPP
|
||||
Loading…
Reference in New Issue