抓包教程

Windows2000在TCP/IP协议组件上做了很多改进,功能也有增强。比如在协议栈上的调整,增大了默认窗口大小,以及高延迟链接新算法。同时在安全性上,可应用IPSec加强安全性,比NT下有不少的改进。

 

 Microsoft TCP/IP 组件包含“核心协议”、“服务”及两者之间的“接口”。传输驱动程序接口 (TDI) 与网络设备接口规范 (NDIS) 是公用的。 此外,还有许多用户模型应用程序的更高级接口。最常用的接口是Windows Sockets、远程过程调用 (RPC) 和 NetBIOS。

 

 Windows Sockets 是一个编程接口,它是在加州大学伯克利分校开发的套接字接口的基础上定义的。它包括了一组扩展件,以充分利用 Microsoft Windows 消息驱动的特点。规范的 1.1 版是在 1993 年 1 月发行的,2.2.0 版在 1996 年 5 月发行。Windows 2000 支持 Winsock 2.2 版。在Winsock2中,支持多个传输协议的原始套接字,重叠I/O模型、服务质量控制等。

 

  这里介绍Windows Sockets的一些关于原始套接字(Raw Socket)的编程。同Winsock1相比,最明显的就是支持了Raw Socket套接字类型,通过原始套接字,我们可以更加自如地控制Windows下的多种协议,而且能够对网络底层的传输机制进行控制。

 

1、创建一个原始套接字,并设置IP头选项。

 

SOCKET sock;

sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);

或者:

s =WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);

 

  这里,我们设置了SOCK_RAW标志,表示我们声明的是一个原始套接字类型。创建原始套接字后,IP头就会包含在接收的数据中,如果我们设定 IP_HDRINCL 选项,那么,就需要自己来构造IP头。注意,如果设置IP_HDRINCL 选项,那么必须具有 administrator权限,要不就必须修改注册表:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Afd\Parameter\

修改键:DisableRawSecurity(类型为DWORD),把值修改为 1。如果没有,就添加。

 

BOOL blnFlag=TRUE;

setsockopt(sock, IPPROTO_IP, IP_HDRINCL,(char *)&blnFlag, sizeof(blnFlag);

 

  对于原始套接字在接收数据报的时候,要注意这么几点:

1、如果接收的数据报中协议类型和定义的原始套接字匹配,那么,接收的所有数据就拷贝到套接字中。

2、如果绑定了本地地址,那么只有接收数据IP头中对应的远端地址匹配,接收的数据就拷贝到套接字中。

3、如果定义的是外部地址,比如使用connect(),那么,只有接收数据IP头中对应的源地址匹配,接收的数据就拷贝到套接字中。

 

 

2、构造IP头和TCP头

 

这里,提供IP头和TCP头的结构:

// Standard TCP flags

#define URG 0x20

#define ACK 0x10

#define PSH 0x08

#define RST 0x04

#define SYN 0x02

#define FIN 0x01

typedef struct _iphdr //定义IP首部

{

unsigned char h_lenver; //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 destIP; //32位目的IP地址

}IP_HEADER;

 

typedef struct psd_hdr //定义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;

 

TCP伪首部并不是真正存在的,只是用于计算检验和。校验和函数:

 

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);

}

 

  当需要自己填充IP头部和TCP头部的时候,就同时需要自己计算他们的检验和。

 

3、发送原始套接字数据报

 

  填充这些头部稍微麻烦点,发送就相对简单多了。只需要使用sendto()就OK。

 

sendto(sock, (char*)&tcpHeader,sizeof(tcpHeader), 0, (sockaddr*)&addr_in,sizeof(addr_in));

 

下面是一个示例程序,可以作为SYN扫描的一部分。

 

#include <stdio.h>

#include <winsock2.h>

#include <ws2tcpip.h>

 

#define SOURCE_PORT 7234

#define MAX_RECEIVEBYTE 255

 

typedef struct ip_hdr //定义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 destIP; //32位目的IP地址

}IPHEADER;

 

typedef struct tsd_hdr //定义TCP伪首部

{

unsigned long saddr; //源地址

unsigned long daddr; //目的地址

char mbz;

char ptcl; //协议类型

unsigned short tcpl; //TCP长度

}PSDHEADER;

 

typedef struct tcp_hdr //定义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;

 

//CheckSum:计算校验和的子函数

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);

}

 

void useage()

{

printf("******************************************\n");

printf("TCPPing\n");

printf("\t Written by Refdom\n");

printf("\t Email:refdom@263.net\n");

printf("Useage: TCPPing.exe Target_ipTarget_port \n");

printf("*******************************************\n");

}

 

int main(int argc, char* argv[])

{

WSADATA WSAData;

SOCKET sock;

SOCKADDR_IN addr_in;

IPHEADER ipHeader;

TCPHEADER tcpHeader;

PSDHEADER psdHeader;

 

char szSendBuf[60]={0};

BOOL flag;

int rect,nTimeOver;

 

useage();

 

if (argc!= 3)

{ return false; }

 

if (WSAStartup(MAKEWORD(2,2),&WSAData)!=0)

{

printf("WSAStartup Error!\n");

return false;

}

 

if((sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)

{

printf("Socket Setup Error!\n");

return false;

}

flag=true;

if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(flag))==SOCKET_ERROR)

