网络协议信息隐藏
1.实验目的
(1)掌握网络层信息隐藏的原理
(2)理解在网络层两种主要协议上进行信息隐藏的基本方法
2.实验内容
(1)利用IP/ICMP协议进行信息隐藏
(2)利用TCP协议进行信息隐藏
3.实验环境
(1)网络环境:100Mbps 交换式以太网,2 台主机,其中包括发送端(192.168.0.1/24)与接收端(192.168.0.2/24)
(2)操作系统:Windows 2003 或内核 2.4.20 以上版本的 Linux(如 Kali Linux 2.0)
(3)编程工具:Microsoft Visual C++ 6.0 以上,WinPcap 开发包(版本 3.0 以上)
4.实验原理
注:代码仅供参考
(1)基于网络层协议的信息隐藏
目前的网络协议使用的 IPV4 版本,设计时候存在漏洞,首部存在冗余或可选字段,网络设备对某些字段限制过于宽松,通过精心设计和构造,可以利用这些字段进行信息隐藏以实现隐蔽通信。这种通信不增加额外带宽,很难被网络防火墙和入侵检测系统检测到,容易逃避网络监控,实现信息隐藏的目的。传统信息隐藏的载体是静态的多媒体数据,而网络隐蔽通道的载体是动态的网络协议的首部,这种载体上的区别是两者最根本的区别;前者依赖于人的视觉或听觉不敏感性,而后者是基于网络协议在语法或语义上的冗余;前者的隐匿性主要对于人感官上的不可感知,而后者的隐匿性是对于网络监控设备而言的。多媒体数据中存在大量的信息冗余,网络协议数据包中的冗余显然要少许多;多媒体有着复杂的数据结构,任取其中的一个数据(像素、视频帧等)进行数值改写,几乎不会对它的感官效果产生影响,而网络协议的数据包中的各个首部字段都是最简单的“01”比特串,对首部字段取值的改写不但彻底改变了数据包的类型,而且有可能使得这个数据包由于畸形而被丢弃。网络协议信息隐藏(以下简称协议隐写)是一种利用数据包作为掩护载体,将秘密信息隐匿在网络协议的数据包之中的信息隐藏技术,它可以通过网络协议数据包中的保留、可选、未定义等字段和数据包的顺序、数量、到达时间、特定时间流量以及其它可被利用的特征,在网络中不同的主机之间建立隐蔽通信。
利用协议隐写进行隐蔽通信时,发送端在协议数据包中使用嵌入算法嵌入秘密信息,得到携密数据包,可将隐蔽通信划分为 6 种模式,其中有效的方式如下图 A、B 两个。

图4-1 隐蔽通信模式示意图

图4-2 网络隐蔽通道的基本工作模式

图4-3 IP的首部

图4-4 ICMMP回显请求和回显应答的报文格式
(2)利用TCP协议进行信息隐藏
在传输层中,TCP和UDP都是用相同的网络层,TCP向应用层提供一种面向连接的、可靠的字节流服务,而UDP提供的是无连接的、不可靠的字节流服务。在TCP和UDP上都可以进行信息隐藏,TCP首部格式如4-5所示。

图4-5 TCP的首部

图4-6 正常TCP的三次握手
- 请求端发送一个SYN段指名请求端打算连接的服务器的端口以及ISN;
- 服务器发回包含服务器ISN的SYN报文段作为应答,同时将确认号设置为请求段的ISN加1以对请求端的SYN报文段进行确认;
- 请求端必须将确认号设置为服务器的ISN加1对服务器的SYN报文段进行确认。
利用TCP数据包的32位序列号隐藏信息的方法原理如图4-7所示。

