多线程扫描器是一种并发执行的网络安全工具,用于扫描目标网络上的主机和端口,以发现可能存在的漏洞或弱点。扫描器利用多线程并发地扫描目标主机和端口,通过对网络通信的分析和响应处理,发现潜在的安全问题。它能够提高扫描效率和准确性,缩短扫描时间,并帮助管理员或安全专家识别和修复可能存在的漏洞。
其原理基于以下几个关键步骤:
-
目标选择:确定要扫描的目标网络或主机。这可以是单个IP地址、IP地址范围或域名。
-
端口扫描:使用多线程技术并发地扫描目标主机的端口。常见的端口扫描方法包括TCP连接扫描、SYN扫描、UDP扫描等。每个线程负责扫描一个或多个端口,并记录响应结果。
-
并发处理:为了提高扫描速度,多线程扫描器同时执行多个扫描任务。每个线程负责处理一个目标主机或一个端口范围。通过合理的线程管理和调度策略,可以有效地利用计算资源。
-
响应处理:根据端口扫描的结果,对每个目标主机或端口进行相应的处理。这可能包括判断端口是否开放、识别服务和应用程序、检测漏洞等。具体的响应处理方式取决于扫描器的设计和目的。
-
结果报告:将扫描结果整理并生成报告。报告可以包括目标主机列表、开放的端口列表、发现的漏洞或弱点等信息。通常,报告可以以文本、HTML或其他格式输出,以便用户进行进一步分析和处理。
获取本机IP地址
这段代码是一个简单的示例,代码的作用是获取本机的IPv4和IPv6地址,并将其输出到控制台。
-
GetLocalAddress
方法是用来获取本机的IP地址列表。它接收一个参数netType
,用于指定要获取的地址类型,可以是"InterNetwork"
(IPv4)或"InterNetworkV6"
(IPv6)。方法内部使用Dns.GetHostName()
获取主机名,然后使用Dns.GetHostAddresses()
获取该主机名对应的IP地址数组。根据netType
的值,筛选出符合条件的IP地址并添加到IpList
列表中,最后返回该列表。 -
在
Main
方法中,首先调用GetLocalAddress
方法获取本机的IPv4地址列表,并使用foreach
循环遍历输出每个地址。 -
接下来,调用
GetLocalAddress
方法获取本机的IPv6地址列表,并同样使用foreach
循环输出每个地址。 -
最后,使用
Console.ReadKey()
方法等待用户按下任意键后程序结束。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
public static List<string> GetLocalAddress(string netType)
{
string HostName = Dns.GetHostName();
IPAddress[] address = Dns.GetHostAddresses(HostName);
List<string> IpList = new List<string>();
if(netType == string.Empty)
{
for (int i = 0; i < address.Length; i++)
{
IpList.Add(address[i].ToString());
}
}
else
{
for (int i = 0; i < address.Length; i++)
{
if (address[i].AddressFamily.ToString() == netType)
{
IpList.Add(address[i].ToString());
}
}
}
return IpList;
}
static void Main(string[] args)
{
// 获取IPV4地址
List<string> ipv4 = GetLocalAddress("InterNetwork");
foreach (string each in ipv4)
Console.WriteLine(each);
// 获取IPV6地址
List<string> ipv6 = GetLocalAddress("InterNetworkV6");
foreach (string each in ipv6)
Console.WriteLine(each);
Console.ReadKey();
}
}
}
线程操作基础
这段代码是一个简单的示例,代码的作用是创建一个新的线程,并获取和显示线程的一些基本信息,然后等待子线程执行完毕。同时,在子线程函数中输出一条信息。
-
首先,在
My_Thread
方法中定义了一个无参的线程函数,它会在执行时输出一条信息。 -
在
Main
方法中,创建了一个ThreadStart
委托对象childref
,该委托用于指定线程要执行的方法。然后使用childref
初始化一个新的线程thread
。 -
调用
thread.Start()
方法启动线程。 -
使用一些属性和方法来获取和显示线程的信息:
thread.ManagedThreadId
返回线程的唯一标识符。thread.Name
返回线程的名称。thread.ThreadState
返回线程的状态,以字符串形式表示。thread.Priority
返回线程的优先级。thread.IsBackground
表示线程是否为后台线程。
-
使用
Thread.Sleep(1000)
让主线程暂停一秒,让子线程有足够的时间运行。 -
使用
thread.Join()
方法等待子线程执行完毕。 -
最后,使用
Console.ReadKey()
方法等待用户按下任意键后程序结束。
using System;
using System.Collections;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
// 定义一个无参线程函数
public static void My_Thread()
{
Console.WriteLine("线程函数已运行");
}
static void Main(string[] args)
{
string strinfo = string.Empty;
ThreadStart childref = new ThreadStart(My_Thread);
Thread thread = new Thread(childref);
thread.Start();
Console.WriteLine("线程唯一标识符: " + thread.ManagedThreadId);
Console.WriteLine("线程名称: " + thread.Name);
Console.WriteLine("线程状态: " + thread.ThreadState.ToString());
Console.WriteLine("线程优先级: " + thread.Priority.ToString());
Console.WriteLine("是否为后台线程: " + thread.IsBackground);
Thread.Sleep(1000);
thread.Join();
Console.ReadKey();
}
}
}
线程传递参数
这段代码是一个多线程示例,代码的作用是创建了200个线程,每个线程输出一个包含姓名和年龄信息的结构体对象的内容,并在输出后休眠3秒钟。这样可以模拟多个线程同时执行,并展示多线程的并发特性。
-
首先,在
ThreadObj
结构体中定义了两个字段name
和age
,用于存储姓名和年龄的信息。 -
在
My_Thread
方法中,通过将obj
强制转换为ThreadObj
结构体类型,获取传递给线程的姓名和年龄信息,并输出到控制台。然后通过Thread.Sleep(3000)
让线程休眠3秒钟。 -
在
Main
方法中,使用for
循环创建200个线程。在每次循环中,创建一个新的ThreadObj
对象,并将姓名设为 "admin",年龄设为循环变量x
的值。然后创建一个新的线程thread
,将My_Thread
方法作为线程函数,并将obj
作为参数传递给线程。最后,将线程的IsBackground
属性设置为true
,以确保在主线程结束时,后台线程也会自动结束。 -
执行完循环后,通过
Console.ReadKey()
方法等待用户按下任意键后程序结束。
using System;
using System.Collections;
using System.Threading;
namespace ConsoleApplication1
{
public struct ThreadObj
{
public string name;
public int age;
public ThreadObj(string _name, int _age)
{
this.name = _name;
this.age = _age;
}
}
class Program
{
// 定义一个无参线程函数
public static void My_Thread(object obj)
{
ThreadObj thread_path = (ThreadObj)obj;
Console.WriteLine("姓名: {0} 年纪: {1}", thread_path.name, thread_path.age);
Thread.Sleep(3000);
}
static void Main(string[] args)
{
for(int x=0;x<200;x++)
{
ThreadObj obj = new ThreadObj("admin", x);
Thread thread = new Thread(My_Thread);
thread.IsBackground = true;
thread.Start(obj);
}
Console.ReadKey();
}
}
}
实现端口扫描
这段代码是一个简单的端口扫描程序,代码通过循环尝试连接指定 IP 地址上的各个端口,并输出每个端口的开放状态。它可以用于快速扫描指定 IP 地址上的常见端口,以便进行网络安全评估或服务探测。请注意,这只是一个简单的示例,并不能完整地表示端口扫描的所有细节和技术。在实际应用中,需要考虑更多的因素,例如扫描范围、扫描速度和权限等。
-
首先定义了一个整型数组
Port
,其中包含了要扫描的端口号。 -
在
foreach
循环中,对于每个端口号,创建一个新的Socket
对象sock
,指定了地址族为InterNetwork
,套接字类型为Stream
,协议类型为IP
。 -
使用
sock.Connect("192.168.1.10", each)
尝试连接到指定的 IP 地址和端口。如果连接成功,则表示该端口开放,输出 "端口开启:"加上端口号。 -
如果连接失败,捕获异常并输出 "端口关闭:"加上端口号,然后关闭套接字。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// FTP, SSH, Telnet, SMTP, HTTP, POP3, RPC, SMB, SMTP, IMAP, POP3
int[] Port = new int[] { 21, 22, 23, 25, 80, 110, 135, 445, 587, 993, 995 };
foreach(int each in Port)
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
try
{
sock.Connect("192.168.1.10",each);
if(sock.Connected)
{
Console.WriteLine("端口开启:" + each);
}
}
catch
{
Console.WriteLine("端口关闭:" + each);
sock.Close();
}
}
}
}
}
多线程端口扫描
这段代码实现了一个具有超时功能的端口扫描程序。这段代码可以用于快速扫描指定 IP 地址范围内的多个端口,并输出每个端口的开放状态。它利用了异步连接操作和超时机制,可以提高扫描效率,并避免长时间等待连接的情况。请注意,在实际使用中,应该遵守法律和道德规范,并遵循适当的授权和使用方式。
主要代码解析如下:
-
定义了一个私有字段
ip
,用于存储目标 IP 地址,以及一个整型数组ports
,包含要扫描的端口号列表。 -
Connect
方法接受一个IPEndPoint
对象和超时时间(以毫秒为单位),并返回一个布尔值表示连接是否成功。在方法中,创建一个Socket
对象scanSocket
,指定地址族为InterNetwork
,套接字类型为Stream
,协议类型为IP
。 -
调用
scanSocket.BeginConnect
方法发起异步连接操作,使用result.AsyncWaitHandle.WaitOne
等待连接操作完成,超时时间由TimeSpan.FromMilliseconds(timeoutMSec)
指定。如果连接操作完成且成功建立连接,则返回true
,否则返回false
。 -
在
Main
方法中,创建TimeoutPortScan
对象ps
,然后遍历指定的 IP 地址范围(从1到255),构建目标 IP 地址。 -
对于每个 IP 地址,遍历
ports
数组,调用ps.Connect
方法尝试与目标主机的每个端口建立连接,超时时间设置为100毫秒。 -
如果连接成功,则输出 "IP: {0} --> 端口: {1} --> 状态: Open",其中
{0}
和{1}
分别代表目标 IP 地址和端口号。
using System;
using System.Net;
using System.Net.Sockets;
namespace TimeoutPortScan
{
class TimeoutPortScan
{
private IPAddress ip;
private readonly int[] ports = new int[] { 21, 22, 23, 25, 53, 80, 110, 118, 135, 143, 156, 161,
443, 445, 465, 587, 666, 990, 991, 993, 995, 1080, 1433, 1434, 1984, 2049, 2483, 2484, 3128,
3306, 3389, 4662, 4672, 5222, 5223, 5269, 5432, 5500, 5800, 5900, 8000, 8008, 8080 };
public bool Connect(IPEndPoint remoteEndPoint, int timeoutMSec)
{
Socket scanSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
try
{
IAsyncResult result = scanSocket.BeginConnect(remoteEndPoint, null, null);
bool success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeoutMSec), false);
if (result.IsCompleted && scanSocket.Connected)
{
scanSocket.EndConnect(result);
return true;
}
else
return false;
}
finally
{
scanSocket.Close();
}
}
static void Main(string[] args)
{
TimeoutPortScan ps = new TimeoutPortScan();
for (int x = 1; x < 255;x++ )
{
string addr = string.Format("192.168.1.{0}", x);
IPAddress.TryParse(addr, out ps.ip);
for (int num = 0; num < ps.ports.Length; num++)
{
if (ps.Connect(new IPEndPoint(ps.ip, ps.ports[num]), 100))
Console.WriteLine("IP:{0} --> 端口: {1} --> 状态: Open", addr,ps.ports[num]);
}
}
}
}
}
异步端口扫描
这段代码实现了一个异步端口扫描程序。它从命令行参数中提取目标 IP 地址、起始端口和结束端口,然后使用异步连接方式扫描指定范围内的端口,并输出开放的端口号。它利用异步连接操作和回调函数来实现非阻塞的扫描过程,提高了扫描效率。注意,在实际使用中,请遵守法律和道德规范,并遵循适当的授权和使用方式。
主要代码解析如下:
-
Main
方法获取命令行参数,并调用GetPortRange
方法解析目标 IP 地址、起始端口和结束端口。如果解析成功,则调用Scan
方法进行端口扫描。 -
GetPortRange
方法用于从命令行参数中提取目标 IP 地址、起始端口和结束端口。如果命令行参数符合要求,则将提取的值赋给相应的变量,并返回true
;否则,返回false
。如果命令行参数为/?
、/h
或/help
,则输出帮助信息。 -
Scan
方法用于进行端口扫描。它使用一个Socket
对象scanSocket
,指定地址族为InterNetwork
,套接字类型为Stream
,协议类型为IP
。然后,循环遍历指定的端口范围,在每次循环中创建一个新的随机端口进行绑定,以避免与已有的端口冲突。 -
在每次循环中,使用
scanSocket.BeginConnect
方法发起异步连接操作,指定目标 IP 地址和端口号,并提供一个回调函数ScanCallBack
。回调函数的第一个参数是IAsyncResult
对象,用于获取异步连接的结果。回调函数的第二个参数是一个ArrayList
对象,包含了需要在回调函数中使用的额外参数(scanSocket
和port
)。 -
在回调函数
ScanCallBack
中,首先解析回调函数的输入参数,并判断端口是否连接成功。如果连接成功,则输出 "端口: {0,5} 状态: Open",其中{0,5}
代表端口号,并关闭scanSocket
。
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections;
namespace AsyncPortScan
{
class AsyncPortScan
{
static void Main(string[] args)
{
IPAddress ip;
int startPort, endPort;
if (GetPortRange(args, out ip, out startPort, out endPort) == true) // 提取命令行参数
Scan(ip, startPort, endPort); // 端口扫描
}
/// 从命令行参数中提取端口
private static bool GetPortRange(string[] args, out IPAddress ip, out int startPort, out int endPort)
{
ip = null;
startPort = endPort = 0;
// 帮助命令
if (args.Length != 0 && (args[0] == "/?" || args[0] == "/h" || args[0] == "/help"))
{
Console.WriteLine("scan 192.168.1.10 100 2000");
return false;
}
if (args.Length == 3)
{
// 解析端口号成功
if (IPAddress.TryParse(args[0], out ip) && int.TryParse(args[1], out startPort) && int.TryParse(args[2], out endPort))
return true;
else
return false;
}
else
{
return false;
}
}
/// 端口 扫描
static void Scan(IPAddress ip, int startPort, int endPort)
{
Random rand = new Random((int)DateTime.Now.Ticks);
for (int port = startPort; port < endPort; port++)
{
Socket scanSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
//寻找一个未使用的端口进行绑定
do
{
try
{
scanSocket.Bind(new IPEndPoint(IPAddress.Any, rand.Next(65535)));
break;
}
catch
{
//绑定失败
}
} while (true);
try
{
scanSocket.BeginConnect(new IPEndPoint(ip, port), ScanCallBack, new ArrayList() { scanSocket, port });
}
catch
{
continue;
}
}
}
/// BeginConnect的回调函数 异步Connect的结果
static void ScanCallBack(IAsyncResult result)
{
// 解析 回调函数输入 参数
ArrayList arrList = (ArrayList)result.AsyncState;
Socket scanSocket = (Socket)arrList[0];
int port = (int)arrList[1];
// 判断端口是否开放
if (result.IsCompleted && scanSocket.Connected)
{
Console.WriteLine("端口: {0,5} 状态: Open", port);
}
scanSocket.Close();
}
}
}