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

来源:互联网 发布:ubuntu下重装ubuntu 编辑:程序博客网 时间:2024/05/22 12:35

Section I Problem Specification

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

TCP数据包:

作为必备的知识,主要就是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的包。

类图:


序列图:



Section III Test Cases and Results Analysis


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;//EthernetHeaderint i=0;for (i;i<6;i++){this->destinationAddress[i]=gatewayMAC.PhysicalAddress[i];this->sourceAddress[i]=currentSlectedAdapter->Address[i];}this->ethernetType=htons(0x0800);//Ipheaderthis->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);//绝对不能为0this->dwAck=0;this->ucLength=0x80;this->ucFlag=0x002;//表示只是设置SYN为1this->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;}