图4.2-3 利用32位序列号实现协议隐写
5.实验记录
(1)基于网络层协议的信息隐藏
1)算法描述
实验的主要思路是在网络层实现信息发送及接收程序,由于在底层实现该程序,需要手工构造 IP 报文的首部字段来自定义发送数据包,因此必须使用 Raw Socket 编程。Raw Socket 允许程序绕过系统内核而直接访问底层协议,因此 IP 层的封装工作就要用手工填充数据的方法实现,而不是由操作系统自动完成。由于 Raw Socket 经常被用来编写网络扫描程序等恶意软件,微软在 Windows XP 的协议驱动 tcpip.sys 中,基于安全考虑已经对利用 Raw Socket 发送数据包进行了限制,故本文中的底层数据包发送程序在Windows 2000 下通过 VC6.0 实现。由于使用原始套接字发送数据包,数据包的某些字段还被用来隐藏信息,接收端不能用普通的 Recv( )或 Recvfrom( )来接收数据包,只能采用嗅探的方式接收发送方的数据。但网络中的数据包很多,这又会产生识别特定数据包的问题,在程序实现时,除在接收端根据源 IP 地址、目的 IP 地址、协议等字段设置规则进行过滤外,还在发送端对 IP 标志的最高位进行了置位,只有符合这些规则的数据包才会被接收。流式套接字编程对上层应用提供了可靠的服务,而使用 Raw Socket 发送数据包则没有这样的保证,容易造成丢包等情况,下文的原型程序仅为验证隐蔽通信的可行性,没有考虑对乱序、丢包、数据错误的处理。
2)数据结构
// 协议
#define PROTO_ICMP 1
typedef struct _IPHeader // 20 字节的 IP 头
{
UCHAR iphVerLen; // 版本号和头长度(各占 4 位)
UCHAR ipTOS; // 服务类型
USHORT ipLength; // 数据包总长度,即整个 IP 报的长度
USHORT ipID; // 数据包标识,唯一标识发送的每一个数据报
USHORT ipFlags; // 标志
UCHAR ipTTL; // 生存时间,就是 TTL
UCHAR ipProtocol; // 协议,可能是 TCP、UDP、ICMP 等
USHORT ipChecksum; // 校验和
ULONG ipSource; // 源 IP 地址
ULONG ipDestination; // 目标 IP 地址
} IPHeader, *PIPHeader;
typedef struct _ICMPHeader //定义 ICMP 报头(回显请求与回显应答)
{
unsigned char i_type;//8 位类型
unsigned char i_code; //8 位代码
unsigned short i_cksum; //16 位校验和
unsigned short i_id; //识别号(一般用进程号作为识别号)
unsigned short i_seq; //报文序列号
unsigned int timestamp;//时间戳
}ICMP_HEADER;
3)算法实现
a)嵌入算法:
//计算IP包头的校验和
USHORT 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);
}
//检查输入是否合法
BOOL CIPCovertSenderDlg::CheckValidIP()
{
BOOL bOK;
bOK=FALSE;
if (!IPCtrlToSTR(&m_SrcAddress))
MessageBox("Please enter a valid source address!");
else if (!IPCtrlToSTR(&m_DestAddress))
MessageBox("Please enter a valid destination address!");
else if (m_TypeOfService==-1)
MessageBox("Please enter a valid type of service!");
else if (m_FragmentationType==-1)
MessageBox("Please enter a valid fragmentation size!");
else
bOK=TRUE;
return bOK;
}
//将IP地址转换为字符串类型
LPSTR CIPCovertSenderDlg::IPCtrlToSTR(CIPAddressCtrl* ctrl)
{
BYTE bOctet1;
BYTE bOctet2;
BYTE bOctet3;
BYTE bOctet4;
int iBlank;
iBlank=ctrl->GetAddress(bOctet1,bOctet2,bOctet3,bOctet4);
if (iBlank!=4)
return NULL;
else
{
in_addr iAddr;
iAddr.S_un.S_un_b.s_b1=bOctet1;
iAddr.S_un.S_un_b.s_b2=bOctet2;
iAddr.S_un.S_un_b.s_b3=bOctet3;
iAddr.S_un.S_un_b.s_b4=bOctet4;
return inet_ntoa(iAddr);
}
}
//发送数据包
void CIPCovertSenderDlg::OnSendIp()
{
UpdateData(TRUE);
DWORD szDestIp;
((CIPAddressCtrl*)GetDlgItem(IDC_DestAddress))->GetAddress(szDestIp);
DWORD szSourceIp;
((CIPAddressCtrl*)GetDlgItem(IDC_SrcAddress))->GetAddress(szSourceIp);
char szMsg[] = "This is a test \r\n";
int nMsgLen = strlen(szMsg);
//创建原始套接字
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
//设置IP首部包含选项
BOOL bIncl = TRUE;
::setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)&bIncl, sizeof(bIncl));
char buff[1024] = { 0 };
//开始填充IP首部
IPHeader *pIphdr = (IPHeader *)buff;
pIphdr->iphVerLen = (m_Version<<4 | m_HeaderSize);
pIphdr->ipLength = m_TotalLenth;
pIphdr->ipTTL = m_TTL;
pIphdr->ipProtocol = m_Protocol;
pIphdr->ipSource = htonl(szSourceIp);
pIphdr->ipDestination = htonl(szDestIp);
pIphdr->ipChecksum = m_CheckSum;
pIphdr->ipTOS = m_TypeOfService;
pIphdr->ipFlags = htons((USHORT)(m_FragmentationType<<13 | m_Offset));
pIphdr->ipID = htons(m_Identification);
//开始填充ICMP首部
ICMP_HEADER *pIcmphdr = (ICMP_HEADER *)&buff[sizeof(IPHeader)];
pIcmphdr->i_type = m_ICMP_Type;
pIcmphdr->i_code = m_ICMP_Code;
pIcmphdr->i_cksum = htons(m_ICMP_Checksum);
pIcmphdr->i_id = htons(m_ICMP_ID);
pIcmphdr->i_seq = htons(m_ICMP_Sequence);
pIcmphdr->timestamp = htonl(m_ICMP_Timestamp);
char *pData = &buff[sizeof(IPHeader) + sizeof(ICMP_HEADER)];
memcpy(pData, szMsg, nMsgLen);
// 设置目的地址
SOCKADDR_IN destAddr = { 0 };
destAddr.sin_family = AF_INET;
destAddr.sin_port = 0;
destAddr.sin_addr.S_un.S_addr = htonl(szDestIp);
// 发送原始ICMP数据包
int nRet,j=0;
CString str;
for(int i=0; i<num_of_packet; i++,j=j+1)//+11)
{
pIcmphdr->i_code = filebuff[j];
pData = &buff[sizeof(IPHeader) + sizeof(ICMP_HEADER)];
memcpy(pData, szMsg, nMsgLen);
nRet = ::sendto(sRaw, buff, sizeof(IPHeader) + sizeof(ICMP_HEADER) + nMsgLen,
0, (sockaddr*)&destAddr, sizeof(destAddr));
if(nRet == SOCKET_ERROR)
{
AfxMessageBox(" sendto() failed.");
break;
}
}
str.Format("%d",nRet);
str="成功发送 "+str+" 字节!";
AfxMessageBox(str);
::closesocket(sRaw);
}
//打开文件
void CIPCovertSenderDlg::OnOpenFile()
{
CFileDialog m_ldFile(TRUE);
// 显示打开文件对话框,并捕捉结果
if(m_ldFile.DoModal()==IDOK)
{
// 捕获所选文件名
m_sFilePathName=m_ldFile.GetPathName();
m_sFileName=m_ldFile.GetFileName();
// 更新对话框
UpdateData(FALSE);
// 按钮可用
GetDlgItem(ID_SEND_IP)->EnableWindow(TRUE);
}
UpdateData(FALSE);
FillData();
}
//填充读取的数据到缓存
void CIPCovertSenderDlg::FillData()
{
FILE *fpin;
CString str;
int fh,ch,i,filelength;
// 用户输入更新变量
UpdateData(TRUE);
if((fpin=fopen(m_sFilePathName,"rb"))==NULL)
{
str="打开文件 "+m_sFileName+" 失败";
MessageBox(str);
exit(0);
}
fh = _open( m_sFilePathName, _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE );
filelength=_filelength( fh );
if(filelength>1024)
{
str="文件 "+m_sFileName+" 长度大于1024!";
MessageBox(str);
exit(0);
}
BeginWaitCursor();
ch=fgetc(fpin);
i=0;
while(ch!=-1)
{
filebuff[i++]=ch;
ch=fgetc(fpin);
}
fclose(fpin);
num_of_packet=filelength;
}
填写相应的源ip和目的ip,版本号,首部长度等字节

