最近寫了一個抓包軟件,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.csdn.net/detail/fawdlstty/4823596