最近写了一个抓包软件,C++写的,VS2008工程
以下是软件的代码及讲解
首先对于主函数,
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
::hInstance = hInstance;
static HWND hPrev;
hPrev = FindWindow(L"#32770", L"网络数据包捕获工具 v2.0 By:寒漠软件工作室");
if(hPrev)
{
MessageBox(NULL, L"程序已经在运行!", L"提示", MB_ICONASTERISK);
ShowWindow(hPrev, SW_SHOWNORMAL);
SetForegroundWindow(hPrev);
return -1;
}
InitCommonControls();
//加载图标
::hImageListSmall = ImageList_Create(16, 16, ILC_COLOR4, 0, 10);//创建图标列表
static HICON hIcon;
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_IP));
::iImageList[0] = ImageList_AddIcon(::hImageListSmall, hIcon);//插入图标到图标列表中
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_ICMP));
::iImageList[1] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_IGMP));
::iImageList[2] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_GGP));
::iImageList[3] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_TCP));
::iImageList[4] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_PUP));
::iImageList[5] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_UDP));
::iImageList[6] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_IDP));
::iImageList[7] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_ND));
::iImageList[8] = ImageList_AddIcon(::hImageListSmall, hIcon);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTL_UNKNOWN));
::iImageList[9] = ImageList_AddIcon(::hImageListSmall, hIcon);
::dDataPtr[0] = (DWORD)&::bDataBuf;
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, (DLGPROC)hanDialogFunc);
return 0;
}
FindWindow为在创建窗口前先检查一下自身是否有实例在运行,如果在运行,那么激活实例并结束自身。
然后是创建一个图标列表,也就是ListView列表最前面的小图标了,全部是我自己画的,嘿嘿。
然后是消息循环了:
BOOL WINAPI hanDialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//消息过程函数
{
HICON dVar;
switch(uMsg)
{
case WM_INITDIALOG://初始化
::hWnd = hDlg;
dVar = LoadIcon(Instance,MAKEINTRESOURCE(IDI_ICON_MAIN));
SendMessage(hDlg ,WM_SETICON, ICON_BIG,(LPARAM)dVar);
InitListView();
SetDlgItemText(hDlg, IDC_EDIT_DATA, wcLogo);
::hList = (HWND)GetDlgItem(hDlg, IDC_LIST_PACKATE);//ListView的窗体句柄
::dListProc = SetWindowLong(::hList, GWL_WNDPROC, (LONG)ListControlProc);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCMD:
::bSniffer = !::bSniffer;
if(::bSniffer)
{
//开始嗅探
SetDlgItemText(hDlg, IDCMD, L"暂停");
::hSniffThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainSniffer, NULL, 0, NULL);
} else {
//暂停嗅探
SetDlgItemText(hDlg, IDCMD, L"开始");
CloseHandle(::hSniffThread);
}
break;
case IDCLEAR://清理列表框
if (TRUE == bSniffer)
{
::bClearList = TRUE;
SendMessage(::hList, LVM_DELETEALLITEMS, 0, 0);
}
else
{
SendMessage(::hList, LVM_DELETEALLITEMS, 0, 0);
}
SetDlgItemText(hDlg, IDC_EDIT_DATA, wcLogo);
iMaxList = 0;
}
break;
case WM_CLOSE:
if(::bSniffer)
{
if(IDOK != MessageBox(hDlg, L"程序正在嗅探中,确定要退出?", L"提示", MB_OKCANCEL | MB_ICONWARNING))
return TRUE;
::bSniffer = FALSE;
CloseHandle(::hSniffThread);
}
EndDialog(hDlg, 0);
}
return FALSE;
}
我就不一一具体讲解了,主要是在嗅探时创建一个嗅探线程,嗅探结束时关闭线程。
另外,接收WM_INITDIALOG消息时执行InitListView();函数,这个函数的作用主要是初始化ListView,以下是源代码:
void InitListView()
{
//初始化小图标
SendDlgItemMessage(::hWnd,IDC_LIST_PACKATE, LVM_SETEXTENDEDLISTVIEWSTYLE, 0,
LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_SUBITEMIMAGES | LVS_EX_GRIDLINES);
SendDlgItemMessage(::hWnd,IDC_LIST_PACKATE, LVM_SETIMAGELIST,LVSIL_SMALL,(LPARAM)hImageListSmall);
//初始化报表头
LVCOLUMN column;
HWND hList = GetDlgItem(::hWnd, IDC_LIST_PACKATE);
column.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH;
column.fmt = LVCFMT_LEFT | LVCF_WIDTH;
column.pszText = NULL;
column.cx = 25;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_INSERTCOLUMN, 0, (LPARAM)&column);
column.pszText = L"源地址";
column.cx = 160;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_INSERTCOLUMN, 1, (LPARAM)&column);
column.pszText = L"源端口";
column.cx = 75;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_INSERTCOLUMN, 2, (LPARAM)&column);
column.pszText = L"目标地址";
column.cx = 160;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_INSERTCOLUMN, 3, (LPARAM)&column);
column.pszText = L"目标端口";
column.cx = 75;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_INSERTCOLUMN, 4, (LPARAM)&column);
column.pszText = L"协议类型";
column.cx = 80;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_INSERTCOLUMN, 5, (LPARAM)&column);
}
为了能响应ListView的双击消息,还需要做个消息钩子或消息子类,在hanDialogFunc函数接收WM_INITDIALOG消息时创建了一个消息子类。以下是子类函数:
LRESULT CALLBACK ListControlProc(HWND lhList, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(WM_LBUTTONDBLCLK == uMsg)//左键双击
{
int iHotItem = ListView_GetNextItem(lhList, -1, LVNI_SELECTED);
iHotItem = ListView_GetItemCount(lhList) - iHotItem-1;
DWORD dlLen = (DWORD)::dDataPtr[iHotItem+1] - (DWORD)::dDataPtr[iHotItem];
FormatAllString(::cfBuff, (BYTE *)::dDataPtr[iHotItem], dlLen);
SetDlgItemTextA(::hWnd, IDC_EDIT_DATA, ::cfBuff);
}
//一定要这么加,只处理需要的消息,不需要的返回给父窗口
return CallWindowProc((WNDPROC)::dListProc, lhList, uMsg, wParam, lParam);
}
这个函数中的FormatAllString是格式化字符串函数,,我写的比较简陋,,
BOOL FormatOneString(char *pNewString, BYTE *pOldString, int iMomentLength)
{
sprintf(pNewString,
"%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X | %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c",
(UCHAR)(iMomentLength>=0?pOldString[0]:0),
(UCHAR)(iMomentLength>=1?pOldString[1]:0),
(UCHAR)(iMomentLength>=2?pOldString[2]:0),
(UCHAR)(iMomentLength>=3?pOldString[3]:0),
(UCHAR)(iMomentLength>=4?pOldString[4]:0),
(UCHAR)(iMomentLength>=5?pOldString[5]:0),
(UCHAR)(iMomentLength>=6?pOldString[6]:0),
(UCHAR)(iMomentLength>=7?pOldString[7]:0),
(UCHAR)(iMomentLength>=8?pOldString[8]:0),
(UCHAR)(iMomentLength>=9?pOldString[9]:0),
(UCHAR)(iMomentLength>=10?pOldString[10]:0),
(UCHAR)(iMomentLength>=11?pOldString[11]:0),
(UCHAR)(iMomentLength>=12?pOldString[12]:0),
(UCHAR)(iMomentLength>=13?pOldString[13]:0),
(UCHAR)(iMomentLength>=14?pOldString[14]:0),
(UCHAR)(iMomentLength>=15?pOldString[15]:0),
iMomentLength>=0?(pOldString[0]>=32?pOldString[0]:46):46,
iMomentLength>=1?(pOldString[1]>=32?pOldString[1]:46):46,
iMomentLength>=2?(pOldString[2]>=32?pOldString[2]:46):46,
iMomentLength>=3?(pOldString[3]>=32?pOldString[3]:46):46,
iMomentLength>=4?(pOldString[4]>=32?pOldString[4]:46):46,
iMomentLength>=5?(pOldString[5]>=32?pOldString[5]:46):46,
iMomentLength>=6?(pOldString[6]>=32?pOldString[6]:46):46,
iMomentLength>=7?(pOldString[7]>=32?pOldString[7]:46):46,
iMomentLength>=8?(pOldString[8]>=32?pOldString[8]:46):46,
iMomentLength>=9?(pOldString[9]>=32?pOldString[9]:46):46,
iMomentLength>=10?(pOldString[10]>=32?pOldString[10]:46):46,
iMomentLength>=11?(pOldString[11]>=32?pOldString[11]:46):46,
iMomentLength>=12?(pOldString[12]>=32?pOldString[12]:46):46,
iMomentLength>=13?(pOldString[13]>=32?pOldString[13]:46):46,
iMomentLength>=14?(pOldString[14]>=32?pOldString[14]:46):46,
iMomentLength>=15?(pOldString[15]>=32?pOldString[15]:46):46
);
return TRUE;
}
BOOL FormatAllString(char *pNewString, BYTE *pOldString, int iMaxLength)
{
BOOL IsOneLine = TRUE;
for(int i=0; i<iMaxLength; i+=16, pNewString+=68, pOldString+=16)
{
if(IsOneLine)
{
IsOneLine = FALSE;
}
else
{
*pNewString++ = 13;
*pNewString++ = 10;
}
FormatOneString(pNewString, pOldString, (iMaxLength-i)>16 ? 16 : (iMaxLength-i));
}
*pNewString = '\0';
return TRUE;
}
界面UI部分贴的差不多了,以下是实现部分。
首先是主嗅探线程:
void StartupError(int iCode)
{
static wchar_t buff[20];
if(1 == iCode)
{
MessageBox(NULL,L"网络中断!", L"提示", MB_ICONHAND);
}
else
{
wsprintf((LPWSTR)buff, L"程序初始化失败!错误号:%d", iCode);
MessageBox(NULL, (LPCWSTR)buff, L"提示", MB_ICONHAND);
}
SendMessage(::hWnd, WM_COMMAND, (WPARAM)IDCMD, 0);
}
void MainSniffer()
{
/
SOCKET sock;
SOCKADDR_IN addr_in;
IpHeader Lip;
TcpHeader Ltcp;
char RecvBuf[BUFFER_SIZE];
WSADATA WSAData;
BOOL flag = true;
int nTimeout = 1000;
char LocalName[16];
struct hostent *pHost;
::iMaxList = 0;
if( NULL == dDataPtr[0])
{
StartupError(0);
return;
}
// 检查 Winsock 版本号
if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
{
StartupError(0);
return;
}
// 初始化 Raw Socket
if ((sock = socket(AF_INET, SOCK_RAW,IPPROTO_IP)) == INVALID_SOCKET)
{
StartupError(0);
return;
}
// 设置IP头操作选项
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)) == SOCKET_ERROR)
{
StartupError(0);
return;
}
// 获取本机名
if (gethostname((char*)LocalName, 16) == SOCKET_ERROR)
{
StartupError(0);
return;
}
// 获取本地 IP 地址
if ((pHost = gethostbyname((char*)LocalName)) == NULL)
{
StartupError(0);
return;
}
addr_in.sin_addr = *(in_addr *)pHost->h_addr_list[0]; //IP
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(52013);//57274
// 把 sock 绑定到本地地址上
if (bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
{
StartupError(0);
return;
}
DWORD dwValue = 1;
// 设置 SOCK_RAW 为SIO_RCVALL,以便接收所有的IP包
if (ioctlsocket(sock, SIO_RCVALL, &dwValue) != 0)
{
StartupError(0);
return;
}
LVITEM item;
//消息接收循环
while (::bSniffer)
{
if(::iMaxList >= 999 | (::dDataPtr[::iMaxList] - ::dDataPtr[0] >= 6550000))
{
MessageBox(NULL, L"程序分配内存空间已达最大限度!", L"提示", MB_ICONHAND);
}
int ret = recv(sock, RecvBuf, BUFFER_SIZE, 0);
if(SOCKET_ERROR == ret)
{
continue;
}
else if(!ret)
{
//网络中断
StartupError(1);
return;
}
//清理列表框
if(TRUE == bClearList)
{
bClearList = FALSE;
::iMaxList = 0;
}
Lip = *(IpHeader *)RecvBuf;
Ltcp = *(TcpHeader *)(RecvBuf + Lip.HdrLen);
item.lParam = item.iItem = 0;
item.iSubItem = 0;//nLine
//选择图标
switch(Lip.Protocol)
{
case IPPROTO_IP:
item.iImage = iImageList[0];
break;
case IPPROTO_ICMP:
item.iImage = iImageList[1];
break;
case IPPROTO_IGMP:
item.iImage = iImageList[2];
break;
case IPPROTO_GGP:
item.iImage = iImageList[3];
break;
case IPPROTO_TCP:
item.iImage = iImageList[4];
break;
case IPPROTO_PUP:
item.iImage = iImageList[5];
break;
case IPPROTO_UDP:
item.iImage = iImageList[6];
break;
case IPPROTO_IDP:
item.iImage = iImageList[7];
break;
case IPPROTO_ND:
item.iImage = iImageList[8];
break;
default:
item.iImage = iImageList[9];
}
item.mask = LVIF_IMAGE | LVIF_PARAM;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_INSERTITEM, 0, (LPARAM)&item);//插入一行
item.mask = LVIF_TEXT;
char Tbuff[16] = {0};
wchar_t Lbuff[16] = {0};
item.iSubItem = 1;//源地址
lstrcpyA(Tbuff, inet_ntoa(*(in_addr*)&Lip.SrcAddr));
int cchWideChar = MultiByteToWideChar(CP_ACP, 0, Tbuff, -1, NULL, 0);
MultiByteToWideChar(CP_ACP, 0, Tbuff, -1, Lbuff, cchWideChar);
item.pszText = Lbuff;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_SETITEMTEXT, 0, (LPARAM)&item);
item.iSubItem = 2;//源端口
wsprintf(Lbuff, L"%d", Ltcp.SrcPort);
item.pszText = TEXT(buff);
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_SETITEMTEXT, 0, (LPARAM)&item);
item.iSubItem = 3;//目标地址
lstrcpyA(Tbuff , inet_ntoa(*(in_addr*)&Lip.DstAddr));
cchWideChar = MultiByteToWideChar(CP_ACP, 0, Tbuff, -1, NULL, 0);
MultiByteToWideChar(CP_ACP, 0, Tbuff, -1, Lbuff, cchWideChar);
item.pszText = Lbuff;
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_SETITEMTEXT, 0, (LPARAM)&item);
item.iSubItem = 4;//目标端口
wsprintf(Lbuff, L"%d", Ltcp.DstPort);
item.pszText = TEXT(buff);
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_SETITEMTEXT, 0, (LPARAM)&item);
item.iSubItem = 5;//协议类型
switch(Lip.Protocol)
{
case IPPROTO_IP:
item.pszText = L"IP";
break;
case IPPROTO_ICMP:
item.pszText = L"ICMP";
break;
case IPPROTO_IGMP:
item.pszText = L"IGMP";
break;
case IPPROTO_GGP:
item.pszText = L"GGP";
break;
case IPPROTO_TCP:
item.pszText = L"TCP";
break;
case IPPROTO_PUP:
item.pszText = L"PUP";
break;
case IPPROTO_UDP:
item.pszText = L"UDP";
break;
case IPPROTO_IDP:
item.pszText = L"IDP";
break;
case IPPROTO_ND:
item.pszText = L"ND";
break;
default:
item.pszText = L"UNKNOWN";
}
SendDlgItemMessage(::hWnd, IDC_LIST_PACKATE, LVM_SETITEMTEXT, 0, (LPARAM)&item);
int iDataLen = ntohs(Lip.TotalLen);
memcpy((void *)::dDataPtr[::iMaxList++], (void *)RecvBuf, (DWORD)iDataLen);//这儿的问题
::dDataPtr[::iMaxList] = ::dDataPtr[::iMaxList-1] + iDataLen;
}
}
具体的嗅探技术都差不多,简单的技术。
在判断协议时在ListView上面插入相应图片,使程序看起来更美观。
然后是定义部分:
#include <windows.h>
#include "hanSniff.h"
HINSTANCE Instance;
HIMAGELIST hImageListSmall;//TCP UDP ARP ICMP
HANDLE hSniffThread;
HWND hWnd, hList;
DWORD dListProc;//ListView原消息循环
int iMaxList;
int iImageList[10];
//#define IPPROTO_IP
//#define IPPROTO_ICMP
//#define IPPROTO_IGMP
//#define IPPROTO_GGP
//#define IPPROTO_TCP
//#define IPPROTO_PUP
//#define IPPROTO_UDP
//#define IPPROTO_IDP
//#define IPPROTO_ND
BOOL bSniffer = FALSE;
BOOL bClearList = FALSE;
DWORD dDataPtr[1000];
BYTE bDataBuf[6553600];
char cfBuff[286720];
wchar_t wcLogo[] = L"\r\n网络数据包捕获工具 v1.0\r\n\r\n\t网络数据包嗅探工具\r\n\r\n\r\n\t\t\t\t有目标就不累、等着我超越";
hanSniff.h的代码:
#include "resource.h"
#include <wchar.h>
#include <string.h>
#include <stdio.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
#include<sys/types.h>
//#include<sys/socket.h>
//#include <ws2tcpip.h>
//#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#define BUFFER_SIZE 65535
typedef struct _IpHeader{
union{
BYTE Version;
BYTE HdrLen;
};
BYTE ServiceType;
WORD TotalLen;
WORD ID;
union{
WORD Flags;
WORD FragOff;
};
BYTE TimeToLive;
BYTE Protocol;
WORD HdrChksum;
DWORD SrcAddr;
DWORD DstAddr;
BYTE Options;
}IpHeader, *PIpHeader;
//
// TCP Packet Structure
//
typedef struct _TcpHeader{
WORD SrcPort;
WORD DstPort;
DWORD SeqNum;
DWORD AckNum;
BYTE DataOff;
BYTE Flags;
WORD Window;
WORD Chksum;
WORD UrgPtr;
}TcpHeader, *PTcpHeader;
#define SIO_RCVALL 0x98000001
#define IP_HDRINCL 2
VS2008完整工程下载地址:http://download.youkuaiyun.com/detail/fawdlstty/4823596