WinAPI 网络监听程序的开发步骤(sniff)

老婆大人今天和他们同事们打羽毛球去了,闲着没事把这两天使用winapi做的一个网络抓包监听的程序步骤整理一下:

我的程序参考了《Visual C++网络通信编程实用案例精选》这本书里的一个例子。

1、首先在VC6里建立一个win32 app工程,在工程里添加Iphlpapi.lib ws2_32.lib

方法:


#pragma comment(lib,"wsock32.lib")
#pragma comment(lib,"comctl32.lib")

或者在build-》setting-》link里添加

2、定义IP,TCP,UDP协议头,这个到处有,copy一个就行了:


typedef struct _IPHEADER {
        unsigned char  header_len:4;
        unsigned char  version:4;  
        unsigned char  tos;            // type of service
        unsigned short total_len;      // length of the packet
        unsigned short ident;          // unique identifier
        unsigned short flags;         
        unsigned char  ttl;           
        unsigned char  proto;          // protocol ( IP , TCP, UDP etc)
        unsigned short checksum;      
        unsigned int   sourceIP;
        unsigned int   destIP;

}IPHEADER;
#define UDP_HEAD_LEN 8

#define PSEUDO_HEAD_LEN 12

#define ICMP_HEAD_LEN 4

struct TCPPacketHead {
 WORD SourPort;
 WORD DestPort;
 DWORD SeqNo;
 DWORD AckNo;
 BYTE HLen;
 BYTE Flag;
 WORD WndSize;
 WORD ChkSum;
 WORD UrgPtr;
};

struct ICMPPacketHead {
 BYTE Type;
 BYTE Code;
 WORD ChkSum;
};

struct UDPPacketHead {
 WORD SourPort;
 WORD DestPort;
 WORD Len;
 WORD ChkSum;
};

3、在工程初始化的地方添加socket的初始化和对网卡IP地址的寻找,我因为创建了一个dlg,所以放在了WM_INITDIALOG里:

WSAStartup(MAKEWORD(2,2), &wsaData);
   if(gethostname(szHostName,128) == 0)
   {
    pHost = gethostbyname(szHostName);
    if(pHost != NULL)
    {
     pszIp = inet_ntoa(*(in_addr *)pHost->h_addr_list[iNum]);
     
   //  memset(szTemp,0x00,128);
    // pszIp = "127.0.0.1";
     g_ipsource = inet_addr(pszIp);

//  ::SetWindowText(hShowDataWnd,pszIp);
    }
    else
     ::MessageBox(NULL, "pHost = NULL", "gethostbyname", MB_OK);
   }
   else
    ::MessageBox(NULL, "szHostName = NULL", "gethostname", MB_OK);

DWORD dwSize = 0 ;
   GetIpAddrTable( NULL , &dwSize, FALSE ) ;
   PMIB_IPADDRTABLE pIpAddrTable = (PMIB_IPADDRTABLE )new BYTE [ dwSize ] ;
   if( pIpAddrTable )
   {
    if( GetIpAddrTable( (PMIB_IPADDRTABLE)pIpAddrTable,   // // buffer for IP table
       &dwSize,                // size of buffer
       FALSE                  // sort by IP address
       ) == NO_ERROR )
    {
      if(  pIpAddrTable->dwNumEntries > 2 ) // Second is MS TCP loopback IP ( 127.0.0.1 )
     {
      g_Multihomed = TRUE ;
      char szIP[16];
      for( int i = 0 ; i < (int)pIpAddrTable->dwNumEntries ; i++ )
      {
       in_addr ina ;
       ina.S_un.S_addr = pIpAddrTable->table[i].dwAddr ;
        char *pIP = inet_ntoa( ina ) ;
       strcpy( szIP , pIP ) ;
 //      if( stricmp( szIP , "127.0.0.1" ) )
 //       ::SetWindowText(hShowDataWnd,pszIp);
      }
     }
    }
    delete [] pIpAddrTable ;
   }
  }

5、建立原始套接字(RAW),在这里我都去掉了对返回值的判断,实际应用应该加上

g_sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_IP);

6、设置套接字的各种属性:

int rcvtimeout = 5000;
 if(setsockopt(g_sockfd,SOL_SOCKET,SO_RCVTIMEO,(const char *)&rcvtimeout,sizeof(rcvtimeout)) == SOCKET_ERROR){
  dwErr = WSAGetLastError();
  sprintf(szErr,"Error setsockopt() = %ld ",dwErr);
  ::MessageBox(NULL, szErr, "setsockopt", MB_OK);
  closesocket(g_sockfd);
  return -1;
 }

sa.sin_family = AF_INET;
 sa.sin_port = htons(MONITOR_PORT);
 sa.sin_addr.s_addr = g_iphostsource;

if(bind(g_sockfd,(PSOCKADDR)&sa,sizeof(sa)) == SOCKET_ERROR)
 {
  dwErr = WSAGetLastError();
  sprintf(szErr,"Error bind() = %ld ",dwErr);
  ::MessageBox(NULL, szErr, "bind", MB_OK);
  closesocket(g_sockfd);
  return -1;
 }
 
 if(SOCKET_ERROR != WSAIoctl(g_sockfd,SIO_RCVALL,&dwBufferInLen,sizeof(dwBufferInLen),&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL))
 {
 
 // ::MessageBox(NULL, "WSAIoctl()  OK", "WSAIoctl", MB_OK);
  HANDLE hThread = ::CreateThread(NULL,NULL,monitor_threadFunc,NULL,0,&g_MonitorThreadID);
 }
 else
 {
  dwErr = WSAGetLastError();
  sprintf(szErr,"Error WSAIoctl() = %ld ",dwErr);
  ::MessageBox(NULL, szErr, "WSAIoctl", MB_OK);
  closesocket(g_sockfd);
  return -1;

}

其中比较重要需要注意的就是sin_port 要设置成特定的端口,sa.sin_addr.s_addr 必须是本机一个网卡的IP地址而不能是ANY,

WSAIoctl(g_sockfd,SIO_RCVALL,&dwBufferInLen,sizeof(dwBufferInLen),&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL))这句话是将选中网卡设置为混杂模式,因为只有在混杂模式下网卡才可以将发到本端口而不是发给本机的网络包(我称之为路过的包)收到协议栈里。

7、到此,准备工作基本做好了,下面就可以创建一个线程阻塞着收网络包了,收到之后就可以进行你想进行的分析处理了,收包方式:

memset( buf , 0 , sizeof(buf) ) ;
  iRet =  recv( g_sockfd , buf , sizeof( buf ) , 0 ) ;

 

我们常见的监听程序sniff基本上就是上面的做法了,还有一种就是使用winpcap处理的监听了,著名的etheral软件和linux下的tcpdump就是采用的libcap做的,本人有时间的话会去了解一下。

我在编写这个程序时用到了combobox,ListBox等控件来显示,控制等,都是用API处理的,有时间我打算把这些控件的用法总结一下,以便以后查找方便,因为本人主要是做嵌入式系统下编程的人(如VxWorks,Linux),windows用的少,容易忘,呵呵,好了,今天就写到这里了。

 

你可能感兴趣的:(网络,socket,struct,tcp,null,winapi)