书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口

时间:2021-02-14 18:14:35

Section I Problem Specification

本次实验主要是写一个程序:能够扫描到指定Ip地址的端口是否开放。主要利用TCP协议,因为如果端口开放,目的主机将会:发送固定格式的TCP数据给源主机,表示:可以利用TCP进行连接。如果端口没有开发,那么也会返回一个特别的TCP数据包给源主机,表示不能连接。我们主要是根据返回的TCP数据包判断该端口是否开放。

TCP数据包:

作为必备的知识,主要就是TCP数据包的格式以及各个字段的含义: 书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口
书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口

书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口
书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口
上面两幅图清楚的指明了TCP数据包的格式以及内容,下面对其内容作简要的解释: 序列号(sequence Number):TCP协议将发送一段连接的数据,这个序列号也就是按照数据流的先后顺序给每个发送的数据包编号 确认号(Acknowledgement number):表示接收端希望接受的下一个TCP协议包的第一个字节的编号,与发送端下一个要发的序列号相对应。 头部长度:TCP数据包的长度,单位为ULONG,其范围为5到15之间,基本长度为20B,最长可以是60B。 保留字段:无实际用途,设置为0。
控制字段:
SYN:同步信息,用于建立一个新的TCP连接
ACK:确认信息,当当前号设置为1时,表示确认号才有效。
PSH:表示希望协议栈尽快向上层传递数据
RST:表示Reset,强制切断连接
URG:表示需要紧急处理的数据存在。
FIN:连接正常终止,用来接收现有的TCP连接

窗口大小:用来表示滑动窗口的大小 校验和:数据报的忘记校验和 紧急指针:代表紧急数据在数据包中的位置,在URG置位时才有效(置位一词说的是将该位设置为1)

利用TCP扫描端口:

TCP连接的正常流程,
  1. Host A 发一个 SYN 包 给Host B,
  2. 如果端口打开,Host B 将会回一个 SYN+ACK包给 Host A。如果端口关闭,Host B 将会发一个RST+ACK包给Host A
  3. 那么Host A只需要再回一个ACK给Host B即可建立连接。

所以我们关键就是利用 第2部回过来的消息判断,到底是打开还是关闭。

Section II Solution Method and Design

主要流程还是构造包、发包、收包的过程,和上次实验差不多。这不过这次构造的是TCP的包。

类图:

书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口

序列图:

书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口

Section III Test Cases and Results Analysis

书:计算机网络高级软件编程技术(P120) 之 基础训练:利用TCP协议扫描端口

Section IV Conclusion

校验和 一定要计算吗?

百度知道:用wireshark抓包,为什么校验和错误还是能建立TCP三次连接 我也在此遇到了问题,如果 用网上下载的软件:ScanPort1.2(已存网盘)进行端口扫描, 无论我是否将网卡的 CheckSum Offload(硬件校验和) 功能 开启,该软件均能成功进行TCP连接(或者扫描到端口)。 但是,经过我用wireshark抓的包来看,该软件的的IPheader的checkSum还是TCP数据包里面的CheckSum 均为错误。
然而,我使用winpcap发包,我计算了IP头的checkSum,也计算了TCP数据包的checkSum, CheckSum Offload(硬件校验和) 功能是否开启,我都必须填写正确的CheckSum。

计算TCP包的校验和:


我的错在于 伪首部的长度:
theTcpFakeHeader.bTcpLength=htons(32);//计算校验和的时候必须将32转为网络字节 

整个代码请看TCPpacket.h与TCPpacket.cpp


Section V References

书:计算机网络高级软件编程技术(吴功宜)  第八章 TCP和UDP数据包发送程序 网站:msdn

Section VI Appendix

TCP类:

