最近疯狂的在肝Warframe,疯狂的挂生存模式,但是在开多人后总有人瞎玩搞乱,为了把他们踢出房间,我需要获取他们的ip。
简单?
一开始用过ipip.net出的一个工具,能够获取到tcp与udp连接的源地址、目的地址与PID。但是有个问题,就是他那个工具用着用着就莫名其妙卡死了,所以遂决定自己造个一样的轮子。
google之,发现了Microsoft已为我们准备好了GetExtendedTcpTable与GetExtendedUdpTable,照着文档写了写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | #pragma comment(lib, "iphlpapi.lib") { 	DWORD dwSize = 0; 	PMIB_TCPTABLE_OWNER_PID pTcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(sizeof(PMIB_TCPTABLE_OWNER_PID)); 	GetExtendedTcpTable(pTcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0); 	GetExtendedTcpTable(pTcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0); 	for (DWORD i = 0; i < pTcpTable->dwNumEntries; i++) 	{ 		std::cout << (DWORD)pTcpTable->table[i].dwState << std::endl; 		std::cout << (DWORD)pTcpTable->table[i].dwLocalAddr << std::endl; 		std::cout << (DWORD)pTcpTable->table[i].dwLocalPort << std::endl; 		std::cout << (DWORD)pTcpTable->table[i].dwRemoteAddr << std::endl; 		std::cout << (DWORD)pTcpTable->table[i].dwRemotePort << std::endl; 		std::cout << (DWORD)pTcpTable->table[i].dwOwningPid << std::endl; 		std::cout << "======================================" << std::endl; 	} }
   | 
 
以上是tcp的获取方式,其中代码忽略了错误判断(一点都不健壮)。udp的获取方式也是差不多的,使用udp的函数即可。
不简单!
然而我还是太天真,写udp的时候发现了一个问题:微软给的GetExtendedUdpTable返回的结构体中并不包含目的地址与端口,只包含本地地址与端口。返回的结构体如下:
1 2 3 4 5
   | typedef struct _MIB_UDPROW_OWNER_PID {   DWORD dwLocalAddr;   DWORD dwLocalPort;   DWORD dwOwningPid; } MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID;
  | 
 
我以为是我找错了API,然而换了GetUdpTable也不行,TCPView、ProcessHacker 甚至 netstat -ano 都没有提示udp的远程地址。又搜了搜,发现能够使用抓包的方式来获得远程的地址与端口,甚至有人修改了WINPCAP的驱动与wireshark,使得wireshark可以显示数据包的pid。
最后,我找到了LiveTcpUdpWatch,发现它能够实现之前提出的要求。他使用了Windows Event Track的技术,获取Windows Kernel中Tcp/Udp的日志,从而得到包含进程的pid、进程链接的本地与远程的地址与端口。大致代码如下(其实就是抄了抄官方给的源码):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
   | #define INITGUID #pragma comment(lib, "tdh.lib") DEFINE_GUID(  	UdpIpGuid, 	0xbf3a50c5, 	0xa9c9, 	0x4988, 	0xa0, 0x05, 0x2d, 0xf0, 0xb7, 0xc8, 0x0f, 0x80 ); int main(){          EVENT_TRACE_PROPERTIES* event_trace_prop = nullptr;      TCHAR logger_name[] = KERNEL_LOGGER_NAME;        TRACEHANDLE trace_handler = NULL;          const auto buffer_size = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(logger_name);     event_trace_prop = (EVENT_TRACE_PROPERTIES *)malloc(buffer_size);     memset(event_trace_prop, 0, buffer_size);          EVENT_TRACE_PROPERTIES* event_trace_prop;     event_trace_prop->Wnode.BufferSize = buffer_size;     event_trace_prop->Wnode.Guid = SystemTraceControlGuid;     event_trace_prop->Wnode.ClientContext = 2;     event_trace_prop->Wnode.Flags |= WNODE_FLAG_TRACED_GUID;     event_trace_prop->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;     event_trace_prop->EnableFlags = EVENT_TRACE_FLAG_NETWORK_TCPIP;     event_trace_prop->LogFileNameOffset = NULL;     event_trace_prop->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);          StartTrace(&trace_handler, logger_name, event_trace_prop);                      TDHSTATUS status = ERROR_SUCCESS;     EVENT_TRACE_LOGFILE trace;     TRACE_LOGFILE_HEADER* pHeader = &trace.LogfileHeader;     ZeroMemory(&trace, sizeof(EVENT_TRACE_LOGFILE));     trace.LogFileName = NULL;     trace.LoggerName = (LPWSTR)KERNEL_LOGGER_NAME;     trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK)(ProcessEvent);        trace.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_REAL_TIME;     g_hTrace = OpenTrace(&trace);     ProcessTrace(&g_hTrace, 1, 0, 0); }
  VOID WINAPI ProcessEvent(PEVENT_TRACE pEvent);
 
 
 
 
 
 
   | 
 
其他
hook掉相应程序WS2_32.dll的网络相关的api也可以,不过因为我这是游戏,所以就没敢这么搞了。