http://blog.csdn.net/zjai2004/article/details/7603804
自己耗费了2个月的时间在,师傅的帮助下总算将这个功能实现出来了。下面对这个功能的实现进行一个简单的总结。希望这篇文章能给大家帮助。
首先,如果采用一般的网络通信过程中的原始套接字的功能来实现方法在wince6.0下是行不通的。如何通过套接字实现,网络上已经都讲得很清楚了,这里我就不多说了,大家可以自己去搜索。
实现过程:
1. 从wince自带的目录底下:C:\WINCE600\PUBLIC\COMMON\OAK\UTILS\NETLOG中获取netlog文件夹中自带的所有文件。使用它们来创建一个DLL工程,删掉和工程中自带的头文件,源文件,资源文件中的内容。将netlog文件夹中的文件都增加到工程中,包括netlog.def文件,或者保留 netlog.def然后用C:\WINCE600\PUBLIC\COMMON\OAK\UTILS\NETLOG中的netlog.def替代。另外在头文件中增加一个文件netlogioctl.h。因为这个是属于驱动文件,所以不需要资源文件。然后编译,
2. 出现了无法找到stadx.h的错误。解决办法:属性—C/C++—在预编译头中/创建/使用预编译头中选择“不使用预编译头”
3. F7出现查找不到npptypes.h的等4个错误。解决办法:属性/C/C++/常规/附加包含目录/中增加C:\WINCE600\PUBLIC\COMMON\OAK\INC
F7出现了124个错误。增加依赖库 修改属性/链接器/输入附加依赖项ndis.lib ;coredll.lib ; corelibc.lib; ole32.lib; oleaut32.lib; uuid.lib; commctrl.lib ;模块定义文件.\netlog.def修改成netlog.def;延迟加载的DLL中增加$(NOINHERIT); f7编译通过。
开始对程序进行修改:
1. 首先设置混杂模式和设置普通模式。即在netlogioctl.h中增加两个define
IOCTL_NETLOG_SETMODE 9 IOCTL_NETLOG_RESETMODE 10 这两个值分别对应着cestream.cpp在extern “c ” BOOL NLG_IOControl函数中的 case值里面的代码为
{
case IOCTL_NETLOG_SETMODE : //设置混杂模式
case IOCTL_NETLOG_RESETMODE: //恢复普通模式
{
HANDLE h;
DWORD dwFilter = NDIS_PACKET_TYPE_PROMISCUOUS;
if(dwCode == IOCTL_NETLOG_RESETMODE)
dwFilter = NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST | NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_BROADCAST;
RETAILMSG(1, (_T("IOCTL_NETLOG_SETMODE/IOCTL_NETLOG_SETMODE %s\r\n"), (WCHAR *)pBufIn));
h = CreateFile(
NDISUIO_DEVICE_NAME, // Object name.
0x00, // Desired access.
0x00, // Share Mode.
NULL, // Security Attr
OPEN_EXISTING, // Creation Disposition.
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // Flag and Attributes..
(HANDLE)INVALID_HANDLE_VALUE);
if (INVALID_HANDLE_VALUE != h)
{
DWORD dwWritten;
struct {
NDISUIO_SET_OID SetOid;
unsigned char uca[64];
} NdisUioSetOid;
// --- Set Authentication for the connection
NdisUioSetOid.SetOid.Oid = OID_GEN_CURRENT_PACKET_FILTER;
NdisUioSetOid.SetOid.ptcDeviceName = (WCHAR *)pBufIn;
*(PDWORD)NdisUioSetOid.SetOid.Data = dwFilter;
dwWritten = 0;
if (! DeviceIoControl(
h,
IOCTL_NDISUIO_SET_OID_VALUE,
&NdisUioSetOid,
sizeof(NdisUioSetOid),
NULL,
0,
&dwWritten,
NULL)) {
CloseHandle(h);
goto error;
}}
F7
这时候出现了18个错误。增加两个头文件
#include <ndis.h>
#include <nuiouser.h>
F7出现一个错误
wcsncpy(Globals.state.wszCapFileBaseName,(USHORT *)
pBufIn,dwLenIn);
这时候将USHORT 改成 wchar_t就可以了
这里主要是设置混杂模式,即全部抓包。
另外还需要设置
Case IOCTL_NETLOG_SET_FILTER: //设置过滤条件
case IOCTL_NETLOG_CAPTURE_FILTER_PACKET: //上传过滤的数据
两个值
2.PacketFilterAndDump(
IN PWSTR wszProtocolName,
IN PWSTR wszStreamName,
IN PSTR szDirection,
IN PBYTE pPacket,
IN DWORD cbPacket,
IN PWSTR wszPacketType)函数中的增加这段代码,表示抓包为IP包。
//判断是否存在VLAN标记,
int bVlan = 0;
int bQinQ = 0;
//2次嵌套
if(ntohs(*(short *)(pPacket + 12)) == 0x8100)
{
bVlan = 1;
if(ntohs(*(short *)(pPacket + 16)) == 0x8100)
{
bQinQ = 1;
}
}
int bOffset = 12;
bOffset += (bVlan + bQinQ) * 4;
short nType = ntohs(*(short *)(pPacket + bOffset));
//RETAILMSG(1,(TEXT("%x\r\n"), nType));
if(nType == 0x0800) //IP包
{
bOffset += 2;
//RETAILMSG(1,(TEXT("netlogPalen = %d bOffset = %d \r\n"), cbPacket, bOffset));
BOOL ret = Packet_analyze((char *)pPacket + bOffset, cbPacket - bOffset);
// if (ret == FALSE)
// {
// RETAILMSG(1,(TEXT("Failed\r\n")));
// }
}
// No adjustment Necassary.
// pFrame=pPacket;
// cbFrame=cbPacket;
return true;
3.开始编写上层的代码了。
主要有两个函数,大家参照我这几个函数来写吧,这些函数可以放置在一个cpp中
/*--------------------------------------------------------------------
【函数介绍】: 关闭监听,设置驱动为正常模式,卸载CXport驱动
【入口参数】: pWnd用于指定父窗口句柄
【出口参数】: (无)
【返回值】: TRUE:打开成功;FALSE:打开失败
---------------------------------------------------------------------*/
bool CTCPClient_CE::Close_Monit()
{
//设置线程退出事件
SetEvent(m_exitThreadEvent);
//设置网卡为正常模式
//获取网卡的名称
if (!DeviceIoControl(m_hDriver,
IOCTL_NETLOG_RESETMODE,
(LPBYTE)ADAPTER_NAME,//网卡名称
(_tcslen(ADAPTER_NAME) + 1) * sizeof(TCHAR),
NULL,
NULL,
NULL,
NULL
))
{
return FALSE;
}
//设置开始
if (!DeviceIoControl(m_hDriver,
IOCTL_NETLOG_STOP,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
))
{
return FALSE;
}
CloseHandle(m_hDriver); //关闭驱动
//调用CExport驱动,进行卸载
SendCommandToCxport(IOCTL_NETLOG_UNLOAD, NULL, NULL, NULL, NULL);
//卸载驱动
if(m_hDevice)
DeactivateDevice(m_hDevice);
}
/*--------------------------------------------------------------------
【函数介绍】: 用于打开客户端监听socket
【入口参数】: pWnd用于指定父窗口句柄
【出口参数】: (无)
【返回值】: TRUE:打开成功;FALSE:打开失败
---------------------------------------------------------------------*/
bool CTCPClient_CE::Open_Monit(CWnd * pWnd)
{
//初始化socket环境
//复位线程退出事件
ResetEvent(m_exitThreadEvent);
//存储父窗口句柄
m_pOwnerWnd = pWnd;
HOSTENT* pHost = NULL;
CHAR* pszIp = NULL;
unsigned long uLIpsource = 0;
SOCKADDR_IN Addr;
//创建TCP套接字
//m_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IP);//IPPROTO_IP == 0
CHAR szHostName[128] = {0};//名称
//根据名称找到对应的网卡
if(gethostname(szHostName, sizeof(szHostName))==0)
{
pHost = gethostbyname(szHostName);
if(pHost != NULL)
{
//获取IP值
pszIp = inet_ntoa(*(in_addr*)pHost->h_addr_list[0]);
uLIpsource = inet_addr(pszIp);
//设置参数填充SOCKADDR_IN结构
Addr.sin_addr = *(in_addr*)pHost->h_addr_list[0];
TRACE(_T("%d.%d.%d.%d"),Addr.sin_addr.S_un.S_un_b.s_b1,
Addr.sin_addr.S_un.S_un_b.s_b2,
Addr.sin_addr.S_un.S_un_b.s_b3,
Addr.sin_addr.S_un.S_un_b.s_b4);
Addr.sin_family = AF_INET;
Addr.sin_port =(USHORT)m_port;//htons(m_port);
//将原始的套接字SOCK绑定到本地网卡地址上
//if(bind(m_socket,(PSOCKADDR)&Addr, sizeof(Addr)) == SOCKET_ERROR)
//{
// AfxMessageBox(_T("Bind = false!"));
// closesocket(m_socket);
// return FALSE;
//}
CreateNetLog();//填写注册表
//调用CExport驱动
SendCommandToCxport(IOCTL_NETLOG_LOAD, NULL, NULL, NULL, NULL);
//激活驱动
BOOL Ret = SendCommandToDriver(NETLOG_DEVICE_KEY, NETLOG_LEGACY_NAME, ADAPTER_NAME);
if (Ret == FALSE)
{
//驱动打开失败
return FALSE;
}
}
else
{
AfxMessageBox(_T("pHost = NULL!"));
closesocket(m_socket);
return FALSE;
}
}
else
{
AfxMessageBox(_T("can't find host name!"));
closesocket(m_socket);
return FALSE;
}
if (m_socket == SOCKET_ERROR)
{
closesocket(m_socket);
return FALSE;
}
//创建通讯线程
m_tcpThreadHandle = CreateThread(NULL,0,Monitor_ThreadFunc,this,0,NULL);
if (m_tcpThreadHandle == NULL)
{
closesocket(m_socket);
return FALSE;
}
return TRUE;
}
// 创建Netlog驱动相关的注册表信息
BOOL CTCPClient_CE::CreateNetLog()
{
//手工添加注册表
HKEY hKey;
DWORD dwDisp = 0;
HRESULT hr = ERROR_SUCCESS;
CString strPath, strNlgDrvPath;
TCHAR szBuffer[_MAX_PATH];
static HANDLE hNlgLoadHandle = INVALID_HANDLE_VALUE;
HANDLE hNlgHandle = INVALID_HANDLE_VALUE;
DWORD dwRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Drivers\\Netlog"),
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisp);
if( dwRet != ERROR_SUCCESS )
{
//打开注册表失败
return FALSE;
}
::GetModuleFileName(AfxGetInstanceHandle(), szBuffer, _MAX_PATH);
strPath = szBuffer;
strPath = strPath.Left(strPath.ReverseFind(_T('\\')));
strPath += _T("\\netlog.dll");
strNlgDrvPath = _T("\\Windows\\netlog.dll");
DeleteFile(strNlgDrvPath);
CopyFile(strPath, strNlgDrvPath, FALSE);
//如果不是新建键值,则进行子键添加
//if (dwDisp != REG_OPENED_EXISTING_KEY)
{
//RETAILMSG(1, (_T("CreateReg DMM!")));
if (ERROR_SUCCESS != (hr = RegSetValueEx (hKey, L"Dll", 0, REG_SZ, (BYTE *)strNlgDrvPath.GetBuffer(0), strNlgDrvPath.GetLength() * 2))) {
RegCloseKey (hKey);
return FALSE;
}
DWORD dwValue = 0x41;
if (ERROR_SUCCESS != (hr = RegSetValueEx (hKey, L"Order", 0, REG_DWORD, (BYTE *)&dwValue, sizeof(DWORD)))) {
RegCloseKey (hKey);
return FALSE;
}
dwValue = 0;
if (ERROR_SUCCESS != (hr = RegSetValueEx (hKey, L"Index", 0, REG_DWORD, (BYTE *)&dwValue, sizeof(DWORD)))) {
RegCloseKey (hKey);
return FALSE;
}
if (ERROR_SUCCESS != (hr = RegSetValueEx (hKey, L"Prefix", 0, REG_SZ, (BYTE *)L"NLG", sizeof(L"NLG")))) {
RegCloseKey (hKey);
return FALSE;
}
}
RegCloseKey(hKey);
return TRUE;
}/*--------------------------------------------------------------------
【函数介绍】: 获取设置的IP
【入口参数】: IP1主站IP2从站
【出口参数】: (无)
【返回值】:
---------------------------------------------------------------------*/
void CTCPClient_CE::GetSet_IP(DWORD IP1,DWORD IP2, int port)
{
m_MainIP = IP1;
m_SecoIP = IP2;
m_filterIP.MainIP = ntohl(IP1);
m_filterIP.SeconIP = ntohl(IP2);
m_filterIP.nport = ntohs(port);
}
BOOL CTCPClient_CE::SendCommandToCxport(
IN DWORD cmd,
IN PVOID pIn,
IN DWORD cbIn,
OUT PVOID pOut,
IN DWORD cbOut)
{
DWORD cbReturned = 0;
// Failing to Activate is not fatal.
ce::auto_hdevice hDevice = ActivateDevice(CXPORT_DEVICE_KEY, NULL);
ce::auto_hfile hDriver = CreateFile(CXPORT_LEGACY_NAME, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if(!hDriver.valid())
{
RETAILMSG(1, (L"Unable to load %s, CreateFile failed 0x%x\n",CXPORT_LEGACY_NAME, GetLastError()));
return FALSE;
}
if (!DeviceIoControl(hDriver,
cmd,
pIn,
cbIn,
pOut,
cbOut,
&cbReturned,
NULL
))
{
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------
//发送命令到驱动--- 打开驱动
//szDeviceKey 驱动的名称,szLegacyName 驱动的路径,cmd命令
//pIn 输入的缓存区,cbIn缓存区的大小pcbReturned 返回值
//---------------------------------------------------------------------
BOOL CTCPClient_CE::SendCommandToDriver(LPCWSTR szDeviceKey, LPCWSTR szLegacyName,LPCWSTR szadaptername)
{
// Failing to Activate is not fatal.
DWORD nDw = 0;
m_hDevice = ActivateDevice(szDeviceKey, nDw);
if(m_hDevice == 0 || m_hDevice == INVALID_HANDLE_VALUE)
{
nDw = GetLastError();
}
m_hDriver = CreateFile(szLegacyName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if(m_hDriver == INVALID_HANDLE_VALUE)
{
RETAILMSG(1, (L"Unable to load %s, CreateFile failed 0x%x\n",szLegacyName,GetLastError()));
MessageBox(NULL, _T("Netlog驱动打开失败!"), _T("警告"), MB_OK);
return FALSE;
}
//设置混杂模式
//获取网卡的名称
if (!DeviceIoControl(m_hDriver,
IOCTL_NETLOG_SETMODE,
(LPBYTE)szadaptername,//网卡名称
(_tcslen(szadaptername) + 1) * sizeof(TCHAR),
NULL,
NULL,
NULL,
NULL
))
{
return FALSE;
}
//设置过滤条件
if (!DeviceIoControl(m_hDriver,
IOCTL_NETLOG_SET_FILTER,
(PVOID)&m_filterIP,
sizeof(STFilter_IP),
NULL,
NULL,
NULL,
NULL
))
{
return FALSE;
}
//设置开始
if (!DeviceIoControl(m_hDriver,
IOCTL_NETLOG_START,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
))
{
return FALSE;
}
return TRUE;
}
/*--------------------------------------------------------------------
【函数介绍】: 此线程用于监听TCP客户端通讯的事件,例如当接收到数据、
连接断开和通讯过程发生错误等事件
【入口参数】: lparam:无类型指针,可以通过此参数,向线程中传入需要用到的资源。
在这里我们将CTCPClient_CE类实例指针传进来
【出口参数】: (无)
【返回值】: 返回值没有特别的意义,在此我们将返回值设为。
---------------------------------------------------------------------*/
DWORD CTCPClient_CE::Monitor_ThreadFunc(LPVOID lparam)
{
CTCPClient_CE *pSocket;
//得到CTCPClient_CE实例指针
pSocket = (CTCPClient_CE*)lparam;
BYTE szBufer[30 * 1024];
while (TRUE)
{
//收到退出事件,结束线程
if (WaitForSingleObject(pSocket->m_exitThreadEvent,100) == WAIT_OBJECT_0)
{
break;
}
int recvLen;
ZeroMemory(szBufer, sizeof(szBufer));
//设置读取事件
if (!DeviceIoControl(pSocket->m_hDriver,
IOCTL_NETLOG_CAPTURE_FILTER_PACKET,
NULL,//
NULL,
szBufer,
sizeof(szBufer),
(LPDWORD)&recvLen,
NULL
))
{
return FALSE;
}
//打印缓冲区
if (recvLen != 0)
{
//过滤分析
BOOL ret = pSocket->Packet_analyze((const char *)szBufer, recvLen);
if (ret == TRUE)
{
//RETAILMSG(1, (_T("deal with Data complete.\r\n")));
}
else
{
//RETAILMSG(1, (_T("deal with Data complete flase.\r\n")));
}
}
Sleep(20);
}
RETAILMSG(1, (_T("Monitor_ThreadFunc Exit.\r\n")));
return 0;
}
4. /*--------------------------------------------------------------------
【函数介绍】: 获取IP包的数据
【入口参数】: buf抓取到的包,len包的长度
【出口参数】: (无)
【返回值】: TRUE:打开成功;FALSE:打开失败
---------------------------------------------------------------------*/
BOOL CTCPClient_CE::Get_IP_packet_Data(char * buf, int len)
{
ip_hdr * p_IPHdr = (ip_hdr *)buf;
int pack_len = 0;
tcp_hdr *pTCPHead;
udp_hdr *pUDPHead;
icmp_hdr *pICMPHead;
BYTE *pdata = NULL;
int HdrLen = 0;
BOOL Ret = FALSE;
//方向判断
BOOL DIR = FALSE;
if(m_filterIP.MainIP == p_IPHdr->srcaddr)
{
DIR = FALSE;
}
else
{
DIR = TRUE;
}
RETAILMSG(1, (_T(" MainIP = %x ,srcaddr = %x ,DIR = %x"), m_filterIP.MainIP, p_IPHdr->srcaddr, DIR));
HdrLen = p_IPHdr->ihl * 4;
pack_len = ntohs(p_IPHdr->tot_len);
pack_len -= HdrLen;
//RETAILMSG(1,(_T(" len = %d, Packlen = %d \r\n"), len, pack_len));
switch(p_IPHdr->protocol)//协议判断
{
case IPPROTO_ICMP:
{
pICMPHead=(icmp_hdr *)(buf+HdrLen);
//strL4.Format(" type:%d code:%d\n",pICMPHead->Type,pICMPHead->Code);
pdata=((BYTE *)pICMPHead) + sizeof(icmp_hdr);
pack_len -= sizeof(icmp_hdr);
}
break;
case IPPROTO_TCP:
{
pTCPHead=(tcp_hdr *)(buf+HdrLen);
HdrLen = pTCPHead->thl; //in fact only 4 bits
HdrLen *= 4;//TCP头的实际长度
pdata=((BYTE *)pTCPHead)+HdrLen;
pack_len -= HdrLen;
//TRACE(_T("HdrLen = %d, packinlen = %d"), HdrLen, pack_len);
//buf = (char *)pdata;
//memcpy(buf, pdata, len);
//len = pack_len;
RETAILMSG(1, (_T(" %d \r\n"), pack_len));
Ret = TRUE;
}
break;
case IPPROTO_UDP:
{
pUDPHead=(udp_hdr *)(buf+HdrLen);
pdata=((BYTE *)pUDPHead) + sizeof(udp_hdr);
pack_len -= sizeof(udp_hdr);
//buf = (char *) pdata;
//len = pack_len;
Ret = TRUE;
}
break;
}
// for (int i = 0; i<pack_len; i++)
// {
// RETAILMSG(1, (_T(" %x "), *(pdata + i)));
// }
if (pack_len != 0)
{
int inlen = 0;
if (DIR == FALSE)//
{
memcpy(m_MainMonitBuf + m_MainBufLen, pdata, pack_len);
m_MainBufLen += pack_len;
inlen = m_MainBufLen;
//主站
if(pdata)
{
OnRead(m_pOwnerWnd,(char *)m_MainMonitBuf, m_MainBufLen, DIR);
if(m_MainBufLen != 0)
{
memcpy(m_MainMonitBuf, m_MainMonitBuf + (inlen - m_MainBufLen), m_MainBufLen);//将剩余的拷贝到前面
}
memset(m_MainMonitBuf + m_MainBufLen, 0, (inlen - m_MainBufLen));//清空后面的
}
}
else
{
//从站
memcpy(m_SecoMonitBuf + m_SecoBufLen, pdata, pack_len);
m_SecoBufLen += pack_len;
inlen = m_SecoBufLen;
//从站
if(pdata)
{
OnRead(m_pOwnerWnd,(char *)m_SecoMonitBuf, m_SecoBufLen, DIR);
if(m_SecoBufLen != 0)
{
memcpy(m_SecoMonitBuf, m_SecoMonitBuf + (inlen - m_SecoBufLen), m_SecoBufLen);//将剩余的拷贝到前面
}
memset(m_SecoMonitBuf + m_SecoBufLen, 0, (inlen - m_SecoBufLen));//清空后面的
}
}
}
return Ret;
}
可能后面的函数有点乱,因为时间及也没好好整理,等以后有时间的时候设法弄成一个模块。有错误的地方,希望大家能多多包涵,谢谢大家