mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
a5360acec3
* fixed the IPv6-related workaround causing crashes git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4195 21ef857c-d57f-4fe0-8362-d861dc6d29cd
1062 lines
33 KiB
C
1062 lines
33 KiB
C
/*
|
|
* Process Hacker -
|
|
* network provider
|
|
*
|
|
* Copyright (C) 2010 wj32
|
|
* Copyright (C) 2010 evilpie
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define PH_NETPRV_PRIVATE
|
|
#include <phapp.h>
|
|
#include <ws2tcpip.h>
|
|
#include <ws2ipdef.h>
|
|
#include <iphlpapi.h>
|
|
|
|
typedef struct _PH_NETWORK_CONNECTION
|
|
{
|
|
ULONG ProtocolType;
|
|
PH_IP_ENDPOINT LocalEndpoint;
|
|
PH_IP_ENDPOINT RemoteEndpoint;
|
|
ULONG State;
|
|
HANDLE ProcessId;
|
|
ULONGLONG OwnerInfo[PH_NETWORK_OWNER_INFO_SIZE];
|
|
} PH_NETWORK_CONNECTION, *PPH_NETWORK_CONNECTION;
|
|
|
|
typedef struct _PH_NETWORK_ITEM_QUERY_DATA
|
|
{
|
|
SLIST_ENTRY ListEntry;
|
|
PPH_NETWORK_ITEM NetworkItem;
|
|
|
|
PH_IP_ADDRESS Address;
|
|
BOOLEAN Remote;
|
|
PPH_STRING HostString;
|
|
} PH_NETWORK_ITEM_QUERY_DATA, *PPH_NETWORK_ITEM_QUERY_DATA;
|
|
|
|
typedef struct _PHP_RESOLVE_CACHE_ITEM
|
|
{
|
|
PH_IP_ADDRESS Address;
|
|
PPH_STRING HostString;
|
|
} PHP_RESOLVE_CACHE_ITEM, *PPHP_RESOLVE_CACHE_ITEM;
|
|
|
|
typedef DWORD (WINAPI *_GetExtendedTcpTable)(
|
|
__out_bcount_opt(*pdwSize) PVOID pTcpTable,
|
|
__inout PDWORD pdwSize,
|
|
__in BOOL bOrder,
|
|
__in ULONG ulAf,
|
|
__in TCP_TABLE_CLASS TableClass,
|
|
__in ULONG Reserved
|
|
);
|
|
|
|
typedef DWORD (WINAPI *_GetExtendedUdpTable)(
|
|
__out_bcount_opt(*pdwSize) PVOID pUdpTable,
|
|
__inout PDWORD pdwSize,
|
|
__in BOOL bOrder,
|
|
__in ULONG ulAf,
|
|
__in UDP_TABLE_CLASS TableClass,
|
|
__in ULONG Reserved
|
|
);
|
|
|
|
typedef int (WSAAPI *_WSAStartup)(
|
|
__in WORD wVersionRequested,
|
|
__out LPWSADATA lpWSAData
|
|
);
|
|
|
|
typedef int (WSAAPI *_WSAGetLastError)();
|
|
|
|
typedef INT (WSAAPI *_GetNameInfoW)(
|
|
__in_bcount(SockaddrLength) const SOCKADDR *pSockaddr,
|
|
__in socklen_t SockaddrLength,
|
|
__out_ecount_opt(NodeBufferSize) PWCHAR pNodeBuffer,
|
|
__in DWORD NodeBufferSize,
|
|
__out_ecount_opt(ServiceBufferSize) PWCHAR pServiceBuffer,
|
|
__in DWORD ServiceBufferSize,
|
|
__in INT Flags
|
|
);
|
|
|
|
typedef struct hostent *(WSAAPI *_gethostbyaddr)(
|
|
__in_bcount(len) const char *addr,
|
|
__in int len,
|
|
__in int type
|
|
);
|
|
|
|
VOID NTAPI PhpNetworkItemDeleteProcedure(
|
|
__in PVOID Object,
|
|
__in ULONG Flags
|
|
);
|
|
|
|
BOOLEAN PhpNetworkHashtableCompareFunction(
|
|
__in PVOID Entry1,
|
|
__in PVOID Entry2
|
|
);
|
|
|
|
ULONG NTAPI PhpNetworkHashtableHashFunction(
|
|
__in PVOID Entry
|
|
);
|
|
|
|
BOOLEAN PhpResolveCacheHashtableCompareFunction(
|
|
__in PVOID Entry1,
|
|
__in PVOID Entry2
|
|
);
|
|
|
|
ULONG NTAPI PhpResolveCacheHashtableHashFunction(
|
|
__in PVOID Entry
|
|
);
|
|
|
|
BOOLEAN PhGetNetworkConnections(
|
|
__out PPH_NETWORK_CONNECTION *Connections,
|
|
__out PULONG NumberOfConnections
|
|
);
|
|
|
|
PPH_OBJECT_TYPE PhNetworkItemType;
|
|
|
|
PPH_HASHTABLE PhNetworkHashtable;
|
|
PH_QUEUED_LOCK PhNetworkHashtableLock = PH_QUEUED_LOCK_INIT;
|
|
|
|
PH_CALLBACK_DECLARE(PhNetworkItemAddedEvent);
|
|
PH_CALLBACK_DECLARE(PhNetworkItemModifiedEvent);
|
|
PH_CALLBACK_DECLARE(PhNetworkItemRemovedEvent);
|
|
PH_CALLBACK_DECLARE(PhNetworkItemsUpdatedEvent);
|
|
|
|
BOOLEAN PhEnableNetworkProviderResolve = TRUE;
|
|
|
|
PH_INITONCE PhNetworkProviderWorkQueueInitOnce = PH_INITONCE_INIT;
|
|
PH_WORK_QUEUE PhNetworkProviderWorkQueue;
|
|
SLIST_HEADER PhNetworkItemQueryListHead;
|
|
|
|
static PPH_HASHTABLE PhpResolveCacheHashtable;
|
|
static PH_QUEUED_LOCK PhpResolveCacheHashtableLock = PH_QUEUED_LOCK_INIT;
|
|
|
|
static BOOLEAN NetworkImportDone = FALSE;
|
|
static _GetExtendedTcpTable GetExtendedTcpTable_I;
|
|
static _GetExtendedUdpTable GetExtendedUdpTable_I;
|
|
static _WSAStartup WSAStartup_I;
|
|
static _WSAGetLastError WSAGetLastError_I;
|
|
static _GetNameInfoW GetNameInfoW_I;
|
|
static _gethostbyaddr gethostbyaddr_I;
|
|
|
|
BOOLEAN PhNetworkProviderInitialization()
|
|
{
|
|
if (!NT_SUCCESS(PhCreateObjectType(
|
|
&PhNetworkItemType,
|
|
L"NetworkItem",
|
|
0,
|
|
PhpNetworkItemDeleteProcedure
|
|
)))
|
|
return FALSE;
|
|
|
|
PhNetworkHashtable = PhCreateHashtable(
|
|
sizeof(PPH_NETWORK_ITEM),
|
|
PhpNetworkHashtableCompareFunction,
|
|
PhpNetworkHashtableHashFunction,
|
|
40
|
|
);
|
|
|
|
RtlInitializeSListHead(&PhNetworkItemQueryListHead);
|
|
|
|
PhpResolveCacheHashtable = PhCreateHashtable(
|
|
sizeof(PHP_RESOLVE_CACHE_ITEM),
|
|
PhpResolveCacheHashtableCompareFunction,
|
|
PhpResolveCacheHashtableHashFunction,
|
|
20
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PPH_NETWORK_ITEM PhCreateNetworkItem()
|
|
{
|
|
PPH_NETWORK_ITEM networkItem;
|
|
|
|
if (!NT_SUCCESS(PhCreateObject(
|
|
&networkItem,
|
|
sizeof(PH_NETWORK_ITEM),
|
|
0,
|
|
PhNetworkItemType
|
|
)))
|
|
return NULL;
|
|
|
|
memset(networkItem, 0, sizeof(PH_NETWORK_ITEM));
|
|
|
|
return networkItem;
|
|
}
|
|
|
|
VOID NTAPI PhpNetworkItemDeleteProcedure(
|
|
__in PVOID Object,
|
|
__in ULONG Flags
|
|
)
|
|
{
|
|
PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Object;
|
|
|
|
if (networkItem->ProcessName)
|
|
PhDereferenceObject(networkItem->ProcessName);
|
|
if (networkItem->OwnerName)
|
|
PhDereferenceObject(networkItem->OwnerName);
|
|
if (networkItem->LocalHostString)
|
|
PhDereferenceObject(networkItem->LocalHostString);
|
|
if (networkItem->RemoteHostString)
|
|
PhDereferenceObject(networkItem->RemoteHostString);
|
|
}
|
|
|
|
BOOLEAN PhpNetworkHashtableCompareFunction(
|
|
__in PVOID Entry1,
|
|
__in PVOID Entry2
|
|
)
|
|
{
|
|
PPH_NETWORK_ITEM networkItem1 = *(PPH_NETWORK_ITEM *)Entry1;
|
|
PPH_NETWORK_ITEM networkItem2 = *(PPH_NETWORK_ITEM *)Entry2;
|
|
|
|
return
|
|
networkItem1->ProtocolType == networkItem2->ProtocolType &&
|
|
PhEqualIpEndpoint(&networkItem1->LocalEndpoint, &networkItem2->LocalEndpoint) &&
|
|
PhEqualIpEndpoint(&networkItem1->RemoteEndpoint, &networkItem2->RemoteEndpoint) &&
|
|
networkItem1->ProcessId == networkItem2->ProcessId;
|
|
}
|
|
|
|
ULONG NTAPI PhpNetworkHashtableHashFunction(
|
|
__in PVOID Entry
|
|
)
|
|
{
|
|
PPH_NETWORK_ITEM networkItem = *(PPH_NETWORK_ITEM *)Entry;
|
|
|
|
return
|
|
networkItem->ProtocolType ^
|
|
PhHashIpEndpoint(&networkItem->LocalEndpoint) ^
|
|
PhHashIpEndpoint(&networkItem->RemoteEndpoint) ^
|
|
(ULONG)networkItem->ProcessId;
|
|
}
|
|
|
|
PPH_NETWORK_ITEM PhReferenceNetworkItem(
|
|
__in ULONG ProtocolType,
|
|
__in PPH_IP_ENDPOINT LocalEndpoint,
|
|
__in PPH_IP_ENDPOINT RemoteEndpoint,
|
|
__in HANDLE ProcessId
|
|
)
|
|
{
|
|
PH_NETWORK_ITEM lookupNetworkItem;
|
|
PPH_NETWORK_ITEM lookupNetworkItemPtr = &lookupNetworkItem;
|
|
PPH_NETWORK_ITEM *networkItemPtr;
|
|
PPH_NETWORK_ITEM networkItem;
|
|
|
|
lookupNetworkItem.ProtocolType = ProtocolType;
|
|
lookupNetworkItem.LocalEndpoint = *LocalEndpoint;
|
|
lookupNetworkItem.RemoteEndpoint = *RemoteEndpoint;
|
|
lookupNetworkItem.ProcessId = ProcessId;
|
|
|
|
PhAcquireQueuedLockShared(&PhNetworkHashtableLock);
|
|
|
|
networkItemPtr = (PPH_NETWORK_ITEM *)PhFindEntryHashtable(
|
|
PhNetworkHashtable,
|
|
&lookupNetworkItemPtr
|
|
);
|
|
|
|
if (networkItemPtr)
|
|
{
|
|
networkItem = *networkItemPtr;
|
|
PhReferenceObject(networkItem);
|
|
}
|
|
else
|
|
{
|
|
networkItem = NULL;
|
|
}
|
|
|
|
PhReleaseQueuedLockShared(&PhNetworkHashtableLock);
|
|
|
|
return networkItem;
|
|
}
|
|
|
|
__assumeLocked VOID PhpRemoveNetworkItem(
|
|
__in PPH_NETWORK_ITEM NetworkItem
|
|
)
|
|
{
|
|
PhRemoveEntryHashtable(PhNetworkHashtable, &NetworkItem);
|
|
PhDereferenceObject(NetworkItem);
|
|
}
|
|
|
|
BOOLEAN PhpResolveCacheHashtableCompareFunction(
|
|
__in PVOID Entry1,
|
|
__in PVOID Entry2
|
|
)
|
|
{
|
|
PPHP_RESOLVE_CACHE_ITEM cacheItem1 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry1;
|
|
PPHP_RESOLVE_CACHE_ITEM cacheItem2 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry2;
|
|
|
|
return PhEqualIpAddress(&cacheItem1->Address, &cacheItem2->Address);
|
|
}
|
|
|
|
ULONG NTAPI PhpResolveCacheHashtableHashFunction(
|
|
__in PVOID Entry
|
|
)
|
|
{
|
|
PPHP_RESOLVE_CACHE_ITEM cacheItem = *(PPHP_RESOLVE_CACHE_ITEM *)Entry;
|
|
|
|
return PhHashIpAddress(&cacheItem->Address);
|
|
}
|
|
|
|
__assumeLocked PPHP_RESOLVE_CACHE_ITEM PhpLookupResolveCacheItem(
|
|
__in PPH_IP_ADDRESS Address
|
|
)
|
|
{
|
|
PHP_RESOLVE_CACHE_ITEM lookupCacheItem;
|
|
PPHP_RESOLVE_CACHE_ITEM lookupCacheItemPtr = &lookupCacheItem;
|
|
PPHP_RESOLVE_CACHE_ITEM *cacheItemPtr;
|
|
|
|
// Construct a temporary cache item for the lookup.
|
|
lookupCacheItem.Address = *Address;
|
|
|
|
cacheItemPtr = (PPHP_RESOLVE_CACHE_ITEM *)PhFindEntryHashtable(
|
|
PhpResolveCacheHashtable,
|
|
&lookupCacheItemPtr
|
|
);
|
|
|
|
if (cacheItemPtr)
|
|
return *cacheItemPtr;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
PPH_STRING PhGetHostNameFromAddress(
|
|
__in PPH_IP_ADDRESS Address
|
|
)
|
|
{
|
|
struct sockaddr_in ipv4Address;
|
|
struct sockaddr_in6 ipv6Address;
|
|
struct sockaddr *address;
|
|
socklen_t length;
|
|
PPH_STRING hostName;
|
|
|
|
if (!GetNameInfoW_I)
|
|
return NULL;
|
|
|
|
if (Address->Type == PH_IPV4_NETWORK_TYPE)
|
|
{
|
|
ipv4Address.sin_family = AF_INET;
|
|
ipv4Address.sin_port = 0;
|
|
ipv4Address.sin_addr = Address->InAddr;
|
|
address = (struct sockaddr *)&ipv4Address;
|
|
length = sizeof(ipv4Address);
|
|
}
|
|
else if (Address->Type == PH_IPV6_NETWORK_TYPE)
|
|
{
|
|
ipv6Address.sin6_family = AF_INET6;
|
|
ipv6Address.sin6_port = 0;
|
|
ipv6Address.sin6_flowinfo = 0;
|
|
ipv6Address.sin6_addr = Address->In6Addr;
|
|
ipv6Address.sin6_scope_id = 0;
|
|
address = (struct sockaddr *)&ipv6Address;
|
|
length = sizeof(ipv6Address);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
hostName = PhCreateStringEx(NULL, 128);
|
|
|
|
if (GetNameInfoW_I(
|
|
address,
|
|
length,
|
|
hostName->Buffer,
|
|
hostName->Length / 2 + 1,
|
|
NULL,
|
|
0,
|
|
NI_NAMEREQD
|
|
) != 0)
|
|
{
|
|
// Try with the maximum host name size.
|
|
PhDereferenceObject(hostName);
|
|
hostName = PhCreateStringEx(NULL, NI_MAXHOST * 2);
|
|
|
|
if (GetNameInfoW_I(
|
|
address,
|
|
length,
|
|
hostName->Buffer,
|
|
hostName->Length / 2 + 1,
|
|
NULL,
|
|
0,
|
|
NI_NAMEREQD
|
|
) != 0)
|
|
{
|
|
PhDereferenceObject(hostName);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
PhTrimToNullTerminatorString(hostName);
|
|
|
|
return hostName;
|
|
}
|
|
|
|
NTSTATUS PhpNetworkItemQueryWorker(
|
|
__in PVOID Parameter
|
|
)
|
|
{
|
|
PPH_NETWORK_ITEM_QUERY_DATA data = (PPH_NETWORK_ITEM_QUERY_DATA)Parameter;
|
|
PPH_STRING hostString;
|
|
PPHP_RESOLVE_CACHE_ITEM cacheItem;
|
|
|
|
// Last minute check of the cache.
|
|
|
|
PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock);
|
|
cacheItem = PhpLookupResolveCacheItem(&data->Address);
|
|
PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock);
|
|
|
|
if (!cacheItem)
|
|
{
|
|
hostString = PhGetHostNameFromAddress(&data->Address);
|
|
|
|
if (hostString)
|
|
{
|
|
data->HostString = hostString;
|
|
|
|
// Update the cache.
|
|
|
|
PhAcquireQueuedLockExclusive(&PhpResolveCacheHashtableLock);
|
|
|
|
cacheItem = PhpLookupResolveCacheItem(&data->Address);
|
|
|
|
if (!cacheItem)
|
|
{
|
|
cacheItem = PhAllocate(sizeof(PHP_RESOLVE_CACHE_ITEM));
|
|
cacheItem->Address = data->Address;
|
|
cacheItem->HostString = hostString;
|
|
PhReferenceObject(hostString);
|
|
|
|
PhAddEntryHashtable(PhpResolveCacheHashtable, &cacheItem);
|
|
}
|
|
|
|
PhReleaseQueuedLockExclusive(&PhpResolveCacheHashtableLock);
|
|
}
|
|
else
|
|
{
|
|
dprintf("resolve failed, error %u\n", WSAGetLastError_I());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PhReferenceObject(cacheItem->HostString);
|
|
data->HostString = cacheItem->HostString;
|
|
}
|
|
|
|
RtlInterlockedPushEntrySList(&PhNetworkItemQueryListHead, &data->ListEntry);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID PhpQueueNetworkItemQuery(
|
|
__in PPH_NETWORK_ITEM NetworkItem,
|
|
__in BOOLEAN Remote
|
|
)
|
|
{
|
|
PPH_NETWORK_ITEM_QUERY_DATA data;
|
|
|
|
if (!PhEnableNetworkProviderResolve)
|
|
return;
|
|
|
|
data = PhAllocate(sizeof(PH_NETWORK_ITEM_QUERY_DATA));
|
|
memset(data, 0, sizeof(PH_NETWORK_ITEM_QUERY_DATA));
|
|
data->NetworkItem = NetworkItem;
|
|
data->Remote = Remote;
|
|
|
|
if (Remote)
|
|
data->Address = NetworkItem->RemoteEndpoint.Address;
|
|
else
|
|
data->Address = NetworkItem->LocalEndpoint.Address;
|
|
|
|
PhReferenceObject(NetworkItem);
|
|
|
|
if (PhBeginInitOnce(&PhNetworkProviderWorkQueueInitOnce))
|
|
{
|
|
PhInitializeWorkQueue(&PhNetworkProviderWorkQueue, 0, 3, 500);
|
|
PhEndInitOnce(&PhNetworkProviderWorkQueueInitOnce);
|
|
}
|
|
|
|
PhQueueItemWorkQueue(&PhNetworkProviderWorkQueue, PhpNetworkItemQueryWorker, data);
|
|
}
|
|
|
|
VOID PhpUpdateNetworkItemOwner(
|
|
__in PPH_NETWORK_ITEM NetworkItem,
|
|
__in PPH_PROCESS_ITEM ProcessItem
|
|
)
|
|
{
|
|
if (*(PULONG64)NetworkItem->OwnerInfo)
|
|
{
|
|
PVOID serviceTag;
|
|
PPH_STRING serviceName;
|
|
|
|
// May change in the future...
|
|
serviceTag = (PVOID)*(PULONG)NetworkItem->OwnerInfo;
|
|
serviceName = PhGetServiceNameFromTag(NetworkItem->ProcessId, serviceTag);
|
|
|
|
if (serviceName)
|
|
PhSwapReference2(&NetworkItem->OwnerName, serviceName);
|
|
}
|
|
}
|
|
|
|
VOID PhNetworkProviderUpdate(
|
|
__in PVOID Object
|
|
)
|
|
{
|
|
PPH_NETWORK_CONNECTION connections;
|
|
ULONG numberOfConnections;
|
|
ULONG i;
|
|
|
|
if (!NetworkImportDone)
|
|
{
|
|
WSADATA wsaData;
|
|
HMODULE iphlpapi;
|
|
HMODULE ws2_32;
|
|
|
|
iphlpapi = LoadLibrary(L"iphlpapi.dll");
|
|
GetExtendedTcpTable_I = (PVOID)GetProcAddress(iphlpapi, "GetExtendedTcpTable");
|
|
GetExtendedUdpTable_I = (PVOID)GetProcAddress(iphlpapi, "GetExtendedUdpTable");
|
|
ws2_32 = LoadLibrary(L"ws2_32.dll");
|
|
WSAStartup_I = (PVOID)GetProcAddress(ws2_32, "WSAStartup");
|
|
WSAGetLastError_I = (PVOID)GetProcAddress(ws2_32, "WSAGetLastError");
|
|
GetNameInfoW_I = (PVOID)GetProcAddress(ws2_32, "GetNameInfoW");
|
|
gethostbyaddr_I = (PVOID)GetProcAddress(ws2_32, "gethostbyaddr");
|
|
|
|
// Make sure WSA is initialized.
|
|
if (WSAStartup_I)
|
|
{
|
|
WSAStartup_I(MAKEWORD(2, 2), &wsaData);
|
|
}
|
|
|
|
NetworkImportDone = TRUE;
|
|
}
|
|
|
|
if (!PhGetNetworkConnections(&connections, &numberOfConnections))
|
|
return;
|
|
|
|
{
|
|
PPH_LIST connectionsToRemove = NULL;
|
|
PH_HASHTABLE_ENUM_CONTEXT enumContext;
|
|
PPH_NETWORK_ITEM *networkItem;
|
|
|
|
PhBeginEnumHashtable(PhNetworkHashtable, &enumContext);
|
|
|
|
while (networkItem = PhNextEnumHashtable(&enumContext))
|
|
{
|
|
BOOLEAN found = FALSE;
|
|
|
|
for (i = 0; i < numberOfConnections; i++)
|
|
{
|
|
if (
|
|
(*networkItem)->ProtocolType == connections[i].ProtocolType &&
|
|
PhEqualIpEndpoint(&(*networkItem)->LocalEndpoint, &connections[i].LocalEndpoint) &&
|
|
PhEqualIpEndpoint(&(*networkItem)->RemoteEndpoint, &connections[i].RemoteEndpoint) &&
|
|
(*networkItem)->ProcessId == connections[i].ProcessId
|
|
)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
PhInvokeCallback(&PhNetworkItemRemovedEvent, *networkItem);
|
|
|
|
if (!connectionsToRemove)
|
|
connectionsToRemove = PhCreateList(2);
|
|
|
|
PhAddItemList(connectionsToRemove, *networkItem);
|
|
}
|
|
}
|
|
|
|
if (connectionsToRemove)
|
|
{
|
|
PhAcquireQueuedLockExclusive(&PhNetworkHashtableLock);
|
|
|
|
for (i = 0; i < connectionsToRemove->Count; i++)
|
|
{
|
|
PhpRemoveNetworkItem(connectionsToRemove->Items[i]);
|
|
}
|
|
|
|
PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock);
|
|
PhDereferenceObject(connectionsToRemove);
|
|
}
|
|
}
|
|
|
|
// Go through the queued network item query data.
|
|
{
|
|
PSLIST_ENTRY entry;
|
|
PPH_NETWORK_ITEM_QUERY_DATA data;
|
|
|
|
entry = RtlInterlockedFlushSList(&PhNetworkItemQueryListHead);
|
|
|
|
while (entry)
|
|
{
|
|
data = CONTAINING_RECORD(entry, PH_NETWORK_ITEM_QUERY_DATA, ListEntry);
|
|
entry = entry->Next;
|
|
|
|
if (data->Remote)
|
|
PhSwapReference2(&data->NetworkItem->RemoteHostString, data->HostString);
|
|
else
|
|
PhSwapReference2(&data->NetworkItem->LocalHostString, data->HostString);
|
|
|
|
data->NetworkItem->JustResolved = TRUE;
|
|
|
|
PhDereferenceObject(data->NetworkItem);
|
|
PhFree(data);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < numberOfConnections; i++)
|
|
{
|
|
PPH_NETWORK_ITEM networkItem;
|
|
|
|
// Try to find the connection in our hashtable.
|
|
networkItem = PhReferenceNetworkItem(
|
|
connections[i].ProtocolType,
|
|
&connections[i].LocalEndpoint,
|
|
&connections[i].RemoteEndpoint,
|
|
connections[i].ProcessId
|
|
);
|
|
|
|
if (!networkItem)
|
|
{
|
|
PPHP_RESOLVE_CACHE_ITEM cacheItem;
|
|
PPH_PROCESS_ITEM processItem;
|
|
|
|
// Network item not found, create it.
|
|
|
|
networkItem = PhCreateNetworkItem();
|
|
|
|
// Fill in basic information.
|
|
networkItem->ProtocolType = connections[i].ProtocolType;
|
|
networkItem->LocalEndpoint = connections[i].LocalEndpoint;
|
|
networkItem->RemoteEndpoint = connections[i].RemoteEndpoint;
|
|
networkItem->State = connections[i].State;
|
|
networkItem->ProcessId = connections[i].ProcessId;
|
|
memcpy(networkItem->OwnerInfo, connections[i].OwnerInfo, sizeof(ULONGLONG) * PH_NETWORK_OWNER_INFO_SIZE);
|
|
|
|
// Format various strings.
|
|
|
|
if (networkItem->LocalEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE)
|
|
RtlIpv4AddressToString(&networkItem->LocalEndpoint.Address.InAddr, networkItem->LocalAddressString);
|
|
else
|
|
RtlIpv6AddressToString(&networkItem->LocalEndpoint.Address.In6Addr, networkItem->LocalAddressString);
|
|
|
|
PhPrintUInt32(networkItem->LocalPortString, networkItem->LocalEndpoint.Port);
|
|
|
|
if (
|
|
networkItem->RemoteEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE &&
|
|
networkItem->RemoteEndpoint.Address.Ipv4 != 0
|
|
)
|
|
{
|
|
RtlIpv4AddressToString(&networkItem->RemoteEndpoint.Address.InAddr, networkItem->RemoteAddressString);
|
|
}
|
|
else if (
|
|
networkItem->RemoteEndpoint.Address.Type == PH_IPV6_NETWORK_TYPE &&
|
|
!PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address)
|
|
)
|
|
{
|
|
RtlIpv6AddressToString(&networkItem->RemoteEndpoint.Address.In6Addr, networkItem->RemoteAddressString);
|
|
}
|
|
|
|
if (networkItem->RemoteEndpoint.Address.Type != 0 && networkItem->RemoteEndpoint.Port != 0)
|
|
PhPrintUInt32(networkItem->RemotePortString, networkItem->RemoteEndpoint.Port);
|
|
|
|
// Get host names.
|
|
|
|
// Local
|
|
{
|
|
PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock);
|
|
cacheItem = PhpLookupResolveCacheItem(&networkItem->LocalEndpoint.Address);
|
|
PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock);
|
|
|
|
if (cacheItem)
|
|
{
|
|
PhReferenceObject(cacheItem->HostString);
|
|
networkItem->LocalHostString = cacheItem->HostString;
|
|
}
|
|
else
|
|
{
|
|
PhpQueueNetworkItemQuery(networkItem, FALSE);
|
|
}
|
|
}
|
|
|
|
// Remote
|
|
if (!PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address))
|
|
{
|
|
PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock);
|
|
cacheItem = PhpLookupResolveCacheItem(&networkItem->RemoteEndpoint.Address);
|
|
PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock);
|
|
|
|
if (cacheItem)
|
|
{
|
|
PhReferenceObject(cacheItem->HostString);
|
|
networkItem->RemoteHostString = cacheItem->HostString;
|
|
}
|
|
else
|
|
{
|
|
PhpQueueNetworkItemQuery(networkItem, TRUE);
|
|
}
|
|
}
|
|
|
|
// Get process information.
|
|
if (processItem = PhReferenceProcessItem(networkItem->ProcessId))
|
|
{
|
|
networkItem->ProcessName = processItem->ProcessName;
|
|
PhReferenceObject(processItem->ProcessName);
|
|
PhpUpdateNetworkItemOwner(networkItem, processItem);
|
|
|
|
if (PhTestEvent(&processItem->Stage1Event))
|
|
{
|
|
networkItem->ProcessIcon = processItem->SmallIcon;
|
|
networkItem->ProcessIconValid = TRUE;
|
|
}
|
|
|
|
PhDereferenceObject(processItem);
|
|
}
|
|
|
|
// Add the network item to the hashtable.
|
|
PhAcquireQueuedLockExclusive(&PhNetworkHashtableLock);
|
|
PhAddEntryHashtable(PhNetworkHashtable, &networkItem);
|
|
PhReleaseQueuedLockExclusive(&PhNetworkHashtableLock);
|
|
|
|
// Raise the network item added event.
|
|
PhInvokeCallback(&PhNetworkItemAddedEvent, networkItem);
|
|
}
|
|
else
|
|
{
|
|
BOOLEAN modified = FALSE;
|
|
PPH_PROCESS_ITEM processItem;
|
|
|
|
if (networkItem->JustResolved)
|
|
modified = TRUE;
|
|
|
|
if (networkItem->State != connections[i].State)
|
|
{
|
|
networkItem->State = connections[i].State;
|
|
modified = TRUE;
|
|
}
|
|
|
|
if (!networkItem->ProcessName || !networkItem->ProcessIconValid)
|
|
{
|
|
if (processItem = PhReferenceProcessItem(networkItem->ProcessId))
|
|
{
|
|
if (!networkItem->ProcessName)
|
|
{
|
|
networkItem->ProcessName = processItem->ProcessName;
|
|
PhReferenceObject(processItem->ProcessName);
|
|
PhpUpdateNetworkItemOwner(networkItem, processItem);
|
|
modified = TRUE;
|
|
}
|
|
|
|
if (!networkItem->ProcessIconValid && PhTestEvent(&processItem->Stage1Event))
|
|
{
|
|
networkItem->ProcessIcon = processItem->SmallIcon;
|
|
networkItem->ProcessIconValid = TRUE;
|
|
modified = TRUE;
|
|
}
|
|
|
|
PhDereferenceObject(processItem);
|
|
}
|
|
}
|
|
|
|
networkItem->JustResolved = FALSE;
|
|
|
|
if (modified)
|
|
{
|
|
// Raise the network item modified event.
|
|
PhInvokeCallback(&PhNetworkItemModifiedEvent, networkItem);
|
|
}
|
|
|
|
PhDereferenceObject(networkItem);
|
|
}
|
|
}
|
|
|
|
PhFree(connections);
|
|
|
|
PhInvokeCallback(&PhNetworkItemsUpdatedEvent, NULL);
|
|
}
|
|
|
|
PWSTR PhGetProtocolTypeName(
|
|
__in ULONG ProtocolType
|
|
)
|
|
{
|
|
switch (ProtocolType)
|
|
{
|
|
case PH_TCP4_NETWORK_PROTOCOL:
|
|
return L"TCP";
|
|
case PH_TCP6_NETWORK_PROTOCOL:
|
|
return L"TCP6";
|
|
case PH_UDP4_NETWORK_PROTOCOL:
|
|
return L"UDP";
|
|
case PH_UDP6_NETWORK_PROTOCOL:
|
|
return L"UDP6";
|
|
default:
|
|
return L"Unknown";
|
|
}
|
|
}
|
|
|
|
PWSTR PhGetTcpStateName(
|
|
__in ULONG State
|
|
)
|
|
{
|
|
switch (State)
|
|
{
|
|
case MIB_TCP_STATE_CLOSED:
|
|
return L"Closed";
|
|
case MIB_TCP_STATE_LISTEN:
|
|
return L"Listen";
|
|
case MIB_TCP_STATE_SYN_SENT:
|
|
return L"SYN Sent";
|
|
case MIB_TCP_STATE_SYN_RCVD:
|
|
return L"SYN Received";
|
|
case MIB_TCP_STATE_ESTAB:
|
|
return L"Established";
|
|
case MIB_TCP_STATE_FIN_WAIT1:
|
|
return L"FIN Wait 1";
|
|
case MIB_TCP_STATE_FIN_WAIT2:
|
|
return L"FIN Wait 2";
|
|
case MIB_TCP_STATE_CLOSE_WAIT:
|
|
return L"Close Wait";
|
|
case MIB_TCP_STATE_CLOSING:
|
|
return L"Closing";
|
|
case MIB_TCP_STATE_LAST_ACK:
|
|
return L"Last ACK";
|
|
case MIB_TCP_STATE_TIME_WAIT:
|
|
return L"Time Wait";
|
|
case MIB_TCP_STATE_DELETE_TCB:
|
|
return L"Delete TCB";
|
|
default:
|
|
return L"Unknown";
|
|
}
|
|
}
|
|
|
|
BOOLEAN PhGetNetworkConnections(
|
|
__out PPH_NETWORK_CONNECTION *Connections,
|
|
__out PULONG NumberOfConnections
|
|
)
|
|
{
|
|
PVOID table;
|
|
DWORD tableSize;
|
|
PMIB_TCPTABLE_OWNER_MODULE tcp4Table;
|
|
PMIB_UDPTABLE_OWNER_MODULE udp4Table;
|
|
PMIB_TCP6TABLE_OWNER_MODULE tcp6Table;
|
|
PMIB_UDP6TABLE_OWNER_MODULE udp6Table;
|
|
ULONG count = 0;
|
|
ULONG i;
|
|
ULONG index = 0;
|
|
PPH_NETWORK_CONNECTION connections;
|
|
|
|
if (!GetExtendedTcpTable_I || !GetExtendedUdpTable_I)
|
|
return FALSE;
|
|
|
|
// TCP IPv4
|
|
|
|
tableSize = 0;
|
|
GetExtendedTcpTable_I(NULL, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);
|
|
table = PhAllocate(tableSize);
|
|
|
|
if (GetExtendedTcpTable_I(table, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0) == 0)
|
|
{
|
|
tcp4Table = table;
|
|
count += tcp4Table->dwNumEntries;
|
|
}
|
|
else
|
|
{
|
|
PhFree(table);
|
|
tcp4Table = NULL;
|
|
}
|
|
|
|
// TCP IPv6
|
|
|
|
tableSize = 0;
|
|
GetExtendedTcpTable_I(NULL, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0);
|
|
|
|
// Note: On Windows XP, GetExtendedTcpTable had a bug where it calculated the required buffer size
|
|
// for IPv6 TCP_TABLE_OWNER_MODULE_ALL requests incorrectly, causing it to return the wrong size
|
|
// and overrun the provided buffer instead of returning an error. The size should be:
|
|
// = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) + sizeof(MIB_TCP6ROW_OWNER_MODULE) * (number of entries)
|
|
// However, the function calculated it as:
|
|
// = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) + sizeof(MIB_TCP6ROW_OWNER_PID) * (number of entries)
|
|
// A workaround is implemented below.
|
|
if (WindowsVersion <= WINDOWS_XP && tableSize >= FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table)) // make sure we don't wrap around
|
|
{
|
|
tableSize = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) +
|
|
(tableSize - FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table)) / sizeof(MIB_TCP6ROW_OWNER_PID) * sizeof(MIB_TCP6ROW_OWNER_MODULE);
|
|
}
|
|
|
|
table = PhAllocate(tableSize);
|
|
|
|
if (GetExtendedTcpTable_I(table, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0) == 0)
|
|
{
|
|
tcp6Table = table;
|
|
count += tcp6Table->dwNumEntries;
|
|
}
|
|
else
|
|
{
|
|
PhFree(table);
|
|
tcp6Table = NULL;
|
|
}
|
|
|
|
// UDP IPv4
|
|
|
|
tableSize = 0;
|
|
GetExtendedUdpTable_I(NULL, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0);
|
|
table = PhAllocate(tableSize);
|
|
|
|
if (GetExtendedUdpTable_I(table, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0) == 0)
|
|
{
|
|
udp4Table = table;
|
|
count += udp4Table->dwNumEntries;
|
|
}
|
|
else
|
|
{
|
|
PhFree(table);
|
|
udp4Table = NULL;
|
|
}
|
|
|
|
// UDP IPv6
|
|
|
|
tableSize = 0;
|
|
GetExtendedUdpTable_I(NULL, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0);
|
|
table = PhAllocate(tableSize);
|
|
|
|
if (GetExtendedUdpTable_I(table, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0) == 0)
|
|
{
|
|
udp6Table = table;
|
|
count += udp6Table->dwNumEntries;
|
|
}
|
|
else
|
|
{
|
|
PhFree(table);
|
|
udp6Table = NULL;
|
|
}
|
|
|
|
connections = PhAllocate(sizeof(PH_NETWORK_CONNECTION) * count);
|
|
memset(connections, 0, sizeof(PH_NETWORK_CONNECTION) * count);
|
|
|
|
if (tcp4Table)
|
|
{
|
|
for (i = 0; i < tcp4Table->dwNumEntries; i++)
|
|
{
|
|
connections[index].ProtocolType = PH_TCP4_NETWORK_PROTOCOL;
|
|
|
|
connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE;
|
|
connections[index].LocalEndpoint.Address.Ipv4 = tcp4Table->table[i].dwLocalAddr;
|
|
connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwLocalPort);
|
|
|
|
connections[index].RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE;
|
|
connections[index].RemoteEndpoint.Address.Ipv4 = tcp4Table->table[i].dwRemoteAddr;
|
|
connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwRemotePort);
|
|
|
|
connections[index].State = tcp4Table->table[i].dwState;
|
|
connections[index].ProcessId = (HANDLE)tcp4Table->table[i].dwOwningPid;
|
|
memcpy(
|
|
connections[index].OwnerInfo,
|
|
tcp4Table->table[i].OwningModuleInfo,
|
|
sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
|
|
);
|
|
|
|
index++;
|
|
}
|
|
|
|
PhFree(tcp4Table);
|
|
}
|
|
|
|
if (tcp6Table)
|
|
{
|
|
for (i = 0; i < tcp6Table->dwNumEntries; i++)
|
|
{
|
|
connections[index].ProtocolType = PH_TCP6_NETWORK_PROTOCOL;
|
|
|
|
connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE;
|
|
memcpy(connections[index].LocalEndpoint.Address.Ipv6, tcp6Table->table[i].ucLocalAddr, 16);
|
|
connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwLocalPort);
|
|
|
|
connections[index].RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE;
|
|
memcpy(connections[index].RemoteEndpoint.Address.Ipv6, tcp6Table->table[i].ucRemoteAddr, 16);
|
|
connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwRemotePort);
|
|
|
|
connections[index].State = tcp6Table->table[i].dwState;
|
|
connections[index].ProcessId = (HANDLE)tcp6Table->table[i].dwOwningPid;
|
|
memcpy(
|
|
connections[index].OwnerInfo,
|
|
tcp6Table->table[i].OwningModuleInfo,
|
|
sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
|
|
);
|
|
|
|
index++;
|
|
}
|
|
|
|
PhFree(tcp6Table);
|
|
}
|
|
|
|
if (udp4Table)
|
|
{
|
|
for (i = 0; i < udp4Table->dwNumEntries; i++)
|
|
{
|
|
connections[index].ProtocolType = PH_UDP4_NETWORK_PROTOCOL;
|
|
|
|
connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE;
|
|
connections[index].LocalEndpoint.Address.Ipv4 = udp4Table->table[i].dwLocalAddr;
|
|
connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp4Table->table[i].dwLocalPort);
|
|
|
|
connections[index].RemoteEndpoint.Address.Type = 0;
|
|
|
|
connections[index].State = 0;
|
|
connections[index].ProcessId = (HANDLE)udp4Table->table[i].dwOwningPid;
|
|
memcpy(
|
|
connections[index].OwnerInfo,
|
|
udp4Table->table[i].OwningModuleInfo,
|
|
sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
|
|
);
|
|
|
|
index++;
|
|
}
|
|
|
|
PhFree(udp4Table);
|
|
}
|
|
|
|
if (udp6Table)
|
|
{
|
|
for (i = 0; i < udp6Table->dwNumEntries; i++)
|
|
{
|
|
connections[index].ProtocolType = PH_UDP6_NETWORK_PROTOCOL;
|
|
|
|
connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE;
|
|
memcpy(connections[index].LocalEndpoint.Address.Ipv6, udp6Table->table[i].ucLocalAddr, 16);
|
|
connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp6Table->table[i].dwLocalPort);
|
|
|
|
connections[index].RemoteEndpoint.Address.Type = 0;
|
|
|
|
connections[index].State = 0;
|
|
connections[index].ProcessId = (HANDLE)udp6Table->table[i].dwOwningPid;
|
|
memcpy(
|
|
connections[index].OwnerInfo,
|
|
udp6Table->table[i].OwningModuleInfo,
|
|
sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
|
|
);
|
|
|
|
index++;
|
|
}
|
|
|
|
PhFree(udp6Table);
|
|
}
|
|
|
|
*NumberOfConnections = count;
|
|
*Connections = connections;
|
|
|
|
return TRUE;
|
|
}
|