中南大学
“通信网原理”实验报告
(第 三 次实验)
实验名称:
主机端口扫描程序设计
专业班级:
电子信息工程
学生姓名:
学
号:
指导教师:
陈科文,周扬
实验日期:
2016 年 11 月 22 日
中南大学·信息科学与工程学
1
实验名称
主机端口扫描程序设计
一、实验目的和要求
1、 实验目的
(1)通过实验,进一步加深理解传输层服务的基本概念和 TCP 与 UDP
协议的工作原理;
(2)掌握网络中主机端口扫描的设计思想与编程方法。
2、 实验要求
(1)实验之前应做好充分准备工作,并预先编写好源代码;
(2)上机实验时,程序必须调试成功,并接受教师验收(需边演示边
讲解,并回答教师提问);
(3)按要求撰写《实验报告》。
二、实验关键技术(包括编程环境和关键 API 函数简介,以及程
序设计基本思想,比如要解决的关键问题、工作流程,或主要数
据结构的定义、算法的描述等)
关键问题:
TCP 端口扫描
(1)端口扫描程序调用 socket()函数建立套接字,SOCK_STREAM 表示流
式套接字
(2)调用 connect()函数请求与目的主机的指定端口建立连接
(3)根据 TCP 连接是否成功建立,判断目的主机中的端口状态
UDP 端口扫描
(1)端口扫描程序调用 socket()函数建立套接字,SOCK_DGRAM 表示数
据报套接
字
(2)调用 ioctlsocket()函数将套接字设置为非阻塞
(3)调用 sendto()函数发送 UDP 数据包
(4)调用 recvfrom()函数接收返回的错误码,判断目的主机的端口状
态
实验原理:
端口是一个 0-65 535 之间的整数。端口可以分为两种类型:TCP 端口与
UDP 端口。其中,TCP 端口分配给 TCP 服务使用,UDP 端口分配给 UDP 服务使
2
用。通常情况下,TCP 与 UDP 端口同时分配给一种服务。
端口可以分为 3 种类型:熟知端口、注册端口与临时端口。其中,熟知端
口的范围是 0-- 1023,它被统一分配给某种指定的网络服务;注册端口的范
围是 1024-49151,它被分配给用户注册过的网络服务;临时端口的范围是
49152 一 65535,它可以被任何进程临时申请使用。根据传输层协议的规定,
服务器必须使用熟知端口来提供服务,客户机则使用临时端口来发送服务请
求。
主机端口扫描是网络安全检测的主要手段,它可以确定目的主机中处于
打开状态的端口,从而确定目的主机已经开启的服务类型,对维护主机系统
的安全性具有重要作用。由于有 TCP 与 UDP 两种端口类型,它们分别针对具
有不同特点的网络服务,因此端口扫描可以分为 TCP 端口扫描与 UDP 端口扫
描。
TCP 端口:TCP“三次握手”连接完成,说明连接成功。
TCP 端口扫描可以分为 3 种类型:
Connect 扫描:这是原理最简单的 TCP 扫描方式。端口扫描程序调用
Connect 系统调用,尝试连接目的主机的指定端口。如果成功建立
TCP 连接,说明两台主机完成一次完整的三次握手过程,该端口处
于开启状态;否则,说明该端口处于关闭状态。这种方式的优点是不
需要手工构造 TCP 包。由于 TCP 协议是保证可靠运行的协议,Connect
不会在尝试第一次连接未得到响应就放弃,而是会经过多次尝试后
才彻底放弃,因此它的缺点是工作效率比较低。
SYN 扫描:这是当前使用最广泛的 TCP 扫描方式。端口扫描程序向目
ACK=1
的主机的指定端口发送 SYN=1 的 TCP 包。如果接收到 SYN=1,
的 TCP 包,说明该端口处于开启状态;如果接收到 RST=1 的 TCP 包,
说明该端口处于关闭状态;如果没有接收到任何数据包,并且确定目
的主机开启,说明该端口被防火墙等安全设备过滤。由于 SYN 扫描
不完成 TCP 连接的三次握手过程,因此这种方式称为半开放扫描。
这种方式的最大优点是工作效率高,缺点是容易被人侵检测系统发
现。
FIN 扫描:这是一种比较隐蔽的 TCP 扫描方式。端口扫描程序向目的
主机的指定端口发送 FIN=I 的 TCP 包。如果没有接收到任何数据包,
并且确定目的主机开启,说明该端口处于开启状态;如果接收到
RST=I 的 TCP 包,说明该端口处于关闭状态。FIN 扫描的应用有很大
的局限性。不同系统实现网络协议栈的细节不同,FIN 扫描只能用
于 Linux 或 UNIX 系统。如果目的主机使用的是 Windows 系统,无论
端口是否开启都会直接返回 RST=1 的 TCP 包。
UDP 端口针对基于 UDP 服务的网络应用。在大多数情况下,如果向未开
3
启的 UDP 端口发送数据,目的主机都会返回一个端口不可达 ICMP 包 UDP
扫描的工作原理:端口扫描程序向目的主机的指定端口发送零字节的
UDP 包。如果没有接收到任何数据包,并且确定目的主机开启,说明该
端口处于开启状态;如果接收到端口不可达的 ICMP 包,说明该端口处干
关闭状态。由于 UDP 与 ICMP 协议都是不可靠的协议,没有接收到响应
可能是由于数据包没有到达,因此需要对端口进行多次扫描才能确定状
态。
实验流程图:
4
三、实验操作过程(包括:每一步实验内容、实验方法与中间结
果,实验过程中遇到的问题及解决办法)
1.了解传输层协议,主机端口分配方法,tcp,udp 端口扫描原理,方法,
以及不同扫描方法的优点和缺点的对比
2.界面设计
3.代码编写与调试
4.分析实验结果,总结
四、程序源代码(要求有良好的编程规范和必要的注释信息,完
整源代码可打印并粘贴)
// FindPortDlg.h : header file
class CFindPortDlg : public CDialog
{
// Construction
public:
CFindPortDlg(CWnd* pParent = NULL);
// standard constructor
// Dialog Data
//{{AFX_DATA(CFindPortDlg)
enum { IDD = IDD_FINDPORT_DIALOG };
CProgressCtrl m_progress;
CIPAddressCtrl
m_Ip;
CString
m_Port1;
CString
m_Port2;
CString
m_Status;
//}}AFX_DATA
5
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CFindPortDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
// DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CFindPortDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnTcpscan();
afx_msg void OnUdpscan();
afx_msg void OnClear();
afx_msg void OnStop();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
WSADATA WSAData;
SOCKET Socket;
CString
Ip;
int ip;
sockaddr_in DestHost;
CString Port;
BOOL isStop ;
};
// FindPortDlg.cpp : implementation file
#include "stdafx.h"
#include "FindPort.h"
#include "FindPortDlg.h"
#include "conio.h" //控制台调试信息
6
//TCP 端口扫描
void CFindPortDlg::OnTcpscan()
{
// TODO: Add your control notification handler code here
//OnClear();
m_progress.SetPos(0);
SetDlgItemText(IDSTATUS,"");
UpdateData(true);
if ((m_Port1 == "") || (m_Port2 == ""))
{
MessageBox("您没有输入正确的端口号");
return;
}
if(atoi(m_Port1) > atoi(m_Port2))
{
MessageBox("请将小的端口号输在前面");
return;
}
if(WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
{
m_Status += "初始化 Winsock 失败!\r\n";
UpdateData(false);
return;
}
m_Status += "初始化 Winsock 成功!\r\n";
//创建连接套接字
Socket = socket(AF_INET,SOCK_STREAM,0);
if(Socket == INVALID_SOCKET)
{
m_Status += "创建 Socket 失败!\r\n";
UpdateData(false); WSACleanup();
return;
}
7
m_Status += "创建 Socket 成功!\r\n";
//转换 IP 地址格式
m_Ip.GetWindowText(Ip);
//判断域名或 IP 地址
ip = inet_addr(Ip);
if(ip == INADDR_NONE)
{
hostent* pHostent = gethostbyname(Ip);
if(pHostent)
ip = (*(in_addr*)pHostent->h_addr_list).S_un.S_addr;
}
//定义目的主机地址
memset(&DestHost,0,sizeof(DestHost));
DestHost.sin_family = AF_INET;
DestHost.sin_addr.S_un.S_addr = ip;
int pos = 0;
double step; //setPos 时转为 int
isStop = FALSE;
step = (100+0.0)/(atoi(m_Port2)-atoi(m_Port1));
cprintf("step=%lf\n",step);
CString str;
//循环扫描套接字
for( int m_Port = atoi(m_Port1); m_Port <= atoi(m_Port2); m_Port++)
{
if(isStop==TRUE) //安全停止
{
cprintf("stop...\n");
break;
}
DestHost.sin_port = htons(m_Port);
8