最近疯狂的在肝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也可以,不过因为我这是游戏,所以就没敢这么搞了。