Process Hacker
netprv.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * network provider
4  *
5  * Copyright (C) 2010 wj32
6  * Copyright (C) 2010 evilpie
7  *
8  * This file is part of Process Hacker.
9  *
10  * Process Hacker is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Process Hacker is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include <phapp.h>
25 #include <ws2tcpip.h>
26 #include <ws2ipdef.h>
27 #include <iphlpapi.h>
28 #include <extmgri.h>
29 
30 typedef struct _PH_NETWORK_CONNECTION
31 {
32  ULONG ProtocolType;
33  PH_IP_ENDPOINT LocalEndpoint;
34  PH_IP_ENDPOINT RemoteEndpoint;
35  ULONG State;
36  HANDLE ProcessId;
37  LARGE_INTEGER CreateTime;
38  ULONGLONG OwnerInfo[PH_NETWORK_OWNER_INFO_SIZE];
40 
41 typedef struct _PH_NETWORK_ITEM_QUERY_DATA
42 {
43  SLIST_ENTRY ListEntry;
44  PPH_NETWORK_ITEM NetworkItem;
45 
46  PH_IP_ADDRESS Address;
47  BOOLEAN Remote;
48  PPH_STRING HostString;
50 
51 typedef struct _PHP_RESOLVE_CACHE_ITEM
52 {
53  PH_IP_ADDRESS Address;
54  PPH_STRING HostString;
56 
57 typedef DWORD (WINAPI *_GetExtendedTcpTable)(
58  _Out_writes_bytes_opt_(*pdwSize) PVOID pTcpTable,
59  _Inout_ PDWORD pdwSize,
60  _In_ BOOL bOrder,
61  _In_ ULONG ulAf,
62  _In_ TCP_TABLE_CLASS TableClass,
63  _In_ ULONG Reserved
64  );
65 
66 typedef DWORD (WINAPI *_GetExtendedUdpTable)(
67  _Out_writes_bytes_opt_(*pdwSize) PVOID pUdpTable,
68  _Inout_ PDWORD pdwSize,
69  _In_ BOOL bOrder,
70  _In_ ULONG ulAf,
71  _In_ UDP_TABLE_CLASS TableClass,
72  _In_ ULONG Reserved
73  );
74 
75 typedef int (WSAAPI *_WSAStartup)(
76  _In_ WORD wVersionRequested,
77  _Out_ LPWSADATA lpWSAData
78  );
79 
80 typedef int (WSAAPI *_WSAGetLastError)();
81 
82 typedef INT (WSAAPI *_GetNameInfoW)(
83  _In_reads_bytes_(SockaddrLength) const SOCKADDR *pSockaddr,
84  _In_ socklen_t SockaddrLength,
85  _Out_writes_opt_(NodeBufferSize) PWCHAR pNodeBuffer,
86  _In_ DWORD NodeBufferSize,
87  _Out_writes_opt_(ServiceBufferSize) PWCHAR pServiceBuffer,
88  _In_ DWORD ServiceBufferSize,
89  _In_ INT Flags
90  );
91 
92 typedef struct hostent *(WSAAPI *_gethostbyaddr)(
93  _In_reads_bytes_(len) const char *addr,
94  _In_ int len,
95  _In_ int type
96  );
97 
99  _In_ PVOID Object,
100  _In_ ULONG Flags
101  );
102 
104  _In_ PVOID Entry1,
105  _In_ PVOID Entry2
106  );
107 
109  _In_ PVOID Entry
110  );
111 
113  _In_ PVOID Entry1,
114  _In_ PVOID Entry2
115  );
116 
118  _In_ PVOID Entry
119  );
120 
122  _Out_ PPH_NETWORK_CONNECTION *Connections,
123  _Out_ PULONG NumberOfConnections
124  );
125 
127 
130 
131 PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemAddedEvent);
132 PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemModifiedEvent);
133 PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemRemovedEvent);
134 PHAPPAPI PH_CALLBACK_DECLARE(PhNetworkItemsUpdatedEvent);
135 
137 
141 
142 static PPH_HASHTABLE PhpResolveCacheHashtable;
143 static PH_QUEUED_LOCK PhpResolveCacheHashtableLock = PH_QUEUED_LOCK_INIT;
144 
145 static BOOLEAN NetworkImportDone = FALSE;
146 static _GetExtendedTcpTable GetExtendedTcpTable_I;
147 static _GetExtendedUdpTable GetExtendedUdpTable_I;
148 static _WSAStartup WSAStartup_I;
149 static _WSAGetLastError WSAGetLastError_I;
150 static _GetNameInfoW GetNameInfoW_I;
151 static _gethostbyaddr gethostbyaddr_I;
152 
154  VOID
155  )
156 {
157  PhNetworkItemType = PhCreateObjectType(L"NetworkItem", 0, PhpNetworkItemDeleteProcedure);
158  PhNetworkHashtable = PhCreateHashtable(
159  sizeof(PPH_NETWORK_ITEM),
162  40
163  );
164 
165  RtlInitializeSListHead(&PhNetworkItemQueryListHead);
166 
167  PhpResolveCacheHashtable = PhCreateHashtable(
168  sizeof(PHP_RESOLVE_CACHE_ITEM),
171  20
172  );
173 
174  return TRUE;
175 }
176 
178  VOID
179  )
180 {
181  PPH_NETWORK_ITEM networkItem;
182 
183  networkItem = PhCreateObject(
186  );
187  memset(networkItem, 0, sizeof(PH_NETWORK_ITEM));
189 
190  return networkItem;
191 }
192 
194  _In_ PVOID Object,
195  _In_ ULONG Flags
196  )
197 {
198  PPH_NETWORK_ITEM networkItem = (PPH_NETWORK_ITEM)Object;
199 
201 
202  if (networkItem->ProcessName)
203  PhDereferenceObject(networkItem->ProcessName);
204  if (networkItem->OwnerName)
205  PhDereferenceObject(networkItem->OwnerName);
206  if (networkItem->LocalHostString)
207  PhDereferenceObject(networkItem->LocalHostString);
208  if (networkItem->RemoteHostString)
210 }
211 
213  _In_ PVOID Entry1,
214  _In_ PVOID Entry2
215  )
216 {
217  PPH_NETWORK_ITEM networkItem1 = *(PPH_NETWORK_ITEM *)Entry1;
218  PPH_NETWORK_ITEM networkItem2 = *(PPH_NETWORK_ITEM *)Entry2;
219 
220  return
221  networkItem1->ProtocolType == networkItem2->ProtocolType &&
222  PhEqualIpEndpoint(&networkItem1->LocalEndpoint, &networkItem2->LocalEndpoint) &&
223  PhEqualIpEndpoint(&networkItem1->RemoteEndpoint, &networkItem2->RemoteEndpoint) &&
224  networkItem1->ProcessId == networkItem2->ProcessId;
225 }
226 
228  _In_ PVOID Entry
229  )
230 {
231  PPH_NETWORK_ITEM networkItem = *(PPH_NETWORK_ITEM *)Entry;
232 
233  return
234  networkItem->ProtocolType ^
235  PhHashIpEndpoint(&networkItem->LocalEndpoint) ^
236  PhHashIpEndpoint(&networkItem->RemoteEndpoint) ^
237  (ULONG)networkItem->ProcessId;
238 }
239 
241  _In_ ULONG ProtocolType,
242  _In_ PPH_IP_ENDPOINT LocalEndpoint,
243  _In_ PPH_IP_ENDPOINT RemoteEndpoint,
244  _In_ HANDLE ProcessId
245  )
246 {
247  PH_NETWORK_ITEM lookupNetworkItem;
248  PPH_NETWORK_ITEM lookupNetworkItemPtr = &lookupNetworkItem;
249  PPH_NETWORK_ITEM *networkItemPtr;
250  PPH_NETWORK_ITEM networkItem;
251 
252  lookupNetworkItem.ProtocolType = ProtocolType;
253  lookupNetworkItem.LocalEndpoint = *LocalEndpoint;
254  lookupNetworkItem.RemoteEndpoint = *RemoteEndpoint;
255  lookupNetworkItem.ProcessId = ProcessId;
256 
258 
259  networkItemPtr = (PPH_NETWORK_ITEM *)PhFindEntryHashtable(
261  &lookupNetworkItemPtr
262  );
263 
264  if (networkItemPtr)
265  {
266  networkItem = *networkItemPtr;
267  PhReferenceObject(networkItem);
268  }
269  else
270  {
271  networkItem = NULL;
272  }
273 
275 
276  return networkItem;
277 }
278 
280  _In_ PPH_NETWORK_ITEM NetworkItem
281  )
282 {
284  PhDereferenceObject(NetworkItem);
285 }
286 
288  _In_ PVOID Entry1,
289  _In_ PVOID Entry2
290  )
291 {
292  PPHP_RESOLVE_CACHE_ITEM cacheItem1 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry1;
293  PPHP_RESOLVE_CACHE_ITEM cacheItem2 = *(PPHP_RESOLVE_CACHE_ITEM *)Entry2;
294 
295  return PhEqualIpAddress(&cacheItem1->Address, &cacheItem2->Address);
296 }
297 
299  _In_ PVOID Entry
300  )
301 {
302  PPHP_RESOLVE_CACHE_ITEM cacheItem = *(PPHP_RESOLVE_CACHE_ITEM *)Entry;
303 
304  return PhHashIpAddress(&cacheItem->Address);
305 }
306 
308  _In_ PPH_IP_ADDRESS Address
309  )
310 {
311  PHP_RESOLVE_CACHE_ITEM lookupCacheItem;
312  PPHP_RESOLVE_CACHE_ITEM lookupCacheItemPtr = &lookupCacheItem;
313  PPHP_RESOLVE_CACHE_ITEM *cacheItemPtr;
314 
315  // Construct a temporary cache item for the lookup.
316  lookupCacheItem.Address = *Address;
317 
319  PhpResolveCacheHashtable,
320  &lookupCacheItemPtr
321  );
322 
323  if (cacheItemPtr)
324  return *cacheItemPtr;
325  else
326  return NULL;
327 }
328 
330  _In_ PPH_IP_ADDRESS Address
331  )
332 {
333  struct sockaddr_in ipv4Address;
334  struct sockaddr_in6 ipv6Address;
335  struct sockaddr *address;
336  socklen_t length;
337  PPH_STRING hostName;
338 
339  if (!GetNameInfoW_I)
340  return NULL;
341 
342  if (Address->Type == PH_IPV4_NETWORK_TYPE)
343  {
344  ipv4Address.sin_family = AF_INET;
345  ipv4Address.sin_port = 0;
346  ipv4Address.sin_addr = Address->InAddr;
347  address = (struct sockaddr *)&ipv4Address;
348  length = sizeof(ipv4Address);
349  }
350  else if (Address->Type == PH_IPV6_NETWORK_TYPE)
351  {
352  ipv6Address.sin6_family = AF_INET6;
353  ipv6Address.sin6_port = 0;
354  ipv6Address.sin6_flowinfo = 0;
355  ipv6Address.sin6_addr = Address->In6Addr;
356  ipv6Address.sin6_scope_id = 0;
357  address = (struct sockaddr *)&ipv6Address;
358  length = sizeof(ipv6Address);
359  }
360  else
361  {
362  return NULL;
363  }
364 
365  hostName = PhCreateStringEx(NULL, 128);
366 
367  if (GetNameInfoW_I(
368  address,
369  length,
370  hostName->Buffer,
371  (ULONG)hostName->Length / 2 + 1,
372  NULL,
373  0,
374  NI_NAMEREQD
375  ) != 0)
376  {
377  // Try with the maximum host name size.
378  PhDereferenceObject(hostName);
379  hostName = PhCreateStringEx(NULL, NI_MAXHOST * 2);
380 
381  if (GetNameInfoW_I(
382  address,
383  length,
384  hostName->Buffer,
385  (ULONG)hostName->Length / 2 + 1,
386  NULL,
387  0,
388  NI_NAMEREQD
389  ) != 0)
390  {
391  PhDereferenceObject(hostName);
392 
393  return NULL;
394  }
395  }
396 
398 
399  return hostName;
400 }
401 
403  _In_ PVOID Parameter
404  )
405 {
407  PPH_STRING hostString;
408  PPHP_RESOLVE_CACHE_ITEM cacheItem;
409 
410  // Last minute check of the cache.
411 
412  PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock);
413  cacheItem = PhpLookupResolveCacheItem(&data->Address);
414  PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock);
415 
416  if (!cacheItem)
417  {
418  hostString = PhGetHostNameFromAddress(&data->Address);
419 
420  if (hostString)
421  {
422  data->HostString = hostString;
423 
424  // Update the cache.
425 
426  PhAcquireQueuedLockExclusive(&PhpResolveCacheHashtableLock);
427 
428  cacheItem = PhpLookupResolveCacheItem(&data->Address);
429 
430  if (!cacheItem)
431  {
432  cacheItem = PhAllocate(sizeof(PHP_RESOLVE_CACHE_ITEM));
433  cacheItem->Address = data->Address;
434  cacheItem->HostString = hostString;
435  PhReferenceObject(hostString);
436 
437  PhAddEntryHashtable(PhpResolveCacheHashtable, &cacheItem);
438  }
439 
440  PhReleaseQueuedLockExclusive(&PhpResolveCacheHashtableLock);
441  }
442  else
443  {
444  dprintf("resolve failed, error %u\n", WSAGetLastError_I());
445  }
446  }
447  else
448  {
449  PhReferenceObject(cacheItem->HostString);
450  data->HostString = cacheItem->HostString;
451  }
452 
453  RtlInterlockedPushEntrySList(&PhNetworkItemQueryListHead, &data->ListEntry);
454 
455  return STATUS_SUCCESS;
456 }
457 
459  _In_ PPH_NETWORK_ITEM NetworkItem,
460  _In_ BOOLEAN Remote
461  )
462 {
464 
466  return;
467 
468  data = PhAllocate(sizeof(PH_NETWORK_ITEM_QUERY_DATA));
469  memset(data, 0, sizeof(PH_NETWORK_ITEM_QUERY_DATA));
470  data->NetworkItem = NetworkItem;
471  data->Remote = Remote;
472 
473  if (Remote)
474  data->Address = NetworkItem->RemoteEndpoint.Address;
475  else
476  data->Address = NetworkItem->LocalEndpoint.Address;
477 
478  PhReferenceObject(NetworkItem);
479 
481  {
484  }
485 
487 }
488 
490  _In_ PPH_NETWORK_ITEM NetworkItem,
491  _In_ PPH_PROCESS_ITEM ProcessItem
492  )
493 {
494  if (*(PULONG64)NetworkItem->OwnerInfo)
495  {
496  PVOID serviceTag;
497  PPH_STRING serviceName;
498 
499  // May change in the future...
500  serviceTag = (PVOID)*(PULONG)NetworkItem->OwnerInfo;
501  serviceName = PhGetServiceNameFromTag(NetworkItem->ProcessId, serviceTag);
502 
503  if (serviceName)
504  PhMoveReference(&NetworkItem->OwnerName, serviceName);
505  }
506 }
507 
509  _In_ PVOID Object
510  )
511 {
512  PPH_NETWORK_CONNECTION connections;
513  ULONG numberOfConnections;
514  ULONG i;
515 
516  if (!NetworkImportDone)
517  {
518  WSADATA wsaData;
519  HMODULE iphlpapi;
520  HMODULE ws2_32;
521 
522  iphlpapi = LoadLibrary(L"iphlpapi.dll");
523  GetExtendedTcpTable_I = (PVOID)GetProcAddress(iphlpapi, "GetExtendedTcpTable");
524  GetExtendedUdpTable_I = (PVOID)GetProcAddress(iphlpapi, "GetExtendedUdpTable");
525  ws2_32 = LoadLibrary(L"ws2_32.dll");
526  WSAStartup_I = (PVOID)GetProcAddress(ws2_32, "WSAStartup");
527  WSAGetLastError_I = (PVOID)GetProcAddress(ws2_32, "WSAGetLastError");
528  GetNameInfoW_I = (PVOID)GetProcAddress(ws2_32, "GetNameInfoW");
529  gethostbyaddr_I = (PVOID)GetProcAddress(ws2_32, "gethostbyaddr");
530 
531  // Make sure WSA is initialized.
532  if (WSAStartup_I)
533  {
534  WSAStartup_I(MAKEWORD(2, 2), &wsaData);
535  }
536 
537  NetworkImportDone = TRUE;
538  }
539 
540  if (!PhGetNetworkConnections(&connections, &numberOfConnections))
541  return;
542 
543  {
544  PPH_LIST connectionsToRemove = NULL;
545  PH_HASHTABLE_ENUM_CONTEXT enumContext;
546  PPH_NETWORK_ITEM *networkItem;
547 
549 
550  while (networkItem = PhNextEnumHashtable(&enumContext))
551  {
552  BOOLEAN found = FALSE;
553 
554  for (i = 0; i < numberOfConnections; i++)
555  {
556  if (
557  (*networkItem)->ProtocolType == connections[i].ProtocolType &&
558  PhEqualIpEndpoint(&(*networkItem)->LocalEndpoint, &connections[i].LocalEndpoint) &&
559  PhEqualIpEndpoint(&(*networkItem)->RemoteEndpoint, &connections[i].RemoteEndpoint) &&
560  (*networkItem)->ProcessId == connections[i].ProcessId
561  )
562  {
563  found = TRUE;
564  break;
565  }
566  }
567 
568  if (!found)
569  {
571 
572  if (!connectionsToRemove)
573  connectionsToRemove = PhCreateList(2);
574 
575  PhAddItemList(connectionsToRemove, *networkItem);
576  }
577  }
578 
579  if (connectionsToRemove)
580  {
582 
583  for (i = 0; i < connectionsToRemove->Count; i++)
584  {
585  PhpRemoveNetworkItem(connectionsToRemove->Items[i]);
586  }
587 
589  PhDereferenceObject(connectionsToRemove);
590  }
591  }
592 
593  // Go through the queued network item query data.
594  {
595  PSLIST_ENTRY entry;
597 
598  entry = RtlInterlockedFlushSList(&PhNetworkItemQueryListHead);
599 
600  while (entry)
601  {
602  data = CONTAINING_RECORD(entry, PH_NETWORK_ITEM_QUERY_DATA, ListEntry);
603  entry = entry->Next;
604 
605  if (data->Remote)
606  PhMoveReference(&data->NetworkItem->RemoteHostString, data->HostString);
607  else
608  PhMoveReference(&data->NetworkItem->LocalHostString, data->HostString);
609 
610  data->NetworkItem->JustResolved = TRUE;
611 
612  PhDereferenceObject(data->NetworkItem);
613  PhFree(data);
614  }
615  }
616 
617  for (i = 0; i < numberOfConnections; i++)
618  {
619  PPH_NETWORK_ITEM networkItem;
620 
621  // Try to find the connection in our hashtable.
622  networkItem = PhReferenceNetworkItem(
623  connections[i].ProtocolType,
624  &connections[i].LocalEndpoint,
625  &connections[i].RemoteEndpoint,
626  connections[i].ProcessId
627  );
628 
629  if (!networkItem)
630  {
631  PPHP_RESOLVE_CACHE_ITEM cacheItem;
632  PPH_PROCESS_ITEM processItem;
633 
634  // Network item not found, create it.
635 
636  networkItem = PhCreateNetworkItem();
637 
638  // Fill in basic information.
639  networkItem->ProtocolType = connections[i].ProtocolType;
640  networkItem->LocalEndpoint = connections[i].LocalEndpoint;
641  networkItem->RemoteEndpoint = connections[i].RemoteEndpoint;
642  networkItem->State = connections[i].State;
643  networkItem->ProcessId = connections[i].ProcessId;
644  networkItem->CreateTime = connections[i].CreateTime;
645  memcpy(networkItem->OwnerInfo, connections[i].OwnerInfo, sizeof(ULONGLONG) * PH_NETWORK_OWNER_INFO_SIZE);
646 
647  // Format various strings.
648 
649  if (networkItem->LocalEndpoint.Address.Type == PH_IPV4_NETWORK_TYPE)
651  else
653 
654  PhPrintUInt32(networkItem->LocalPortString, networkItem->LocalEndpoint.Port);
655 
656  if (
658  networkItem->RemoteEndpoint.Address.Ipv4 != 0
659  )
660  {
662  }
663  else if (
666  )
667  {
669  }
670 
671  if (networkItem->RemoteEndpoint.Address.Type != 0 && networkItem->RemoteEndpoint.Port != 0)
672  PhPrintUInt32(networkItem->RemotePortString, networkItem->RemoteEndpoint.Port);
673 
674  // Get host names.
675 
676  // Local
677  {
678  PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock);
679  cacheItem = PhpLookupResolveCacheItem(&networkItem->LocalEndpoint.Address);
680  PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock);
681 
682  if (cacheItem)
683  {
684  PhReferenceObject(cacheItem->HostString);
685  networkItem->LocalHostString = cacheItem->HostString;
686  }
687  else
688  {
689  PhpQueueNetworkItemQuery(networkItem, FALSE);
690  }
691  }
692 
693  // Remote
694  if (!PhIsNullIpAddress(&networkItem->RemoteEndpoint.Address))
695  {
696  PhAcquireQueuedLockShared(&PhpResolveCacheHashtableLock);
697  cacheItem = PhpLookupResolveCacheItem(&networkItem->RemoteEndpoint.Address);
698  PhReleaseQueuedLockShared(&PhpResolveCacheHashtableLock);
699 
700  if (cacheItem)
701  {
702  PhReferenceObject(cacheItem->HostString);
703  networkItem->RemoteHostString = cacheItem->HostString;
704  }
705  else
706  {
707  PhpQueueNetworkItemQuery(networkItem, TRUE);
708  }
709  }
710 
711  // Get process information.
712  if (processItem = PhReferenceProcessItem(networkItem->ProcessId))
713  {
714  networkItem->ProcessName = processItem->ProcessName;
715  PhReferenceObject(processItem->ProcessName);
716  PhpUpdateNetworkItemOwner(networkItem, processItem);
717 
718  if (PhTestEvent(&processItem->Stage1Event))
719  {
720  networkItem->ProcessIcon = processItem->SmallIcon;
721  networkItem->ProcessIconValid = TRUE;
722  }
723 
724  PhDereferenceObject(processItem);
725  }
726 
727  // Add the network item to the hashtable.
731 
732  // Raise the network item added event.
734  }
735  else
736  {
737  BOOLEAN modified = FALSE;
738  PPH_PROCESS_ITEM processItem;
739 
740  if (networkItem->JustResolved)
741  modified = TRUE;
742 
743  if (networkItem->State != connections[i].State)
744  {
745  networkItem->State = connections[i].State;
746  modified = TRUE;
747  }
748 
749  if (!networkItem->ProcessName || !networkItem->ProcessIconValid)
750  {
751  if (processItem = PhReferenceProcessItem(networkItem->ProcessId))
752  {
753  if (!networkItem->ProcessName)
754  {
755  networkItem->ProcessName = processItem->ProcessName;
756  PhReferenceObject(processItem->ProcessName);
757  PhpUpdateNetworkItemOwner(networkItem, processItem);
758  modified = TRUE;
759  }
760 
761  if (!networkItem->ProcessIconValid && PhTestEvent(&processItem->Stage1Event))
762  {
763  networkItem->ProcessIcon = processItem->SmallIcon;
764  networkItem->ProcessIconValid = TRUE;
765  modified = TRUE;
766  }
767 
768  PhDereferenceObject(processItem);
769  }
770  }
771 
772  networkItem->JustResolved = FALSE;
773 
774  if (modified)
775  {
776  // Raise the network item modified event.
778  }
779 
780  PhDereferenceObject(networkItem);
781  }
782  }
783 
784  PhFree(connections);
785 
787 }
788 
790  _In_ ULONG ProtocolType
791  )
792 {
793  switch (ProtocolType)
794  {
796  return L"TCP";
798  return L"TCP6";
800  return L"UDP";
802  return L"UDP6";
803  default:
804  return L"Unknown";
805  }
806 }
807 
809  _In_ ULONG State
810  )
811 {
812  switch (State)
813  {
814  case MIB_TCP_STATE_CLOSED:
815  return L"Closed";
816  case MIB_TCP_STATE_LISTEN:
817  return L"Listen";
818  case MIB_TCP_STATE_SYN_SENT:
819  return L"SYN Sent";
820  case MIB_TCP_STATE_SYN_RCVD:
821  return L"SYN Received";
822  case MIB_TCP_STATE_ESTAB:
823  return L"Established";
824  case MIB_TCP_STATE_FIN_WAIT1:
825  return L"FIN Wait 1";
826  case MIB_TCP_STATE_FIN_WAIT2:
827  return L"FIN Wait 2";
828  case MIB_TCP_STATE_CLOSE_WAIT:
829  return L"Close Wait";
830  case MIB_TCP_STATE_CLOSING:
831  return L"Closing";
832  case MIB_TCP_STATE_LAST_ACK:
833  return L"Last ACK";
834  case MIB_TCP_STATE_TIME_WAIT:
835  return L"Time Wait";
836  case MIB_TCP_STATE_DELETE_TCB:
837  return L"Delete TCB";
838  default:
839  return L"Unknown";
840  }
841 }
842 
844  _Out_ PPH_NETWORK_CONNECTION *Connections,
845  _Out_ PULONG NumberOfConnections
846  )
847 {
848  PVOID table;
849  DWORD tableSize;
850  PMIB_TCPTABLE_OWNER_MODULE tcp4Table;
851  PMIB_UDPTABLE_OWNER_MODULE udp4Table;
852  PMIB_TCP6TABLE_OWNER_MODULE tcp6Table;
853  PMIB_UDP6TABLE_OWNER_MODULE udp6Table;
854  ULONG count = 0;
855  ULONG i;
856  ULONG index = 0;
857  PPH_NETWORK_CONNECTION connections;
858 
859  if (!GetExtendedTcpTable_I || !GetExtendedUdpTable_I)
860  return FALSE;
861 
862  // TCP IPv4
863 
864  tableSize = 0;
865  GetExtendedTcpTable_I(NULL, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);
866  table = PhAllocate(tableSize);
867 
868  if (GetExtendedTcpTable_I(table, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0) == 0)
869  {
870  tcp4Table = table;
871  count += tcp4Table->dwNumEntries;
872  }
873  else
874  {
875  PhFree(table);
876  tcp4Table = NULL;
877  }
878 
879  // TCP IPv6
880 
881  tableSize = 0;
882  GetExtendedTcpTable_I(NULL, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0);
883 
884  // Note: On Windows XP, GetExtendedTcpTable had a bug where it calculated the required buffer size
885  // for IPv6 TCP_TABLE_OWNER_MODULE_ALL requests incorrectly, causing it to return the wrong size
886  // and overrun the provided buffer instead of returning an error. The size should be:
887  // = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) + sizeof(MIB_TCP6ROW_OWNER_MODULE) * (number of entries)
888  // However, the function calculated it as:
889  // = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) + sizeof(MIB_TCP6ROW_OWNER_PID) * (number of entries)
890  // A workaround is implemented below.
891  if (WindowsVersion <= WINDOWS_XP && tableSize >= FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table)) // make sure we don't wrap around
892  {
893  tableSize = FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table) +
894  (tableSize - FIELD_OFFSET(MIB_TCP6TABLE_OWNER_MODULE, table)) / sizeof(MIB_TCP6ROW_OWNER_PID) * sizeof(MIB_TCP6ROW_OWNER_MODULE);
895  }
896 
897  table = PhAllocate(tableSize);
898 
899  if (GetExtendedTcpTable_I(table, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_MODULE_ALL, 0) == 0)
900  {
901  tcp6Table = table;
902  count += tcp6Table->dwNumEntries;
903  }
904  else
905  {
906  PhFree(table);
907  tcp6Table = NULL;
908  }
909 
910  // UDP IPv4
911 
912  tableSize = 0;
913  GetExtendedUdpTable_I(NULL, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0);
914  table = PhAllocate(tableSize);
915 
916  if (GetExtendedUdpTable_I(table, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE, 0) == 0)
917  {
918  udp4Table = table;
919  count += udp4Table->dwNumEntries;
920  }
921  else
922  {
923  PhFree(table);
924  udp4Table = NULL;
925  }
926 
927  // UDP IPv6
928 
929  tableSize = 0;
930  GetExtendedUdpTable_I(NULL, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0);
931  table = PhAllocate(tableSize);
932 
933  if (GetExtendedUdpTable_I(table, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_MODULE, 0) == 0)
934  {
935  udp6Table = table;
936  count += udp6Table->dwNumEntries;
937  }
938  else
939  {
940  PhFree(table);
941  udp6Table = NULL;
942  }
943 
944  connections = PhAllocate(sizeof(PH_NETWORK_CONNECTION) * count);
945  memset(connections, 0, sizeof(PH_NETWORK_CONNECTION) * count);
946 
947  if (tcp4Table)
948  {
949  for (i = 0; i < tcp4Table->dwNumEntries; i++)
950  {
951  connections[index].ProtocolType = PH_TCP4_NETWORK_PROTOCOL;
952 
953  connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE;
954  connections[index].LocalEndpoint.Address.Ipv4 = tcp4Table->table[i].dwLocalAddr;
955  connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwLocalPort);
956 
957  connections[index].RemoteEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE;
958  connections[index].RemoteEndpoint.Address.Ipv4 = tcp4Table->table[i].dwRemoteAddr;
959  connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp4Table->table[i].dwRemotePort);
960 
961  connections[index].State = tcp4Table->table[i].dwState;
962  connections[index].ProcessId = (HANDLE)tcp4Table->table[i].dwOwningPid;
963  connections[index].CreateTime = tcp4Table->table[i].liCreateTimestamp;
964  memcpy(
965  connections[index].OwnerInfo,
966  tcp4Table->table[i].OwningModuleInfo,
967  sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
968  );
969 
970  index++;
971  }
972 
973  PhFree(tcp4Table);
974  }
975 
976  if (tcp6Table)
977  {
978  for (i = 0; i < tcp6Table->dwNumEntries; i++)
979  {
980  connections[index].ProtocolType = PH_TCP6_NETWORK_PROTOCOL;
981 
982  connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE;
983  memcpy(connections[index].LocalEndpoint.Address.Ipv6, tcp6Table->table[i].ucLocalAddr, 16);
984  connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwLocalPort);
985 
986  connections[index].RemoteEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE;
987  memcpy(connections[index].RemoteEndpoint.Address.Ipv6, tcp6Table->table[i].ucRemoteAddr, 16);
988  connections[index].RemoteEndpoint.Port = _byteswap_ushort((USHORT)tcp6Table->table[i].dwRemotePort);
989 
990  connections[index].State = tcp6Table->table[i].dwState;
991  connections[index].ProcessId = (HANDLE)tcp6Table->table[i].dwOwningPid;
992  connections[index].CreateTime = tcp6Table->table[i].liCreateTimestamp;
993  memcpy(
994  connections[index].OwnerInfo,
995  tcp6Table->table[i].OwningModuleInfo,
996  sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
997  );
998 
999  index++;
1000  }
1001 
1002  PhFree(tcp6Table);
1003  }
1004 
1005  if (udp4Table)
1006  {
1007  for (i = 0; i < udp4Table->dwNumEntries; i++)
1008  {
1009  connections[index].ProtocolType = PH_UDP4_NETWORK_PROTOCOL;
1010 
1011  connections[index].LocalEndpoint.Address.Type = PH_IPV4_NETWORK_TYPE;
1012  connections[index].LocalEndpoint.Address.Ipv4 = udp4Table->table[i].dwLocalAddr;
1013  connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp4Table->table[i].dwLocalPort);
1014 
1015  connections[index].RemoteEndpoint.Address.Type = 0;
1016 
1017  connections[index].State = 0;
1018  connections[index].ProcessId = (HANDLE)udp4Table->table[i].dwOwningPid;
1019  connections[index].CreateTime = udp4Table->table[i].liCreateTimestamp;
1020  memcpy(
1021  connections[index].OwnerInfo,
1022  udp4Table->table[i].OwningModuleInfo,
1023  sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
1024  );
1025 
1026  index++;
1027  }
1028 
1029  PhFree(udp4Table);
1030  }
1031 
1032  if (udp6Table)
1033  {
1034  for (i = 0; i < udp6Table->dwNumEntries; i++)
1035  {
1036  connections[index].ProtocolType = PH_UDP6_NETWORK_PROTOCOL;
1037 
1038  connections[index].LocalEndpoint.Address.Type = PH_IPV6_NETWORK_TYPE;
1039  memcpy(connections[index].LocalEndpoint.Address.Ipv6, udp6Table->table[i].ucLocalAddr, 16);
1040  connections[index].LocalEndpoint.Port = _byteswap_ushort((USHORT)udp6Table->table[i].dwLocalPort);
1041 
1042  connections[index].RemoteEndpoint.Address.Type = 0;
1043 
1044  connections[index].State = 0;
1045  connections[index].ProcessId = (HANDLE)udp6Table->table[i].dwOwningPid;
1046  connections[index].CreateTime = udp6Table->table[i].liCreateTimestamp;
1047  memcpy(
1048  connections[index].OwnerInfo,
1049  udp6Table->table[i].OwningModuleInfo,
1050  sizeof(ULONGLONG) * min(PH_NETWORK_OWNER_INFO_SIZE, TCPIP_OWNING_MODULE_SIZE)
1051  );
1052 
1053  index++;
1054  }
1055 
1056  PhFree(udp6Table);
1057  }
1058 
1059  *NumberOfConnections = count;
1060  *Connections = connections;
1061 
1062  return TRUE;
1063 }