图5-1 隐藏信息代码运行结果

图5-2 新建隐藏文件

图5-3 成功发送隐藏数据
b)提取算法
typedef struct _tagPROTOMAP
{
int ProtoNum;
char ProtoText[MAX_PROTO_TEXT_LEN];
}PROTOMAP;
static PROTOMAP ProtoMap[MAX_PROTO_NUM]=
{
{ 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 , "NP " },
{ IPPROTO_RAW , "RAW " },
{ IPPROTO_MAX , "MAX " },
{ NULL , "" }
};
typedef struct _iphdr //定义IP首部
{
unsigned char h_verlen; //4位首部长度,4位IP版本号
unsigned char tos; //8位服务类型TOS
unsigned short total_len; //16位总长度(字节)
unsigned short ident; //16位标识
unsigned short frag_and_flags; //3位标志位
unsigned char ttl; //8位生存时间 TTL
unsigned char proto; //8位协议 (TCP, UDP 或其他)
unsigned short checksum; //16位IP首部校验和
unsigned int sourceIP; //32位源IP地址
unsigned int GivenIP; //32位目的IP地址
}IP_HEADER;
typedef struct _icmphdr //定义ICMP报头(回送与或回送响应)
{
unsigned char i_type;//8位类型
unsigned char i_code; //8位代码
unsigned short i_cksum; //16位校验和
unsigned short i_id; //识别号(一般用进程号作为识别号)
unsigned short i_seq; //报文序列号
unsigned int timestamp;//时间戳
}ICMP_HEADER;
USHORT 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);
}
struct //定义TCP伪首部
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl; //协议类型
unsigned short tcpl; //TCP长度
}psd_header;
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位紧急数据偏移量
}TCP_HEADER;
typedef struct _udphdr //定义UDP报头
{
unsigned short uh_sport;//16位源端口
unsigned short uh_dport;//16位目的端口
unsigned short uh_len;//16位长度
unsigned short uh_sum;//16位校验和
}UDP_HEADER;
int DecodeIpPack(char *buf, int iBufSize);
int DecodeTcpPack(char *TcpBuf);
int DecodeUdpPack(char *UdpBuf);
int DecodeIcmpPack(char *IcmpBuf);
//将协议int转为字符串
char* CheckProtocol(int iProtocol)
{
for(int i=0; i<MAX_PROTO_NUM; i++)
{
if(ProtoMap[i].ProtoNum==iProtocol)
{
return ProtoMap[i].ProtoText;
}
}
return "";
}
void usage(char *name)
{
printf("******************************************\n");
printf("Sniffer Info From Given Packets\n");
printf("\t Written by yzd\n");
printf("usage: %s.exe Given_ip \n",name);
printf("******************************************\n");
}
int main(int argc, char **argv)
{
int ErrorCode;
int TimeOut=2000,SendSEQ=0;
char RecvBuf[128]; // 每个数据包是128个字节
char GivenIP[16]; // 要嗅探的机器IP
memset(GivenIP, 0, 4);
// 如果通过参数输入IP,则将这个IP赋给GivenIP,否则将DEFAULT_DEST_IP赋给GivenIP
if(argc < 2)
{
strcpy(GivenIP, DEFAULT_DEST_IP);
usage(argv[0]);
return FALSE;
}
else
strcpy(GivenIP, argv[1]);
// 以下是声明Socket变量和相应的数据结构
//初始化SOCKET
WSADATA wsaData;
SOCKET SockRaw=(SOCKET)NULL;
ErrorCode = WSAStartup(MAKEWORD(2, 1), &wsaData);
SockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
//获取本机IP地址
char name[MAX_HOSTNAME_LAN];
ErrorCode = gethostname(name, MAX_HOSTNAME_LAN);
struct hostent *pHostent;
pHostent = (struct hostent*)malloc(sizeof(struct hostent));
pHostent = gethostbyname(name);
SOCKADDR_IN sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(6000);
memcpy(&sa.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length);
ErrorCode = bind(SockRaw, (PSOCKADDR) &sa, sizeof(sa));
//设置SOCK_RAW为SIO_RCVALL,以便接收所有的IP包
DWORD dwBufferLen[10];
DWORD dwBufferInLen = 1;
DWORD dwBytesReturned = 0;
ErrorCode = WSAIoctl(SockRaw,SIO_RCVALL,&dwBufferInLen,sizeof(dwBufferInLen),
&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL);
//侦听IP报文
while (1)
{
memset(RecvBuf,0,sizeof(RecvBuf));
ErrorCode = recv(SockRaw, RecvBuf, sizeof(RecvBuf), 0);
ErrorCode = DecodeIpPack(RecvBuf, ErrorCode);
}
closesocket(SockRaw);
WSACleanup();
return ErrorCode;
}
//IP包解析
int DecodeIpPack(char *buf, int iBufSize)
{
IP_HEADER *pIpheader;
int iProtocol, iTTL;
char szProtocol[MAX_PROTO_TEXT_LEN];
char szSourceIP[MAX_ADDR_LEN], szGivenIP[MAX_ADDR_LEN];
SOCKADDR_IN saSource, saDest;
pIpheader = (IP_HEADER*)buf;
iProtocol = pIpheader->proto;
strncpy(szProtocol, CheckProtocol(iProtocol), MAX_PROTO_TEXT_LEN);
//获得源IP地址
saSource.sin_addr.s_addr = pIpheader->sourceIP;
strncpy(szSourceIP, inet_ntoa(saSource.sin_addr), MAX_ADDR_LEN);
//获得目的IP地址
saDest.sin_addr.s_addr = pIpheader->GivenIP;
strncpy(szGivenIP, inet_ntoa(saDest.sin_addr), MAX_ADDR_LEN);
iTTL = pIpheader->ttl;
//打印
printf("%s ", szProtocol);
printf("%s->%s ", szSourceIP, szGivenIP);
printf("bytes=%d TTL=%d ", iBufSize, iTTL);
//计算IP包的首部长度
int iIphLen = sizeof(unsigned long)*(pIpheader->h_verlen &0xf);
//协议解析
switch (iProtocol)
{
case IPPROTO_TCP:
DecodeTcpPack(buf + iIphLen);
break;
case IPPROTO_UDP:
DecodeUdpPack(buf + iIphLen);
break;
case IPPROTO_ICMP:
DecodeIcmpPack(buf + iIphLen);
break;
default:
break;
}
return true;
}
//TCP报文解析
int DecodeTcpPack(char *TcpBuf)
{
TCP_HEADER *pTcpHeader;
int i;
pTcpHeader = (TCP_HEADER*)TcpBuf;
printf("Port:%d->%d ", ntohs(pTcpHeader->th_sport), ntohs(pTcpHeader->th_dport));
unsigned char FlagMask = 1;
for (i = 0; i < 6; i++)
{
if ((pTcpHeader->th_flag) &FlagMask)
printf("%c", ((pTcpHeader->th_flag))<<(i+2) & 0x10);
else
printf("-");
FlagMask = FlagMask << 1;
}
printf("\n");
return true;
}
//UDP报文解析
int DecodeUdpPack(char *UdpBuf)
{
UDP_HEADER *pUdpHeader;
pUdpHeader = (UDP_HEADER*)UdpBuf;
printf("Port:%d->%d ", ntohs(pUdpHeader->uh_sport), ntohs(pUdpHeader->uh_dport));
printf("Len=%d\n", ntohs(pUdpHeader->uh_len));
return true;
}
//ICMP报文解析
int DecodeIcmpPack(char *IcmpBuf)
{
ICMP_HEADER *pIcmpHeader;
pIcmpHeader = (ICMP_HEADER*)IcmpBuf;
printf("Type:%d,Code:%d ", pIcmpHeader->i_type, pIcmpHeader->i_code);
printf("ID=%d SEQ=%d\n", pIcmpHeader->i_id, pIcmpHeader->i_seq);
return true;
}

