TCP SYN扫描(使用Winpcap库实现)
0.核心原理
半开扫描(TCP SYN)不同于CSocket和Socket2扫描,后两者扫描都是完成了一个完整的三次握手(即connect函数),而半开扫描则只完成了两次握手就宣告结果,因为仅通过两次握手已读到了要读的数据。
扫描函数的原理就是完成三次握手中的前两次,其中第一次是由扫描器向被扫描的服务器端发送SYN同步数据包,第二次是服务器端回复扫描器ACK确认数据包。按原“计划”,扫描器应该再向服务器发送一个ACK确认包完成三次握手,然而TCP SYN半开扫描在第三次握手时中止该操作或者向服务器端发送FIN结束数据包。
1.使用核心库函数以及核心代码:
#include <pcap.h> #include <iphlpapi.h> #pragma comment(lib,"iphlpapi.lib") #define DEF_BUF_SIZE 100 #define ACK_SYN 1 #define ACK_RST 0 #define ILLEGAL_FLAG -1 #define MAX_FRAME_LEN 1500 //IP地址转换处理 typedef union MultiByteStruct {//IP地址联合体 int iInt; //int有符号整型 float fFloat; //浮点数 UINT uInt; //无符号整数 ULONG uLong; //ULONG无符号整型 DWORD dwDword; //DWORD有符号整型 WORD wWord[2]; //WORD无符号整型数组 UCHAR ucByte[4]; //无符号字符数组 char cByte[4]; //字符数组 }UNIONIP,*PUNIONIP; SYNTHREADPARAM SynThreadParam;//传递给TCP SYNSCAN 线程的参数结构体 static USHORT g_uLocalPort=1240;//绑定的本地端口号 USHORT CPortScan_TCPDlg::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); } CString IPIntToStr(UINT IPInt) { CString IPStr; UNIONIP IP; IP.uInt=IPInt; // IPStr.Format(_T("%d.%d.%d.%d"),IP.ucByte[0],IP.ucByte[1],IP.ucByte[2],IP.ucByte[3]); IPStr.Format(_T("%d.%d.%d.%d"),IP.ucByte[3],IP.ucByte[2],IP.ucByte[1],IP.ucByte[0]); return IPStr; } //char *->CString void CharStrToCString(CString *str,const char *char_str) { int byteLen=strlen(char_str); int Charlen=MultiByteToWideChar(CP_ACP,0,char_str,byteLen,NULL,0); TCHAR *TcharTempItem=new TCHAR[Charlen+1]; MultiByteToWideChar(CP_ACP,0,char_str,byteLen,TcharTempItem,Charlen); TcharTempItem[Charlen]='\0'; *str=TcharTempItem;//***** } void CPortScan_TCPDlg::SynScan_By_WinpCap(UNIONIP uIPBegin,UNIONIP uIPEnd,UINT uPortBegin,UINT uPortEnd) { MessageBox(_T("sys scan by winpcap style...")); PSDHEADER psdHeader; //伪TCP包头 UCHAR *tcp_packet_str=new UCHAR[MIN_TCPPACKET_LEN+1]; PACKET tcp_packet; UINT SrcIp; // 获取本地地址信息 char szLocalName[DEF_BUF_SIZE]={0}; gethostname(szLocalName,DEF_BUF_SIZE); hostent* pHost=gethostbyname(szLocalName); if(pHost!=NULL) memcpy(&SrcIp,pHost->h_addr_list[0],pHost->h_length); else return ; //获取网卡设备句柄 pcap_t *fp; pcap_if_t *d; GetAdapterHandle(&fp,&d); if(!fp) MessageBox(_T("获取网卡设备句柄失败!")); //recv response packet (ACK+SYN) //Theread:监听目标主机(UNIONIP uIPBegin,UNIONIP uIPEnd)的TCP响应数据包 SynThreadParam.fp=fp; SynThreadParam.d=d; SynThreadParam.uIPBegin=uIPBegin; SynThreadParam.uIPEnd=uIPEnd; SynThreadParam.SrcIp=SrcIp; SynThreadParam.ptr=this; CString strIP=IPIntToStr(uIPBegin.uInt); HTREEITEM curr=m_ctlTreeResult.InsertItem(strIP,1,1,TVI_ROOT); SynThreadParam.TreeItem=curr; AfxBeginThread(ThreadRecvTCPSYNPacket,&SynThreadParam,THREAD_PRIORITY_IDLE);//监听返回的ACK+SYN数据包 Sleep(1000);//预留做监听的准备时间 或者设置定时器判断 开始发送数据包的时间 12.16 //end for 12.16 for (UINT ip=uIPBegin.uInt;ip<=uIPEnd.uInt;ip++) { BYTE SrcMac[6]; BYTE DstMac[6]; GetMacAddress(SrcMac);// GetMacAddrOfIP(ip,DstMac);// for(UINT port=m_uPortBegin;port<=m_uPortEnd;port++) { memset(tcp_packet_str,0,MIN_TCPPACKET_LEN+1); memset(&tcp_packet,0,sizeof(PACKET)); //EtherHeader part memcpy(tcp_packet.ethhead.DestMac,DstMac,6); memcpy(tcp_packet.ethhead.SrcMac,SrcMac,6); USHORT type=0x0800;//***以太网帧类型 0x0800 网际协议 *** tcp_packet.ethhead.Type=htons(type); //分别自己填充IP头、TCP头、伪TCP头,并计算其中的校验和 //设置IP头 tcp_packet.ipHeader.h_verlen=0x45; tcp_packet.ipHeader.tos=0; tcp_packet.ipHeader.total_len=htons(sizeof(IPHEADER)+sizeof(TCPHEADER)); tcp_packet.ipHeader.ident=1; tcp_packet.ipHeader.frag_and_flags=0; tcp_packet.ipHeader.ttl=128; tcp_packet.ipHeader.proto=IPPROTO_TCP; tcp_packet.ipHeader.checksum=0; tcp_packet.ipHeader.sourceIP=SrcIp; tcp_packet.ipHeader.destIP=htonl(ip);// //设置TCP头 tcp_packet.translayer.tcpHeader.th_sport=htons(g_uLocalPort); tcp_packet.translayer.tcpHeader.th_dport=htons(port); // tcp_packet.translayer.tcpHeader.th_seq=htonl(SEQ); tcp_packet.translayer.tcpHeader.th_ack=0; tcp_packet.translayer.tcpHeader.th_lenres=(sizeof(TCPHEADER)/4<<4|0); tcp_packet.translayer.tcpHeader.th_flag=2; //2:SYN;1:FIN;16:ACK tcp_packet.translayer.tcpHeader.th_win=htons(512); tcp_packet.translayer.tcpHeader.th_urp=0; tcp_packet.translayer.tcpHeader.th_sum=0; //伪TCP头 psdHeader.saddr=tcp_packet.ipHeader.sourceIP; psdHeader.daddr=tcp_packet.ipHeader.destIP; psdHeader.mbz=0; psdHeader.ptcl=IPPROTO_TCP; psdHeader.tcpl=htons(sizeof(TCPHEADER)); //TCP头校验和 checksum(PsdHeader+TcpHeader) char tempBuf[40]; memcpy(tempBuf,&psdHeader,sizeof(PSDHEADER)); memcpy(tempBuf+sizeof(psdHeader),&tcp_packet.translayer.tcpHeader,sizeof(TCPHEADER)); tcp_packet.translayer.tcpHeader.th_sum=CheckSum((USHORT*)tempBuf,sizeof(PSDHEADER)+sizeof(TCPHEADER)); //*****IP头校验和 checksum(IpHeader) memcpy(tempBuf,&tcp_packet.ipHeader,sizeof(IPHEADER)); tcp_packet.ipHeader.checksum=CheckSum((USHORT*)tempBuf,sizeof(IPHEADER)); //数据包流序列化 memcpy(tcp_packet_str,(UCHAR*)&tcp_packet,sizeof(PACKET)); SetDlgItemInt(IDC_PORT_EDIT,port); if(pcap_sendpacket(fp,tcp_packet_str,sizeof(PACKET)) != 0)//发送数据包 sizeof(PACKET) { MessageBox(_T("pcap_sendpacket()函数执行发生错误!")); return; } } } } //[in] Param :{fp,d,IP,ptr,TreeItem...} UINT ThreadRecvTCPSYNPacket(LPVOID pParam) { //capture,resolve,check 12.16 SYNTHREADPARAM *SynThreadParam=(SYNTHREADPARAM *)pParam; pcap_t *fp=SynThreadParam->fp; pcap_if_t *d=SynThreadParam->d; UNIONIP uIPBegin=SynThreadParam->uIPBegin; UNIONIP uIPEnd=SynThreadParam->uIPEnd; UINT SrcIp=SynThreadParam->SrcIp; CPortScan_TCPDlg *ptr=SynThreadParam->ptr; HTREEITEM curr=SynThreadParam->TreeItem; const BYTE *pkt_data=new BYTE[MAX_FRAME_LEN]; UINT res; struct pcap_pkthdr *header; char *packet_filter_str=new char[100]; char SrcIp_str[16],DstIp_str[16]; SOCKADDR_IN addr_src,addr_dst; struct bpf_program fcode; bpf_u_int32 NetMask; if(d->addresses->netmask) NetMask=((struct sockaddr_in *)d->addresses->netmask)->sin_addr.s_addr; else NetMask=0xffffff; //filter response tcp packets //tcp and ip src host (uIPBegin to uIPEnd) and ip dst host localhost // for (UINT ip=uIPBegin.uInt;ip<=uIPEnd.uInt;ip++) memset(packet_filter_str,0,100); strcpy(packet_filter_str,"tcp and ip src host "); addr_dst.sin_addr.S_un.S_addr=htonl(uIPBegin.uInt);//***only the first destination host(12.16) strcpy(SrcIp_str,inet_ntoa(addr_dst.sin_addr));//SrcIp strcat(packet_filter_str,SrcIp_str); strcat(packet_filter_str," and ip dst host "); addr_src.sin_addr.S_un.S_addr=SrcIp; strcpy(DstIp_str,inet_ntoa(addr_src.sin_addr));//DstIp strcat(packet_filter_str,DstIp_str); if(pcap_compile(fp, &fcode, packet_filter_str, 1, NetMask)<0) { AfxMessageBox(_T("Error compiling filter: wrong syntax.")); return 1; } if(pcap_setfilter(fp, &fcode)<0) { AfxMessageBox(_T("Error setting the filter.")); return 1; } int elapse_times=0; while((res=pcap_next_ex(fp,&header,&pkt_data))>=0) { if(elapse_times>10)//根据超时次数粗略确定已经没有余下的响应数据包 break; if(res==0) { // ptr->MessageBox(_T("获取报文超时!")); elapse_times++; continue; } //resolve received tcp_packet and check relevant bits PACKET *TcpPacket=(PACKET *)pkt_data; int iphead_len=((TcpPacket->ipHeader.h_verlen)&0x0f)*4; TCPHEADER *TcpHeader=(TCPHEADER *)(pkt_data+sizeof(ETHERNET_HEAD)+iphead_len); UCHAR flag=TcpHeader->th_flag; USHORT opened_port=ntohs(TcpHeader->th_sport);//ntohs() 12.16 p.m. CString strTemp; if(((flag&0x10)>>4)&((flag&0x02)>>1))//0 0 URG ACK PSH RST SYN FIN { struct servent *se; CString strNameTemp; se=getservbyport(TcpHeader->th_sport,"tcp"); if(se!=NULL) //get the info of the port and corresponding server name { strTemp.Format(_T("%d "),opened_port); CharStrToCString(&strNameTemp,se->s_name); strTemp+=strNameTemp; } else strTemp.Format(_T("%d"),opened_port); ptr->m_ctlTreeResult.InsertItem(strTemp,2,2,curr); //insert the port node into the tree } } if(res == -1) { AfxMessageBox(_T("Error reading the packets")); return 1; } ptr->MessageBox(_T("Finished SynScan...")); return 1; } void GetMacAddress(BYTE *mac_addr) { IP_ADAPTER_INFO adapter[5]; //Maximum 5 adapters DWORD buflen=sizeof(adapter); DWORD status=GetAdaptersInfo(adapter,&buflen); BYTE s[8]; if(status==ERROR_SUCCESS) { PIP_ADAPTER_INFO painfo=adapter; memcpy(s,painfo->Address,6); for(int i=0;i<6;i++) { mac_addr[i]=s[i]; } } } void GetAdapterHandle(pcap_t **fp,pcap_if_t **d) { pcap_if_t *alldevs; char errbuf[PCAP_ERRBUF_SIZE]; int i,inum;//网卡设备编号 if(pcap_findalldevs(&alldevs, errbuf) == -1) { AfxMessageBox(_T("pcap_findalldevs()函数执行发生错误!")); exit(1); } inum=3;//如何动态获取本地连接网卡 --需改进 for (*d=alldevs, i=0; i< inum-1 ;*d=(*d)->next, i++); if ((*fp = pcap_open_live((*d)->name,65536,1,1000,errbuf)) == NULL) { AfxMessageBox(_T("打开适配器发生错误")); exit(1); } } void GetMacAddrOfIP(UINT ip,BYTE *mac_addr) { //获取网卡设备句柄 pcap_t *fp; pcap_if_t *d; GetAdapterHandle(&fp,&d); if(!fp) AfxMessageBox(_T("获取网卡设备句柄失败!")); // 获取本地地址信息:IP和MAC UINT SrcIP; UINT DstIP=ip;//[in] formal parameter BYTE SrcMac[6]; BYTE DstMac[6]; char szLocalName[DEF_BUF_SIZE]={0}; gethostname(szLocalName,DEF_BUF_SIZE); hostent* pHost=gethostbyname(szLocalName); if(pHost!=NULL) memcpy(&SrcIP,pHost->h_addr_list[0],pHost->h_length); else return ; GetMacAddress(SrcMac); memset(DstMac,0xff,6); //1.构造ARP请求包 //SrcMac SrcIP DstMac DstIP=ip ARP_PACKET arp_request; USHORT type=0x0806; USHORT hardware_type=0x0001; USHORT protocol_type=0x0800; BYTE mac_len=0x06; BYTE ip_len=0x04; USHORT option=0x0001;//1 Request //MAC Frame Head memset(&arp_request,0,sizeof(ARP_PACKET)); memcpy(arp_request.EthHead.DestMac,DstMac,6); memcpy(arp_request.EthHead.SrcMac,SrcMac,6); arp_request.EthHead.Type=htons(type); //ARP Request Head arp_request.ArpFrame.hardware_type=htons(hardware_type); arp_request.ArpFrame.protocol_type=htons(protocol_type); arp_request.ArpFrame.add_len=mac_len; arp_request.ArpFrame.pro_len=ip_len; arp_request.ArpFrame.option=htons(option); memcpy(arp_request.ArpFrame.sour_addr,SrcMac,6); arp_request.ArpFrame.sour_ip=SrcIP;//SrcIP: net sequence memcpy(arp_request.ArpFrame.dest_addr,DstMac,6); arp_request.ArpFrame.dest_ip=htonl(DstIP);//DstIP UINT:host sequence memset(arp_request.ArpFrame.padding,0x00,18); if(pcap_sendpacket(fp,(BYTE *)&arp_request,sizeof(arp_request)) != 0) { AfxMessageBox(_T("pcap_sendpacket()函数执行发生错误!")); return; } //2.recv arp response packet const BYTE *pkt_data; UINT res; struct pcap_pkthdr *header; char *packet_filter_str=new char[100]; char mac_str[18]; char ip_str[16]; struct bpf_program fcode; bpf_u_int32 NetMask; if(d->addresses->netmask)//掩码不为空 NetMask=((struct sockaddr_in *)d->addresses->netmask)->sin_addr.s_addr; else NetMask=0xffffff;//默认为 C类网络 GetMacStrFromByte(SrcMac,mac_str);//MAC Str SOCKADDR_IN addr; addr.sin_addr.S_un.S_addr=htonl(DstIP);//************下面的innet_ntoa()参数是--网络上--的一个IP地址************ strcpy(ip_str,inet_ntoa(addr.sin_addr));//IP Str //packet_filter_str="arp and ether dst str(SrcMac) and src host str(DstIP)" //packet_filter_str += arp_request.ArpFrame.option=0x0002 memset(packet_filter_str,0,100); strcpy(packet_filter_str,"arp and ether dst "); strcat(packet_filter_str,mac_str); strcat(packet_filter_str," and src host "); strcat(packet_filter_str,ip_str); if(pcap_compile(fp, &fcode, packet_filter_str, 1, NetMask)<0) { AfxMessageBox(_T("Error compiling filter: wrong syntax.")); return; } if(pcap_setfilter(fp, &fcode)<0) { AfxMessageBox(_T("Error setting the filter.")); return ; } /* Capture data */ while((res = pcap_next_ex(fp,&header,&pkt_data))>=0) { if(res == 0)/* Timeout elapsed */ continue; if(pkt_data) break; } if(res == -1) { AfxMessageBox(_T("Error reading the packets")); return; } //3.resolve response arp arp packet of sour_addr attribute //const BYTE * pkt_data -->SrcMac ETHERNET_HEAD *Ether_Header=(ETHERNET_HEAD *)pkt_data; memcpy(mac_addr,Ether_Header->SrcMac,6); } void GetMacStrFromByte(BYTE *SrcMac,char *mac_str) { /* In: SrcMac={00,30,18,A3,D2,8E}; Out: mac_str="00:30:18:A3:D2:8E" */ char *MacStr=new char[18]; memset(MacStr,0,18); for(int i=0;i<6;i++) { MacStr[3*i]=GetHexChar((SrcMac[i]&0xf0)>>4); MacStr[3*i+1]=GetHexChar(SrcMac[i]&0x0f); if(i<=4) MacStr[3*i+2]=':'; } strcpy(mac_str,MacStr); } char GetHexChar(BYTE HexNum) { if(HexNum>=0&&HexNum<=9) return '0'+HexNum; else if(HexNum>=0xA&&HexNum<=0xF) return 'A'+HexNum-0xA; else { AfxMessageBox(_T("Illegal HexNum!")); return 0; } }
(1)
TCP SYN扫描结果:发现目标主机出现超时重ACK+SYN 数据包的现象
第一个重传(TCP Retransmission)的数据包是来自目标主机的端口netbios-ssn(139)的数据包
使用Wireshark工具得到该数据包的SEQ/ACK分析:判断为可疑的重传数据包
(2)
添加端口对应的服务名称之后,最终的运行结果:
(3)使用nmap扫描工具进行对比:
3.忽略掉的问题
只扫描了知名端口(1-1024);
没有动态识别本地连接对应的网卡;
没有对重传数据包的判定;
只考虑了对单一主机的扫描,没有考虑多个目标主机的情况;
获取的端口服务名只是使用getservbyport()从本地配置文件读取;
接收TCP响应包的线程函数的退出仅仅根据超时次数,没有恰当处理退出的条件;
其它
......
4.定义的相关数据结构
#pragma pack (1) typedef struct SYNThreadParamStruct { pcap_t *fp; pcap_if_t *d; UNIONIP uIPBegin; UNIONIP uIPEnd; UINT SrcIp; CPortScan_TCPDlg *ptr; HTREEITEM TreeItem; }SYNTHREADPARAM; //以太网帧头 typedef struct _ETHERNET_HEAD { BYTE DestMac[6];//目的MAC地址 BYTE SrcMac[6];//源MAC地址 USHORT Type;//协议类型 IP(0x0800) ARP(0x0806是ARP帧的类型值) RARP }ETHERNET_HEAD;//14Bytes typedef struct _ARP_HEADER { //固定头长度:28Bytes USHORT hardware_type; //硬件类型:以太网接口类型为1 USHORT protocol_type; //协议类型:IP协议类型为0X0800 BYTE add_len; //硬件地址长度:MAC地址长度为6B BYTE pro_len; //协议地址长度:IP地址长度为4B USHORT option; //操作:ARP请求为1,ARP应答为2 BYTE sour_addr[6]; //源MAC地址:发送方的MAC地址 UINT sour_ip; //源IP地址:发送方的IP地址 BYTE dest_addr[6]; //目的MAC地址:ARP请求中该字段没有意义;ARP响应中为接收方的MAC地址 UINT dest_ip; //目的IP地址:ARP请求中为请求解析的IP地址;ARP响应中为接收方的IP地址 BYTE padding[18]; //填充字符长度=以太网最小帧长(64Bytes)-MAC帧头(14Bytes)-ARP包固定长度(28Bytes)-帧校验序列FCS(4Bytes) }ARP_HEADER,*PARP_HEADER; // ARP Packet = ETHERNET_HEAD + ARP_HEADER typedef struct _ARP_PACKET { ETHERNET_HEAD EthHead; ARP_HEADER ArpFrame; }ARP_PACKET,*PARP_PACKET; typedef struct _iphdr { unsigned char h_verlen; //4位IP版本号+4位首部长度 unsigned char tos; //8位服务类型TOS unsigned short total_len; //16位总长度(字节) unsigned short ident; //16位标识 标记当前分片为第几个分片 unsigned short frag_and_flags; //3位标志位+13位片偏移 unsigned char ttl; //8位生存时间 TTL unsigned char proto; //8位协议(TCP、UDP或其他协议) unsigned short checksum; //16位IP首部校验和 unsigned int sourceIP; //32位源IP地址 unsigned int destIP; //32位目的IP地址 }IPHEADER; //TCP伪首部的作用主要是进行校验和的计算 typedef struct psd_hdr //定义TCP伪首部 12Bytes { unsigned long saddr; //源地址 unsigned long daddr; //目的地址 char mbz; char ptcl; //协议类型 unsigned short tcpl; //TCP长度 }PSDHEADER; typedef struct _tcphdr //定义TCP首部 { USHORT th_sport; //16位源端口 USHORT th_dport; //16位目的端口 unsigned int th_seq; //32位序列号 unsigned int th_ack; //32位确认号 unsigned char th_lenres; //4位首部长度/6位保留字 unsigned char th_flag; //6位标志位 USHORT th_win; //16位窗口大小 USHORT th_sum; //16位校验和 USHORT th_urp; //16位紧急数据偏移量 }TCPHEADER; //数据包 typedef struct _packet { ETHERNET_HEAD ethhead; IPHEADER ipHeader; union Translayer { TCPHEADER tcpHeader; UDPHEADER udpHeader; ICMPHEADER icmpHeader; }translayer; //char padding[10];//填充字符 }PACKET; #pragma pack()