头文件:其中继承的Ipheader类等等的详细情况请看移驾本人另一篇博客: 书:计算机网络高级软件编程技术(P88) 之 基础训练:路由追踪程序的实现(tracert程序)
#pragma once
#include "ipheader.h"
class TCPpacket :
public Ipheader
{
public:
//下面两个是传输层的内容
//由于我想取到端口号,TCP协议和UDP协议在destAddress后面都是2个字节的源端口和2个字节的目的端口
unsigned short SourcePort;//源端口
unsigned short DestPort;//目的端口
ULONG dwSeq;
ULONG dwAck;
UCHAR ucLength; //4 位首部长度/4 位保留字
UCHAR ucFlag; //6 位标志位
USHORT usWindow; //16 位窗口大小
USHORT usCrc;//16 位校验和
USHORT usUrgent;//16 位紧急数据偏移量
UINT unMssOpt;
UINT windowsScale;//根据抓包器抓出来的TCP包就有这个字段,具体不知道为什么。
USHORT usNopOpt;
USHORT usSackOpt;
TCPpacket(unsigned short DestinationPort,LPVOID lpParameter,PIP_ADAPTER_INFO currentSlectedAdapter ,MIB_IPNET_ROW2 gatewayMAC);
~TCPpacket(void);
unsigned short getSourcePort();
unsigned short getDestPort();
unsigned short TCPpacket::GenerateChecksum(unsigned short* pBuf, int iSize);
unsigned short TCPpacket::TcpCheckSum(const char *pTcpData, const char *pPshData, UINT nTcpCount);
USHORT TCPpacket::CheckSum(USHORT *buffer, int size);
USHORT TCPpacket::CheckSum(const char *buf, int size);
};



实现文件:
#include "StdAfx.h"
#include "dialog1Dlg.h"
#include "TCPpacket.h"

struct TcpFakeHeader
{
DWORD dwSourceAddr;//源地址
DWORD dwDestAddr;//目的地址
BYTE bZero;//置空
BYTE bProtocolType;//协议类型
USHORT bTcpLength;//TCP长度
};
TCPpacket::TCPpacket(unsigned short DestinationPort,LPVOID lpParameter,PIP_ADAPTER_INFO currentSlectedAdapter ,MIB_IPNET_ROW2 gatewayMAC)
{
Cdialog1Dlg *Cdialog=(Cdialog1Dlg*)lpParameter;
//EthernetHeader
int i=0;
for (i;i<6;i++)
{
this->destinationAddress[i]=gatewayMAC.PhysicalAddress[i];
this->sourceAddress[i]=currentSlectedAdapter->Address[i];
}
this->ethernetType=htons(0x0800);

//Ipheader
this->Version_HeaderLength=0x45;
this->TypeOfService=0x00;
this->TotalLength=htons(52);//根据wireshark抓的一个包的大小填的
this->Identification=htons(0x3521);
this->Flags_FragmentOffset=htons(0x4000);
this->TimeToLive=64;
this->Protocal=6;//1就是ICMP的协议
this->HeaderChecksum=htons(0x0000);

TcpFakeHeader theTcpFakeHeader;
CString text;
Cdialog->m_ListBoxIpAddress.GetText(0,text);
this->SourceAddress=inet_addr(text);
theTcpFakeHeader.dwSourceAddr=inet_addr(text);
Cdialog->m_CEditScanIp.GetWindowText(text);
this->DestAddress=inet_addr(text);
theTcpFakeHeader.dwDestAddr=inet_addr(text);
if (DestinationPort+20000>60000)//这个地方的算法简直没设计好。
{
this->SourcePort=htons(DestinationPort+20000);
}else{
this->SourcePort=htons(DestinationPort-20000);
}
this->DestPort=htons(DestinationPort);
this->dwSeq=ntohl(198327);//绝对不能为0
this->dwAck=0;
this->ucLength=0x80;
this->ucFlag=0x002;//表示只是设置SYN为1
this->usWindow=htons(0x2000);
this->usCrc=0;
this->usUrgent=0;
this->unMssOpt=htonl(0x020405B4);
this->windowsScale=htonl(0x01030302);
this->usNopOpt=0x0101;
this->usSackOpt=0x0204;

//ip包的checkSum
unsigned char IPheaderToCheck[20];
memset(&IPheaderToCheck,0,sizeof(IPheaderToCheck));
memcpy(&IPheaderToCheck,&this->Version_HeaderLength,20);//ip包头的大小
this->HeaderChecksum=GenerateChecksum((unsigned short*)IPheaderToCheck,20);


theTcpFakeHeader.bZero=0;
theTcpFakeHeader.bTcpLength=htons(32);//计算校验和的时候必须将32转为网络字节
theTcpFakeHeader.bProtocolType=6;
this->usCrc=TcpCheckSum((char *)(&(this->SourcePort)),(char *)(&theTcpFakeHeader),32);

}


