上一篇讲到了对所有的网络数据包进行侦听,并过滤,得到自己想要分析的数据包。数据包被侦听到了,但仍旧发送给了远程的服务器,若我们希望截获这些数据包,但不希望把这些数据被发送给远程服务器,那该如何解决呢?防火墙!对,我也想到了使用防火墙。windows下的防火墙,大多使用NIDS(Network Driver Interface Specification)对中间层驱动(Intermediate drivers)进行操作。NIDS就是把IP包拦截在中间驱动层,使这些不能通过网卡发送。但是这样做的话,上一篇的模拟sniffer程序也侦听不到被NIDS所拦截的IP包。因此,防火墙行不通。
使用网卡的混杂模式,通过伪造TCP握手信号,赶在本机与远程端握手连通之前,把本机的手牵到自己这里来,不是也可以实现吗?这正是我所要讲的重点。没错,我的实现思路也是这样的。sendto 函数只在windows server 2003 下支持,在windows XP系统下,为了保证网络安全,已经不再被支持。有网友说可以用Wsasend()可以替代sendto,为了节约时间,我没有尝试去用Wsasend()函数。
使用Winpcap(windows packet capture)可以完成所需要的功能。winpcap独立于主机协议(如TCP-IP)而发送和接收原始数据包。Winpcap为数据包捕获提供了windows下的一个平台,它是由伯克利分组捕获库派生而来的分组捕获库,它是在Windows操作平台上来实现对底层包的截取过滤,它的体系结构是由一个核心的包过滤驱动程序,一个底层的动态连接库packet.dll和一个高层的独立于系统的函数库libpcap组成。
调用winpcap函数的程序,需要安装winpcap程序,使之能与驱动挂上钩。还需要下载winpcap的头文件和相应的库文件。这样程序才能够跑得起来。
首先先来介绍一下包头。除了上一篇介绍的IP头,TCP、UDP、ICMP头外,这里还有两个头:以太网地址包头和计算校验码的包头。
typedef struct _ethhdr
{
unsigned char eh_dst[6];
unsigned char eh_src[6];
unsigned short eh_type;
}ETH_HEADER;
typedef struct _psdhdr
{
unsigned long saddr;
unsigned long daddr;
char mbz; //保留,置0
char ptcl; //协议,如IPPROTO_TCP
unsigned short tcpl; //TCP报头长度
}PSD_HEADER;
写一个对数据进行处理的类。
class CTod
{
public:
CTod(void);
~CTod(void);
public:
pcap* m_pCap; //PCAP句柄
public:
int CTod::SendRaw(BYTE *_dmac,BYTE *_smac,
USHORT _ident,
ULONG _saddr,USHORT _sport,
ULONG _daddr,USHORT _dport,
ULONG _seq, ULONG _ack);
USHORT checksum(USHORT *buffer, int size);
BOOL OpenPcap(ULONG _laddr); //打开本地网口
static DWORD WINAPI WorkSniffer(LPVOID _lp); //抓包线程
};
CTod::CTod(void)
{}
CTod::~CTod(void)
{}
int CTod::SendRaw(BYTE *_dmac,BYTE *_smac, //目的MAC,源MAC,网络顺序
USHORT _ident, //IP包标识号
ULONG _saddr,USHORT _sport, //源IP,PORT,网络顺序
ULONG _daddr,USHORT _dport, //目的IP,PORT,网络顺序
ULONG _seq, ULONG _ack) //32位序列号,应答号,网络顺序
{
if(m_pCap==NULL){
return -1;
}
//构造以太网头
ETH_HEADER eth;
memcpy(eth.eh_dst,_dmac,6);
memcpy(eth.eh_src,_smac,6);
eth.eh_type=htons(ETH_IP);
//构造IP头
IP_HEADER ip;
ip.h_verlen= 4<<4 | sizeof(IP_HEADER)/4;
ip.tos=0;
ip.total_len=htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER));
ip.ident=_ident;
ip.frag_and_frag=htons(0x4000); //禁止分片
ip.ttl=128;
ip.proto=IPPROTO_TCP;
ip.cksum=0;
ip.saddr=_saddr;
ip.daddr=_daddr;
//构造TCP头
TCP_HEADER tcp;
tcp.th_sport=_sport;
tcp.th_dport=_dport;
tcp.th_seq=_seq;
tcp.th_ack=_ack;
tcp.th_lenres=sizeof(TCP_HEADER)/4<<4 | 0;
tcp.th_flag= SYN | ACK;
tcp.th_win=htons(512);
tcp.th_cksum=0;
tcp.th_urp=0;
//构造PSD头
PSD_HEADER psd;
psd.saddr=ip.saddr;
psd.daddr=ip.daddr;
psd.mbz=0;
psd.ptcl=IPPROTO_TCP;
psd.tcpl=htons(sizeof(TCP_HEADER));
//开始装包并计算校验和
char szbuf[128]={0};
memcpy(szbuf,&psd,sizeof(psd));
memcpy(szbuf+sizeof(psd),&tcp,sizeof(tcp));
tcp.th_cksum=checksum((USHORT*)szbuf,sizeof(psd)+sizeof(tcp));
ip.cksum =checksum((USHORT*)&ip,sizeof(ip));
memset(szbuf,0,sizeof(szbuf));
memcpy(szbuf,ð,sizeof(eth));
memcpy(szbuf+sizeof(eth),&ip,sizeof(ip));
memcpy(szbuf+sizeof(eth)+sizeof(ip),&tcp,sizeof(tcp));
memset(szbuf+sizeof(eth)+sizeof(ip)+sizeof(tcp),0,4);
int bytes_out=0;
int len=sizeof(eth)+sizeof(ip)+sizeof(tcp);
int ret=pcap_sendpacket(m_pCap,(BYTE*)szbuf,len);
if(ret!=0){
bytes_out=-1;
}
else{
bytes_out=len;
}
return bytes_out;
}
//计算校验和
USHORT CTod::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 CTod::OpenPcap(ULONG _laddr)
{
pcap_if_t *alldevs; //打开适配器时使用
pcap_if_t *d;
char errbuf[PCAP_ERRBUF_SIZE];
if(pcap_findalldevs(&alldevs,errbuf)==-1){ //获取网卡的列表
return FALSE;
}
BOOL bfind=FALSE;
for(d=alldevs; d; d=d->next){
for(pcap_addr_t *a=d->addresses; a; a=a->next){
struct sockaddr_in *psin=(struct sockaddr_in*)a->addr;
if( psin->sin_family==AF_INET ){
if( psin->sin_addr.s_addr==_laddr ){
bfind=TRUE;
break;
}
}
}
if(bfind)
break;
}
if(!bfind){
pcap_freealldevs(alldevs);
return FALSE;
}
//打开选择的网卡
if((m_pCap=pcap_open_live(d->name, // 设备名称
65535, // portion of the packet to capture
1, // 混杂模式
100, // 读超时为0.1秒
errbuf // error buffer
))==NULL)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
pcap_freealldevs(alldevs); //释放设备列表
return FALSE;
}
pcap_setbuff(m_pCap,1024*1024); //设置内核缓冲区,pcap默认是1M缓冲
struct bpf_program fcode; //指向BPF指令所在空间
char filter[4]="tcp";
ULONG netmask=0x00ffffff;
ULONG mask=*(ULONG*)&(((struct sockaddr_in*)d->addresses->netmask)->sin_addr);
netmask=mask;
pcap_compile(m_pCap, &fcode,filter,1,netmask); //编译 tcpdump 表达式为BPF程序
pcap_setfilter(m_pCap, &fcode); //设置BPF内核过滤器
pcap_freecode(&fcode); //释放指令空间
pcap_freealldevs(alldevs); //释放设备列表
return TRUE;
}
//抓包子线程
DWORD WINAPI CTod::WorkSniffer(LPVOID _lp)
{
CTod *ptod=(CTod*)_lp;
struct pcap_pkthdr *header;
u_char *pkt_data;
char szSourceIP[MAX_ADDR_LEN];
char szDestIP[MAX_ADDR_LEN];
int ret=0;
while(TRUE)
{
//捕获数据包
ret=pcap_next_ex(ptod->m_pCap,
&header, //内核过滤器每输出一个包,将在输出的数据前加了20字节的数据
(const u_char**)&pkt_data);
if(ret<=0)
continue;
ETH_HEADER *peth=(ETH_HEADER*)pkt_data;
IP_HEADER *pip=(IP_HEADER*)(pkt_data+sizeof(ETH_HEADER));
TCP_HEADER *ptcp=(TCP_HEADER*)(pkt_data+sizeof(ETH_HEADER)+sizeof(IP_HEADER));
unsigned long szSourIP = pip->saddr;
if( IsAlexaDes(szSourIP) ){
}
unsigned long szDestIP = pip->daddr;
if( !IsAlexaDes(szDestIP) ){ //目的IP都与Alexa无关
continue;
}
//除掉IP头和TCP头后的数据长度
int predatalen=ntohs(pip->total_len)-40;
////正向发RST包
//ptod->SendRaw(peth->eh_dst,peth->eh_src,
// htons(ntohs(pip->ident)+1),
// pip->saddr,ptcp->th_sport,
// pip->daddr,ptcp->th_dport,
// htonl( ntohl(ptcp->th_seq)+predatalen ),
// ptcp->th_ack);
//反向发RST包
ptod->SendRaw(peth->eh_src,peth->eh_dst,
htons(0),
pip->daddr,ptcp->th_dport,
pip->saddr,ptcp->th_sport,
ptcp->th_ack,
htonl( ntohl(ptcp->th_seq)+1 ));
}
return 0;
}
程序是通过侦听到TCP连接的握手信号,然后对TCP包进行分析后,发送一个握手应答信号给连接的发起端。当连接的发起端接收到连接应答信号后,会再发送一个应答信号,此时,连接发起端认为TCP连接己经建立,就开始发送数据。再用上一篇讲的办法来侦听数据,即可得到所需的IP包。因此,通过这种IP欺诈的办法,可以骗得所需的TCP数据包。
http://blog.sina.com.cn/s/blog_613e4fea0100l5tj.html