网络安全程序设计报告.docx
《网络安全程序设计报告.docx》由会员分享,可在线阅读,更多相关《网络安全程序设计报告.docx(19页珍藏版)》请在冰豆网上搜索。
网络安全程序设计报告
基于原始Socket的网络嗅探器
1.概述
本程序使用原始Socket来捕获网络内的数据包,通过将本机的网卡设置为混杂模式,来使得网卡接收网络内的所有数据包,从而将其捕获。
然后,将捕获到的数据包进行拆解,得到数据包内部的信息。
本系统目前支持TCP、UDP、ICMP包的解析。
1.1程序的开发环境
使用的编程语言:
C++
使用的开发工具:
VisualC++6.0
1.2小组成员及分工
2.背景知识
2.1网卡的混杂模式
网卡的工作模式有正常模式和混杂模式两种。
在正常模式下,网卡只接收目的地址是本机的数据包,其他的数据包全部丢弃。
而网卡在混杂模式下,则接收所有经过网卡是数据包。
也就是说,混杂模式时网卡接收所有通过它的数据流,不论是什么格式,什么地址。
具体的地址转发则是在接收到数据后由MAC层来进行。
通常在设计捕获数据包的程序时,均需要将网卡设置为混杂模式。
2.2常用数据报的格式
2.2.1IP数据报
IP数据报是分布于网络层的数据包,提高对网络中主机的地址定位操作。
其中包含了重要的数据信息,包括发送数据包的主机,即源IP地址,接收数据包的主机,即目的IP地址。
以及其他一些IP地址相关的的附加信息。
IP数据报的格式如下:
版本
报文长度
服务类型
总长度
标识符
标志
片偏移
生存时间
协议
报头校验和
源IP地址
目的IP地址
IP选项
2.2.2TCP数据报
TCP向应用程序提供可靠的数据传输服务。
TCP数据报由TCP首部和TCP数据部分组成。
其TCP首部的格式如下:
2.2.3UDP数据报
UDP向应用程式提供无连接的数据传输。
虽然UDP是数据传输可靠性不如TCP,但其资源占用少,传输速度快,使得其在某些场合使用十分适合。
例如,传输零星数据,简单的文本传输等。
UDP数据报的首部仅仅有4个16位的字段,它们分别是UDP源端口,UDP目的端口,UDP数据报长度及校验和。
UDP头部格式:
UDP源端口号(16位)
UDP目的端口号(16位)
UDP长度(16位)
UDP校验和(16位)
2.2.4ICMP数据报
ICMP是传输控制信息的报文,例如差错信息控制,目的地是否可达的信息等。
标准的ICMP报文只有4个字节,但是在其多种控制报文中又有4个字节的控制信息,因此在ICMP的报文头部中应该加入该字段。
ICMP头部的格式
类型(8位)
代码(8位)
校验和(16位)
控制信息(32位)
3.程序流程图
3.1程序总体流程图
图3.1程序主体流程图
3.2程序创建套接字部分流程图
图3.2创建套接字流程图
3.3程序拆包部分流程图
图3.3拆包部分流程图
4.程序实现
4.1程序中的重要函数
4.1.1创建原始套接字
m_Sock=socket(AF_INET,SOCK_RAW,IPPROTO_IP);
socket()函数用于创建原始套接字,其中相关的参数说明如下:
AF_INET:
设置套接字地址家族为TCP/IP地址家族
SOCK_RAW:
设置套接字为原始套接字
IPPROTP_IP:
设置套接字接收数据包,为IP层的数据包
4.1.2设置套接字的工作方式,为混杂模式
WSAIoctl(m_Sock,SIO_RCVALL,&inBuffer,sizeof(inBuffer),&outBuffer,sizeof(outBuffer),&reValue,NULL,NULL)
WSAIoctl()函数用于设置网卡的工作模式位混杂模式,在此模式下网卡可以接受所经过网卡的数据包。
WSAIoctl()函数的相关参数说明如下:
m_Sock:
传入参数,标识一个套接字
SIO_RCVALL:
网卡的工作模式,混杂模式
&inBuffer:
设置输入的缓冲区
Sizeof(inBuffer):
输入缓冲区的大小
&outBuffer:
设置输出缓冲区
Sizeof(outBuffer):
输出缓冲区的大小
&revalue:
传出参数,指向函数实际返回的字节数的地址
NULL:
传入参数,WSAOVERLAPPED结构的地址
NULL:
传入参数,一个指向操作结束调用的例程指针。
4.1.3接收数据包
ret=recv(pDlg->m_Sock,buffer,1000,0)
recv()函数用于接收经过网卡的数据包。
其相关参数说明如下:
pDlg->m_Sock:
接收使用的特定套接字
buffer:
缓冲区
1000:
缓冲区的大小
0:
其接收模式为正常接收
4.2程序中的重要数据结构
4.2.1IP头部结构
typedefstructHeadIP{
unsignedcharheaderlen:
4;
unsignedcharversion:
4;
unsignedcharservertype;
unsignedshorttotallen;
unsignedshortid;
unsignedshortidoff;
unsignedcharttl;
unsignedcharproto;
unsignedshortchecksum;
unsignedintsourceIP;
unsignedintdestIP;
}HEADIP;
相关参数说明如下:
headerlen:
首部长度,占4位
version:
版本号,占4位
severtype:
服务类型,占8位
totallen:
总长度,占16位
id:
标识
idoff:
与上面的id共同构成标识,共占16位
ttl:
生存时间,占8位
proto:
协议,占8位
checksum:
首部校验和,占16位
sourceIP:
源IP地址,占32位
destIP:
目的IP地址,占32位
4.2.2TCP头部结构
typedefstructHeadTCP{
WORDSourcePort;
WORDDePort;
DWORDSequenceNo;
DWORDConfirmNo;
BYTEHeadLen;
BYTEFlag;
WORDWndSize;
WORDCheckSum;
WORDUrgPtr;
}HEADTCP;
相关参数说明如下:
SourcePort:
源端口号,占16位
DePort:
目的端口号,占16位
SequenceNO:
序号,占32位
ConfirmNO:
确认序号,32位
HeadLen:
首部长度
Flag:
与HeadLen组成一个部分,占16位
WndSize:
窗口大小,占16位
Checksum:
校验和,占16位
UrgPtr:
紧急指针,占16位
4.2.3UDP头部结构
typedefstructHeadUDP{
WORDSourcePort;
WORDDePort;
WORDLen;
WORDChkSum;
}HEADUDP;
相关参数说明如下:
SourcePort:
源端口号,占16位
DePort:
目的端口号,占16位
Len:
UDP长度,占16位
Chksum:
UDP校验和,占16位
4.2.4ICMP头部结构
typedefstructHeadICMP{
BYTEType;
BYTECode;
WORDChkSum;
IntControl;
}HEADICMP;
相关参数说明:
Type:
类型,占8位
Code:
代码,占8位
Chksum:
校验和,占16位
Control:
控制信息,占32位
4.2.5协议封装结构
structPROTONAME{
intvalue;
char*protoname;
};
相关参数说明如下:
Value:
协议对于的常量值
Protoname:
协议的名称
5.程序界面
5.1程序主界面
图5.1程序主界面
程序主界面上显示了如下信息:
一个列表框,显示了如下内容:
协议名称,源IP地址,目的IP地址,源端口,目的端口,数据包大小,以及数据。
两个按钮:
开始监听按钮:
单击按钮开始监听网卡上的数据包
取消按钮:
单击按钮停止监听,并推出程序
5.2程序运行界面
图5.2程序运行界面
6.程序功能结构图
图6.1程序功能结构图
7.程序主体函数
7.1初始化套接字AfxSocketInit()
WSADATAdata;
AfxSocketInit(&data);
AfxSocketInit()函数加载套接字,可以在程序推出时,自动关闭套接字。
7.2监听函数OnBeginlisten()
voidCSniffAppDlg:
:
OnBeginlisten()
{
//创建套接字,原始套接字
m_Sock=socket(AF_INET,SOCK_RAW,IPPROTO_IP);
charname[128];
memset(name,0,128);
hostent*phostent;//hostent标识主机信息的结构体,gethostbyname的返回值类型
phostent=gethostbyname(name);
DWORDip;
ip=inet_addr(inet_ntoa(*(in_addr*)phostent->h_addr_list[0]));//inet_addr将点分十进制的IP转换为网络字节序
//inet_ntoa将网络字节序的IP转换为点分十进制的IP,
inttimeout=4000;//超时4秒
//设置接收数据的超时时间
setsockopt(m_Sock,SOL_SOCKET,SO_RCVTIMEO,(constchar*)&timeout,sizeof(timeout));
sockaddr_inskaddr;
skaddr.sin_family=AF_INET;
skaddr.sin_port=htons(700);
skaddr.sin_addr.S_un.S_addr=ip;
//绑定地址
if(bind(m_Sock,(sockaddr*)&skaddr,sizeof(skaddr))==SOCKET_ERROR)
{
MessageBox("地址绑定错误");
return;
}
DWORDinBuffer=1;
DWORDoutBuffer[10];
DWORDreValue=0;
//WSAIoctl设置套接口的工作方式,SIO_RCVALL为混杂模式
if(WSAIoctl(m_Sock,SIO_RCVALL,&inBuffer,sizeof(inBuffer),&outBuffer,sizeof(outBuffer),&reValue,NULL,NULL)==SOCKET_ERROR)
{
MessageBox("设置缓冲区错误.");
closesocket(m_Sock);
return;
}
else
m_pThread=AfxBeginThread(ThreadFun,(void*)this);//启动接收线程
}
OnBeginListen()函数响应“开始监听”按钮的消息,执行监听功能。
7.3拆包线程函数ThreadFun()
UINTThreadFun(LPVOIDpParam)
{
CSniffAppDlg*pDlg=static_cast(pParam);
MSGmsg;//PeekMessage消息的参数
charbuffer[1000],sourceip[32],*tempbuf;//设置缓冲区和源iP
char*ptemp;
BYTE*pData=NULL;//实际数据报中的数据
UINTsourceport;
UINTdeport;//增加的目的端口
CStringstr;//向列表中增加数据时,格式化非法数据
HEADIP*pHeadIP;
HEADICMP*pHeadICMP;
HEADUDP*pHeadUDP;
HEADTCP*pHeadTCP;
in_addraddr;//该结构体表示一个32位的IP地址
intret;//错误检测函数的返回值
while(TRUE)
{
pData=NULL;
//接收主窗体发来的消息,来退出死循环
if(PeekMessage(&msg,pDlg->m_hWnd,WM_CLOSE,WM_CLOSE,PM_NOREMOVE))
{
closesocket(pDlg->m_Sock);
break;
}
memset(buffer,0,1000);
ret=recv(pDlg->m_Sock,buffer,1000,0);//recv为有连接的接收数据函数,0为接收模式,正常接收
if(ret==SOCKET_ERROR)
{
continue;
}
else//接收到数据
{
tempbuf=buffer;
pHeadIP=(HEADIP*)tempbuf;//提取IP头信息
//获取数据报总长度
//WORDlen=ntohs(pHeadIP->totallen);//2字节网络字节序转化为主机字节序
//获取源IP
pDlg->m_List.InsertItem(pDlg->m_List.GetItemCount(),"");
addr.S_un.S_addr=pHeadIP->sourceIP;
ptemp=inet_ntoa(addr);
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,1,ptemp);
//获取目的IP
addr.S_un.S_addr=pHeadIP->destIP;
ptemp=inet_ntoa(addr);
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,2,ptemp);
//获取协议名称
ptemp=get_protoname(pHeadIP->proto);
strcpy(sourceip,ptemp);
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,0,sourceip);
//获取IP数据报总长度
WORDipSumLen=ntohs(pHeadIP->totallen);
//IP数据报头总长度
intipHeadLen=20;//IP头定义为20字节
//获得去除IP层数据的长度
WORDnetlen=ipSumLen-ipHeadLen;
//根据不同大协议获得不同协议的数据
switch(pHeadIP->proto)
{
caseIPPROTO_ICMP:
{
pHeadICMP=(HEADICMP*)(tempbuf+20);//Ip头为20字节
pData=(BYTE*)(pHeadICMP)+4;//ICMP数据报头共4个字节
//获取数据的长度
netlen-=4;
break;
}
caseIPPROTO_UDP:
{
pHeadUDP=(HEADUDP*)(tempbuf+20);
pData=(BYTE*)pHeadUDP+8;//UDP数据报头共8个字节
sourceport=ntohs(pHeadUDP->SourcePort);
str.Format("%d",sourceport);
//设置源端口
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,3,str);
str.Empty();
deport=ntohs(pHeadUDP->DePort);
str.Format("%d",deport);
//设置目的端口
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,4,str);
str.Empty();
netlen-=8;
break;
}
caseIPPROTO_TCP:
{
pHeadTCP=(HEADTCP*)(tempbuf+20);
sourceport=ntohs(pHeadTCP->SourcePort);
pData=(BYTE*)pHeadTCP+20;//TCP数据报头共20个字节
str.Format("%d",sourceport);
//设置源端口
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,3,str);
str.Empty();
str.Format("%d",deport);
//设置目的端口
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,4,str);
str.Empty();
netlen-=20;
break;
}
}
//设置数据大小
str.Format("%d",netlen);
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,5,str);
str.Empty();
//设置数据
if(pData!
=NULL)
{
str.Format("%s",pData);
pDlg->m_List.SetItemText(pDlg->m_List.GetItemCount()-1,6,str);
}
str.Empty();
pDlg->ShowInfo();
}
}
return0;
ThreadFun()函数用于接收网卡上的数据包,并进行拆包,将其中的消息提取出来。
以显示在列表框中。
7.4获取协议函数get_protoname()
PROTONAMEprotos[11]={
{IPPROTO_IP,"IP"},
{IPPROTO_ICMP,"ICMP"},
{IPPROTO_IGMP,"IGMP"},
{IPPROTO_GGP,"GGP"},
{IPPROTO_TCP,"TCP"},
{IPPROTO_PUP,"PUP"},
{IPPROTO_UDP,"UDP"},
{IPPROTO_IDP,"IDP"},
{IPPROTO_ND,"ND"},
{IPPROTO_RAW,"RAW"},
{IPPROTO_MAX,"MAX"}
};
char*get_protoname(intprotoID)
{
for(inti=0;i<11;i++)
if(protoID==protos[i].value)
{
returnprotos[i].protoname;
}
return"";
}
get_protoname()函数用于获取数据包协议的名称,从而按照特定的方式解析协议。
而protos[]数组则是封装了常见的网络协议。
包括IP、TCP、UDP、ICMP等。
8.程序设计心得
本次程序设计是一个简单的网络嗅探器。
这个嗅探器是基于原始套接字的。
其能捕获网络层的数据包,并且通过拆包来获取其中的包内容信息。
程序设计的过程中,遇到了一下困难,其中一个就是拆包部分的问题。
数据指针如何提取数据包中的数据,又如何从控制数据指针的移动,找到相应的数据包的其他部分的信息。
后来,通过对该部分进行一下合理的算法设计,得到了一个好的拆包方法。
这样这个问题才得到解决。
此外,还遇到了其他许多问题,在小组成员的共同努力下,问题都最终得到了解决。
当然,程序中还是存在很多不足,例如,执行拆包功能的线程不能随时终止,要停线程,必须退出程序。
可能,其中还存在有未知的bug和问题,这些待进一步的完善。
另外,程序的功能上还是不够。
可以进一步的扩充,例如增加对其他数据包解析的支持,增加解析数据的保持功能,增加对数据的分析功能等。
总之,程序还是完成了基本的功能,其中的不足待在进一步的学习中来提高和完善。