TCPpacket::~TCPpacket(void)
{
}
unsigned short TCPpacket::getSourcePort() { return SourcePort; }
unsigned short TCPpacket::getDestPort() { return DestPort; }
unsigned short TCPpacket::GenerateChecksum(unsigned short* pBuf, int iSize)
{
unsigned long cksum = 0;
while (iSize>1)
{
cksum += *pBuf++;
iSize -= sizeof(USHORT);
}
if (iSize)
cksum += *(UCHAR*)pBuf;

cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);

return (USHORT)(~cksum);
}

USHORT TCPpacket::CheckSum(const char *buf, int size)
{
USHORT *buffer=(USHORT *)buf;
unsigned long cksum=0;
while(size >1)
{
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size )
{
cksum += *(UCHAR*)buffer;
}

cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
USHORT TCPpacket::CheckSum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size >1)
{
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size )
{
cksum += *(UCHAR*)buffer;
}

cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}


unsigned short TCPpacket::TcpCheckSum(const char *pTcpData, const char *pPshData, UINT nTcpCount)
{
unsigned short sCheckSum = ~CheckSum(pTcpData,nTcpCount);
unsigned long checkSum = sCheckSum;
checkSum <<= 16;
sCheckSum = ~CheckSum(pPshData,12);
checkSum += sCheckSum;

return CheckSum((char*)&checkSum,4);
}


在dialog1Dlg.cpp里添加的代码:

在button监听实现:
void Cdialog1Dlg::OnBnClickedButton2()
{
m_CEditScanIp.GetWindowText(str);
if(currentSlectedAdapter==0)
{
MessageBox("请先于左侧框内选择设备!");
}else if (str.IsEmpty())
{
MessageBox("请输入IP地址");
}else{
m_hThreadtoRoute = CreateThread(NULL,0,ThreadProcScanPortByWinpcap,this,0,NULL);
}
// TODO: 在此添加控件通知处理程序代码
}

接包和发包的线程:
DWORD _stdcall ThreadProcScanPortByWinpcap(LPVOID lpParameter){
Cdialog1Dlg *Cdialog=(Cdialog1Dlg*)lpParameter;

char errBuf[PCAP_ERRBUF_SIZE];
pcap_findalldevs(&allDevs,errBuf);//列举所有设备 ,这里用来获取网络适配器信息的函数.pcap_findalldevs() returns 0 on success and -1 on failure.当errBuf满了会返回错误信息。

CString a=currentSlectedAdapter->AdapterName;
CString subfromIpHelper = a.Mid(a.ReverseFind('{')+1, 4);
for(Dev=allDevs;Dev;Dev=Dev->next){
a=Dev->name;
CString subfromWinpcap = a.Mid(a.ReverseFind('{')+1, 4);
if(subfromWinpcap==subfromIpHelper){
break;
}
}
currentOpenDev=pcap_open(Dev->name,65535,PCAP_OPENFLAG_PROMISCUOUS,1000,NULL,errBuf);

CString text;
Cdialog->m_CEditScanPortBegin.GetWindowText(text);
int begin=_ttoi(text);
Cdialog->m_CEditScanPortEnd.GetWindowText(text);
int end=_ttoi(text);
for (begin;begin<=end;begin++)
{
TCPpacket sendTCPpacket=TCPpacket(begin,Cdialog,currentSlectedAdapter,gatewayMAC);
unsigned char frame[66];
memset(&frame,0,sizeof(frame));
memcpy(&frame,&sendTCPpacket,sizeof(sendTCPpacket));
pcap_sendpacket(currentOpenDev,frame,sizeof(frame));
}


pcap_pkthdr* hdr;
const u_char *pkt_datas;
int flag=0;
unsigned long temp;
while(true){
pcap_next_ex(currentOpenDev,&hdr,&pkt_datas);
TCPpacket *getTCP;;
getTCP=(TCPpacket *)pkt_datas;
if (ntohs(getTCP->ethernetType)==0x0800&&getTCP->Protocal==6&&getTCP->ucFlag==0x12)
{

str.Format("%d",ntohs(getTCP->SourcePort));
int length=Cdialog->m_ListBoxScanResult.GetCount();
for (flag=0;flag<length;flag++)
{
CString strToCompare;
Cdialog->m_ListBoxScanResult.GetText(flag,strToCompare);
if (strToCompare==str)
{
break;
}
}
if (flag==Cdialog->m_ListBoxScanResult.GetCount())
{
Cdialog->m_ListBoxScanResult.AddString(str);
}

}
}
return 0;
}