图5-4 隐藏信息提取结果
(2)基于TCP协议进行信息隐藏
1)数据结构
typedef struct_TCPHeader //20字节的TCP头
{
USHORT sourcePort; //16位源端口号
USHORT destinationPort; //16位目的端口号
ULONG sequenceNumber; //32位序列号
ULONG acknowledgeNumber; //32位确认号
UCHAR dataoffset; //高4位表示数据偏移
UCHAR flags; //6位标志位
USHORT windows; //16位窗口大小
USHORT checksum; //16位校验和
USHORT urgentPointer; //16位紧急数据偏移量
}TCPHeader,*PTCPHeader;
typedef struct psd_hdr //定义TCP伪报头
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl; //协议类型
unsigned short tcpl; //TCP长度
}PSD_HEADER;
2)算法实现
a)嵌入算法:
//计算TCP伪头校验和
void ComputeTcpPseudoHeaderChecksum(
IPHeader *pIphdr,
TCPHeader *pTcphdr,
char *payload,
int payloadlen)
{
char buff[1024];
char *ptr = buff;
int chksumlen = 0;
ULONG zero = 0;
// 包含源IP地址和目的IP地址
memcpy(ptr, &pIphdr->ipSource, sizeof(pIphdr->ipSource));
ptr += sizeof(pIphdr->ipSource);
chksumlen += sizeof(pIphdr->ipSource);
memcpy(ptr, &pIphdr->ipDestination, sizeof(pIphdr->ipDestination));
ptr += sizeof(pIphdr->ipDestination);
chksumlen += sizeof(pIphdr->ipDestination);
// 包含8位0域
memcpy(ptr, &zero, 1);
ptr += 1;
chksumlen += 1;
// 协议
memcpy(ptr, &pIphdr->ipProtocol, sizeof(pIphdr->ipProtocol));
ptr += sizeof(pIphdr->ipProtocol);
chksumlen += sizeof(pIphdr->ipProtocol);
// TCP长度
USHORT tcp_len = htons(sizeof(TCPHeader) + payloadlen);
memcpy(ptr, &tcp_len, sizeof(tcp_len));
ptr += sizeof(tcp_len);
chksumlen += sizeof(tcp_len);
// TCP头
memcpy(ptr, pTcphdr, sizeof(TCPHeader));
ptr += sizeof(TCPHeader);
chksumlen += sizeof(TCPHeader);
// 净荷
memcpy(ptr, payload, payloadlen);
ptr += payloadlen;
chksumlen += payloadlen;
// 补齐到下一个16位边界
for(int i=0; i<payloadlen%2; i++)
{
*ptr = 0;
ptr++;
chksumlen++;
}
// 计算这个校验和,将结果填充到TCP头
pTcphdr->checksum = checksum((USHORT*)buff, chksumlen);
}
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CIPCovertSenderDlg dialog
CIPCovertSenderDlg::CIPCovertSenderDlg(CWnd* pParent /*=NULL*/)
: CDialog(CIPCovertSenderDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CIPCovertSenderDlg)
m_CheckSum = 0;
m_FragmentationType = 0;
m_HeaderSize = 0;
m_Identification = 0;
m_Offset = 0;
m_Protocol = 0;
m_TotalLenth = 0;
m_TypeOfService = 0;
m_Version = 0;
m_TTL = 0;
m_TCPAckNumber = 0;
m_TCPSeqNumber = 0;
m_TCPSrcPort = 0;
m_TCPDestPort = 0;
m_TCPWindow = 0;
m_TCPUrgentPointer = 0;
m_TCPCheckSum = 0;
m_TCPreserved = 0;
m_TCPHeaderSize = 0;
m_TCPFlags = 0;
m_sFilePathName = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CIPCovertSenderDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CIPCovertSenderDlg)
DDX_Control(pDX, IDC_SrcAddress, m_SrcAddress);
DDX_Control(pDX, IDC_DestAddress, m_DestAddress);
DDX_Text(pDX, IDC_CheckSum, m_CheckSum);
DDV_MinMaxUInt(pDX, m_CheckSum, 0, 65535);
DDX_Text(pDX, IDC_FragmentationType, m_FragmentationType);
DDV_MinMaxUInt(pDX, m_FragmentationType, 0, 7);
DDX_Text(pDX, IDC_HeaderSize, m_HeaderSize);
//DDV_MinMaxByte(pDX, m_HeaderSize, 5, 15);
DDX_Text(pDX, IDC_Identification, m_Identification);
DDV_MinMaxUInt(pDX, m_Identification, 0, 65535);
DDX_Text(pDX, IDC_Offset, m_Offset);
DDV_MinMaxUInt(pDX, m_Offset, 0, 8191);
DDX_Text(pDX, IDC_Protocol, m_Protocol);
DDV_MinMaxByte(pDX, m_Protocol, 0, 255);
DDX_Text(pDX, IDC_TotalLenth, m_TotalLenth);
DDV_MinMaxUInt(pDX, m_TotalLenth, 0, 65535);
DDX_Text(pDX, IDC_TypeOfService, m_TypeOfService);
DDV_MinMaxByte(pDX, m_TypeOfService, 0, 30);
DDX_Text(pDX, IDC_Version, m_Version);
//DDV_MinMaxByte(pDX, m_Version, 4, 6);
DDX_Text(pDX, IDC_TTL, m_TTL);
DDV_MinMaxByte(pDX, m_TTL, 0, 255);
DDX_Text(pDX, IDC_TCPAckNumber, m_TCPAckNumber);
DDX_Text(pDX, IDC_TCPSeqNumber, m_TCPSeqNumber);
DDX_Text(pDX, IDC_TCPSrcPort, m_TCPSrcPort);
DDV_MinMaxUInt(pDX, m_TCPSrcPort, 0, 65535);
DDX_Text(pDX, IDC_TCPDestPort, m_TCPDestPort);
DDV_MinMaxUInt(pDX, m_TCPDestPort, 0, 65535);
DDX_Text(pDX, IDC_TCPWindow, m_TCPWindow);
DDV_MinMaxUInt(pDX, m_TCPWindow, 0, 65535);
DDX_Text(pDX, IDC_TCPUrgentPointer, m_TCPUrgentPointer);
DDV_MinMaxUInt(pDX, m_TCPUrgentPointer, 0, 65535);
DDX_Text(pDX, IDC_TCPCheckSum, m_TCPCheckSum);
DDV_MinMaxUInt(pDX, m_TCPCheckSum, 0, 65535);
DDX_Text(pDX, IDC_TCPReserved, m_TCPreserved);
DDV_MinMaxByte(pDX, m_TCPreserved, 0, 63);
DDX_Text(pDX, IDC_TCPHeaderSize, m_TCPHeaderSize);
//DDV_MinMaxByte(pDX, m_TCPHeaderSize, 5, 15);
DDX_Text(pDX, IDC_TCPFlags, m_TCPFlags);
DDV_MinMaxByte(pDX, m_TCPFlags, 0, 63);
DDX_Text(pDX, IDC_EDIT_SourceFile, m_sFilePathName);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CIPCovertSenderDlg, CDialog)
//{{AFX_MSG_MAP(CIPCovertSenderDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(ID_SEND_IP, OnSendIp)
ON_BN_CLICKED(IDC_OpenFile, OnOpenFile)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CIPCovertSenderDlg message handlers
BOOL CIPCovertSenderDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CIPCovertSenderDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CIPCovertSenderDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CIPCovertSenderDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
USHORT 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);
}
BOOL CIPCovertSenderDlg::CheckValidIP()
{
BOOL bOK;
bOK=FALSE;
if (!IPCtrlToSTR(&m_SrcAddress))
MessageBox("Please enter a valid source address!");
else if (!IPCtrlToSTR(&m_DestAddress))
MessageBox("Please enter a valid destination address!");
else if (m_TypeOfService==-1)
MessageBox("Please enter a valid type of service!");
else if (m_FragmentationType==-1)
MessageBox("Please enter a valid fragmentation size!");
else
bOK=TRUE;
return bOK;
}
LPSTR CIPCovertSenderDlg::IPCtrlToSTR(CIPAddressCtrl* ctrl)
{
//Converts the control address to textual address
//Convert bytes to string
BYTE bOctet1;
BYTE bOctet2;
BYTE bOctet3;
BYTE bOctet4;
//Get the value and blank values
int iBlank;
iBlank=ctrl->GetAddress(bOctet1,bOctet2,bOctet3,bOctet4);
if (iBlank!=4)
//Not filled
return NULL;
else
{
in_addr iAddr;
iAddr.S_un.S_un_b.s_b1=bOctet1;
iAddr.S_un.S_un_b.s_b2=bOctet2;
iAddr.S_un.S_un_b.s_b3=bOctet3;
iAddr.S_un.S_un_b.s_b4=bOctet4;
return inet_ntoa(iAddr);
}
}
//发送数据包
void CIPCovertSenderDlg::OnSendIp()
{
UpdateData(TRUE);
DWORD szDestIp;
((CIPAddressCtrl*)GetDlgItem(IDC_DestAddress))->GetAddress(szDestIp);
DWORD szSourceIp;
((CIPAddressCtrl*)GetDlgItem(IDC_SrcAddress))->GetAddress(szSourceIp);
USHORT nDestPort = 80;
USHORT nSourcePort = 1459;
char szMsg[] = "This is a test \r\n";
int nMsgLen = strlen(szMsg);
//创建原始套接字
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
//设置IP首部包含选项
BOOL bIncl = TRUE;
::setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)&bIncl, sizeof(bIncl));
char buff[1024] = { 0 };
//开始填充IP首部
IPHeader *pIphdr = (IPHeader *)buff;
pIphdr->iphVerLen = (4<<4 | (sizeof(IPHeader)/sizeof(ULONG)));
pIphdr->ipLength = ::htons(sizeof(IPHeader) + sizeof(TCPHeader) + nMsgLen);
pIphdr->ipTTL = 128;
pIphdr->ipProtocol = IPPROTO_TCP;
pIphdr->ipSource = htonl(szSourceIp);
pIphdr->ipDestination = htonl(szDestIp);
pIphdr->ipChecksum = checksum((USHORT*)pIphdr, sizeof(IPHeader));
pIphdr->ipTOS = 0x00;
pIphdr->ipFlags = htons((USHORT)(4<<13 | 0));//分片R标记置位
pIphdr->ipID = 0;
//TCP头
TCPHeader *pTcphdr = (TCPHeader *)&buff[sizeof(IPHeader)];
//填充TCP首部
pTcphdr->sourcePort=htons(1459);
pTcphdr->destinationPort=htons(80);
pTcphdr->sequenceNumber=htonl(m_TCPSeqNumber);
pTcphdr->acknowledgeNumber=0;
pTcphdr->dataoffset=((sizeof(IPHeader)/sizeof(ULONG))<<4 | 0>>2);
pTcphdr->flags=(0<<6 | 34);
pTcphdr->windows=htons(16384);
pTcphdr->checksum=0;
pTcphdr->urgentPointer=0;
char *pData = &buff[sizeof(IPHeader) + sizeof(TCPHeader)];
memcpy(pData, szMsg, nMsgLen);
ComputeTcpPseudoHeaderChecksum(pIphdr, pTcphdr, pData, nMsgLen);
//设置目的地址
SOCKADDR_IN destAddr = { 0 };
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(nDestPort);
destAddr.sin_addr.S_un.S_addr = htonl(szDestIp);
//发送原始TCP数据包
int nRet,j=0;
CString str;
for(int i=0; i<num_of_packet; i++,j=j+4)
{
pTcphdr->sourcePort = htons(1459);
pTcphdr->destinationPort = htons(80);
pTcphdr->sequenceNumber = htonl(filebuff[j]<<24 | filebuff[j+1]<<16 |
filebuff[j+2]<<8 | filebuff[j+3]);
pTcphdr->acknowledgeNumber=0;
pTcphdr->windows=htons(16384);
pTcphdr->urgentPointer=0;
pData = &buff[sizeof(IPHeader) + sizeof(TCPHeader)];
memcpy(pData, szMsg, nMsgLen);
nRet = ::sendto(sRaw, buff, sizeof(IPHeader) + sizeof(TCPHeader) + nMsgLen,
0, (sockaddr*)&destAddr, sizeof(destAddr));
if(nRet == SOCKET_ERROR)
{
AfxMessageBox(" sendto() failed.");
break;
}
Sleep(1000);
}
str.Format("%d",nRet);
str="成功发送 "+str+" 字节!";
AfxMessageBox(str);
::closesocket(sRaw);
}
void CIPCovertSenderDlg::OnOpenFile()
{
// TODO: Add your control notification handler code here
CFileDialog m_ldFile(TRUE);
// 显示打开文件对话框,并捕捉结果
if(m_ldFile.DoModal()==IDOK)
{
// 捕获所选文件名
m_sFilePathName=m_ldFile.GetPathName();
m_sFileName=m_ldFile.GetFileName();
// 更新对话框
UpdateData(FALSE);
// 按钮可用
GetDlgItem(ID_SEND_IP)->EnableWindow(TRUE);
}
UpdateData(FALSE);
FillData();
}
//填充读取的数据到缓存
void CIPCovertSenderDlg::FillData()
{
FILE *fpin;
CString str;
int fh,ch;
long i,filelength=0;
// 用户输入更新变量
UpdateData(TRUE);
fh = _open( m_sFilePathName, _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE );
filelength=_filelength( fh );
if(filelength>65534)
{
str="文件 "+m_sFileName+" 长度大于65K!";
MessageBox(str);
exit(0);
}
_close( fh );
if((fpin=fopen(m_sFilePathName,"rb"))==NULL)
{
str="打开文件 "+m_sFileName+" 失败";
MessageBox(str);
exit(0);
}
BeginWaitCursor();
ch=fgetc(fpin);
i=0;
while(ch!=-1)
{
filebuff[i++]=ch;
ch=fgetc(fpin);
}
fclose(fpin);
if(filelength%4!=0)
num_of_packet=filelength/4+1;
else
num_of_packet=filelength/4;
}

