C++抓包軟件 純SDK

最近寫了一個抓包軟件,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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章