{

printf("setsockopt IP_HDRINCLerror!\n");

return false;

}

 

nTimeOver=1000;

if (setsockopt(sock, SOL_SOCKET,SO_SNDTIMEO, (char*)&nTimeOver, sizeof(nTimeOver))==SOCKET_ERROR)

{

printf("setsockopt SO_SNDTIMEOerror!\n");

return false;

}

addr_in.sin_family=AF_INET;

addr_in.sin_port=htons(atoi(argv[2]));

addr_in.sin_addr.S_un.S_addr=inet_addr(argv[1]);

 

//

//

//填充IP首部

ipHeader.h_verlen=(4<<4 |sizeof(ipHeader)/sizeof(unsigned long));

// ipHeader.tos=0;

ipHeader.total_len=htons(sizeof(ipHeader)+sizeof(tcpHeader));

ipHeader.ident=1;

ipHeader.frag_and_flags=0;

ipHeader.ttl=128;

ipHeader.proto=IPPROTO_TCP;

ipHeader.checksum=0;

ipHeader.sourceIP=inet_addr("本地地址");

ipHeader.destIP=inet_addr(argv[1]);

 

//填充TCP首部

tcpHeader.th_dport=htons(atoi(argv[2]));

tcpHeader.th_sport=htons(SOURCE_PORT); //源端口号

tcpHeader.th_seq=htonl(0x12345678);

tcpHeader.th_ack=0;

tcpHeader.th_lenres=(sizeof(tcpHeader)/4<<4|0);

tcpHeader.th_flag=2; //修改这里来实现不同的标志位探测,2是SYN,1是FIN,16是ACK探测 等等

tcpHeader.th_win=htons(512);

tcpHeader.th_urp=0;

tcpHeader.th_sum=0;

 

psdHeader.saddr=ipHeader.sourceIP;

psdHeader.daddr=ipHeader.destIP;

psdHeader.mbz=0;

psdHeader.ptcl=IPPROTO_TCP;

psdHeader.tcpl=htons(sizeof(tcpHeader));

 

//计算校验和

memcpy(szSendBuf, &psdHeader,sizeof(psdHeader));

memcpy(szSendBuf+sizeof(psdHeader),&tcpHeader, sizeof(tcpHeader));

tcpHeader.th_sum=checksum((USHORT*)szSendBuf,sizeof(psdHeader)+sizeof(tcpHeader));

 

memcpy(szSendBuf, &ipHeader,sizeof(ipHeader));

memcpy(szSendBuf+sizeof(ipHeader),&tcpHeader, sizeof(tcpHeader));

memset(szSendBuf+sizeof(ipHeader)+sizeof(tcpHeader),0, 4);

ipHeader.checksum=checksum((USHORT*)szSendBuf, sizeof(ipHeader)+sizeof(tcpHeader));

 

memcpy(szSendBuf, &ipHeader,sizeof(ipHeader));

 

rect=sendto(sock, szSendBuf,sizeof(ipHeader)+sizeof(tcpHeader),

0, (struct sockaddr*)&addr_in,sizeof(addr_in));

if (rect==SOCKET_ERROR)

{

printf("send error!:%d\n",WSAGetLastError());

return false;

}

else

printf("send ok!\n");

 

closesocket(sock);

WSACleanup();

 

return 0;

}

 

4、接收数据

  和发送原始套接字数据相比,接收就比较麻烦了。因为在WIN我们不能用recv()来接收raw socket上的数据,这是因为,所有的IP包都是先递交给系统核心,然后再传输到用户程序,当发送一个rawssocket包的时候(比如syn),核心并不知道,也没有这个数据被发送或者连接建立的记录,因此,当远端主机回应的时候,系统核心就把这些包都全部丢掉,从而到不了应用程序上。所以,就不能简单地使用接收函数来接收这些数据报。

 

  要达到接收数据的目的,就必须采用嗅探,接收所有通过的数据包,然后进行筛选,留下符合我们需要的。可以再定义一个原始套接字,用来完成接收数据的任务,需要设置SIO_RCVALL,表示接收所有的数据。

 

SOCKET sniffersock;

sniffsock = WSASocket(AF_INET, SOCK_RAW,IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);

 

DWORD lpvBuffer = 1;

DWORD lpcbBytesReturned = 0 ;

WSAIoctl(sniffersock, SIO_RCVALL,&lpvBuffer, sizeof(lpvBuffer), NULL, 0, & lpcbBytesReturned, NULL,NULL);

 

  创建一个用于接收数据的原始套接字,我们可以用接收函数来接收数据包了。然后在使用一个过滤函数达到筛选的目的,接收我们需要的数据包。

 

 

如果在XP以上的操作系统,微软封杀了Raw Soccket,只能用wincpap之类的开发包了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值