图5-5 TCP信息隐藏的嵌入程序
b)提取算法
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
pcap_if_t* alldevs;
pcap_if_t* d;
int inum;
int i = 0;
pcap_t* adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
char packet_filter[] = "ip and tcp";
struct bpf_program fcode;
pcap_dumper_t *dumpfile;
int nRetCode = 0;
str.Empty();
str2.Empty();
i_dialog=0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
/* 得到设备列表 */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
/* 打印列表*/
for (d = alldevs; d; d = d->next)
{
printf("%d. %s", ++ i, d->name);
if (d->description)
{
printf(" (%s)\n", d->description);
}
else
{
printf(" (No description available)\n");
}
}
if (i == 0)
{
printf("\nNo interfaces found! Make sure Winpcap is installed.\n");
return -1;
}
printf("Enter the interface number (1 - %d):", i);
scanf("%d", &inum);
if (inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/* 跳至选择的适配器 */
for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);
/* 打开适配器 */
if ((adhandle = pcap_open(d->name, /*设备名称 */
65536, /* 捕获包的最大长度 */
PCAP_OPENFLAG_PROMISCUOUS, /* 设置为混杂模式 */
1000, /* 读取时延 */
NULL,
errbuf /* 错误缓存 */
)) == NULL)
{
fprintf(stderr, "\nUnable to open the adapter. %s is not supported by Winpcap\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/* 打开一个dump文件 */
dumpfile = pcap_dump_open(adhandle, "message.dump");
if(dumpfile==NULL)
{
fprintf(stderr,"\nError opening output file\n");
return -1;
}
/* 检查链路层,目前只支持以太网协议 */
if (pcap_datalink(adhandle) != DLT_EN10MB)
{
fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
if (d->addresses != NULL)
{
netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
}
else
{
netmask = 0xffffffff;
}
/* 编译过滤器 */
if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
{
fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/* 设置过滤器 */
if (pcap_setfilter(adhandle, &fcode) < 0)
{
fprintf(stderr, "\nError setting the filter.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
printf("\nlistening on %s ...\n", d->description);
pcap_freealldevs(alldevs);
/* 开始捕获 */
pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile);
return 1;
}
return nRetCode;
}
/* 回调函数 */
void packet_handler(u_char* dumpfile, const struct pcap_pkthdr* header, const u_char* pkt_data){
struct tm* ltime;
char timestr[16];
ip_header *ih;
tcp_header* th;
u_int ip_len;
u_short sport, dport;
u_int i_tcp_seq;
u_int i_tcp_ack;
u_short i_TCP_SYN,i_winsize,i_urgent;
u_int i_temp=0;
u_int Data;
/* 转换时间戳 */
ltime = localtime(&header->ts.tv_sec);
strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);
/* 打印间戳和包长度 */
printf("%s.%.6d len: %d \n", timestr, header->ts.tv_usec, header->len);
/* 得到IP首部的地址 */
ih = (ip_header*)(pkt_data + 14);
/* 得到tcp首部的地址 */
ip_len = (ih->ver_ihl & 0xf) * 4;
th = (tcp_header*)((u_char*)ih + ip_len);
/* 字节序转换 */
sport = ntohs(th->tcp_source_port);
dport = ntohs(th->tcp_dest_port);
i_winsize = ntohs(th->tcp_winsize);
i_urgent = ntohs(th->tcp_urgent);
i_TCP_SYN=ntohs((u_short)ih->identification);
i_tcp_seq=ntohl((u_int)th->tcp_seqno);
i_tcp_ack=ntohl((u_int)th->tcp_ackno);
Data=i_tcp_seq;
if(ih->flags_fo==0x80&&ih->saddr.byte1==192&&ih->saddr.byte2==168&&ih->saddr.byte3==0&&ih->saddr.byte4==1&&ih->proto==6&&th->tcp_syn==1)
{
printf("%d.%d.%d.%d: %d -> %d.%d.%d.%d: %d -- seq:%u\n",
ih->saddr.byte1,
ih->saddr.byte2,
ih->saddr.byte3,
ih->saddr.byte4,
sport,
ih->daddr.byte1,
ih->daddr.byte2,
ih->daddr.byte3,
ih->daddr.byte4,
dport,
i_tcp_seq);
num_packet++;
i_dialog++;
stream0=fopen("ISN_Data.dat","a+");
fprintf(stream0,"%c%c%c%c%",
(char)(Data>>24),(char)(Data>>16),
(char)(Data>>8),(char)Data);
str=str+(char)(Data>>24)+(char)(Data>>16)+
(char)(Data>>8)+(char)Data;
str_ip1.Format("%d",ih->saddr.byte1);
str_ip2.Format("%d",ih->saddr.byte2);
str_ip3.Format("%d",ih->saddr.byte3);
str_ip4.Format("%d",ih->saddr.byte4);
str2.Empty();
str2=str2+str_ip1+'.'+str_ip2+'.'+str_ip3+'.'+str_ip4;
fclose(stream0);
stream1=fopen("TCP_SYN.txt","a+");
fprintf(stream1,"%u %d\n",num_packet,i_tcp_seq);
fclose(stream1);
//调节一次显示消息的数量,i_dialog表示数据包的个数,每次可显示i_dialog×32比特的消息
if(i_dialog==20)
{
MessageBox(NULL,str,"收到来自"+str2+"的消息",MB_ICONINFORMATION | MB_OK);
i_dialog=0;
str.Empty();
str2.Empty();
}
}
}

图5-6 使用wireshark所抓获的TCP报文

图5-7 抓获的3个TCP报文中所提取到的信息

浙公网安备 33010602011771号