Bitdefender Hypervisor Memory Introspection
winnet.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "introcore.h"
6 #include "guests.h"
7 #include "intronet.h"
8 #include "winnet.h"
9 #include "winprocesshp.h"
10 #include "winpe.h"
11 #include "drivers.h"
12 #include "alerts.h"
13 #include "bitmask.h"
14 #include "structs.h"
15 
26 #define TCPIP_MAX_PARTITION_CNT 0x40
27 
29 #define NET_BYTE_ORDER(Word) (((Word) >> 8) | ((Word) << 8))
30 
32 #define VALID_PORT(Port) (0 != (Port) && 49151 != (Port))
33 
35 #define WINNET_LIST_ITERS_CAP 64
36 struct _WINNET_STATE
41 {
43  struct _PARTITION
44  {
48 
50  } Partition;
51 
53  struct _BITMAP
54  {
57 
59  } Bitmap;
60 } gWinNet;
61 
62 
63 static inline INTRO_NET_AF
65  _In_ WORD Af
66  )
74 {
75  switch (Af)
76  {
77  case AF_INET:
78  return introNetAfIpv4;
79  case AF_INET6:
80  return introNetAfIpv6;
81  }
82 
83  return introNetAfUnknown;
84 }
85 
86 
87 static INTSTATUS
89  _In_reads_bytes_(Size) const BYTE *Buffer,
90  _In_ size_t Size,
91  _Inout_ size_t *Offset,
92  _Inout_ void *Context
93  )
108 {
109 #define ADDR_FAM_OFFSET (gGuest.Guest64 ? (gGuest.OSVersion >= 9200 ? 0x18 : 0x14) : 0xC)
110 
111  POOL_HEADER ph;
112  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
113  size_t off;
114  QWORD addr;
115  INTSTATUS status;
116  DWORD tag;
117  WORD fam;
118 
119  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
120 
121  off = *Offset;
122  *Offset += gGuest.WordSize;
123 
124  if (off + gGuest.WordSize > Size)
125  {
127  }
128 
129  addr = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
130 
131  if (ROUND_DOWN(addr, WIN_POOL_HEADER_SIZE) != addr)
132  {
134  }
135 
136  status = IntKernVirtMemRead(addr - WIN_POOL_HEADER_SIZE, WIN_POOL_HEADER_SIZE, &ph, NULL);
137  if (!INT_SUCCESS(status))
138  {
139  return status;
140  }
141 
142  tag = gGuest.Guest64 ? ph.Header64.PoolTag : ph.Header32.PoolTag;
143 
144  if (tag != POOL_TAG_INNL)
145  {
147  }
148 
149  status = IntKernVirtMemRead(addr + ADDR_FAM_OFFSET, sizeof(fam), &fam, NULL);
150  if (!INT_SUCCESS(status))
151  {
152  return status;
153  }
154 
155  net->AddressFamily = IntWinNetConvertAf(fam);
156  if (net->AddressFamily == introNetAfUnknown)
157  {
159  }
160 
161  return INT_STATUS_SUCCESS;
162 
163 #undef ADDR_FAM_OFFSET
164 }
165 
166 
167 static INTSTATUS
169  _In_ QWORD Gva,
170  _Out_ INTRONET_ENDPOINT *Endpoint
171  )
183 {
184  INTSTATUS status;
185  QWORD addr;
186  LOCAL_ADDRESS la;
187 
188  addr = FIX_GUEST_POINTER(gGuest.Guest64, Gva);
189  status = IntKernVirtMemRead(addr, gGuest.Guest64 ? sizeof(la.Addr64) : sizeof(la.Addr32), &la, NULL);
190  if (!INT_SUCCESS(status))
191  {
192  return status;
193  }
194 
195  addr = gGuest.Guest64 ? la.Addr64.InAddr : la.Addr32.InAddr;
196  status = IntKernVirtMemFetchWordSize(addr, &addr);
197  if (!INT_SUCCESS(status))
198  {
199  return status;
200  }
201 
202  addr = FIX_GUEST_POINTER(gGuest.Guest64, addr);
203  return IntKernVirtMemRead(addr, sizeof(Endpoint->LocalAddress), &Endpoint->LocalAddress, NULL);
204 }
205 
206 
207 static INTSTATUS
209  _In_reads_bytes_(Size) const BYTE *Buffer,
210  _In_ size_t Size,
211  _Inout_ size_t *Offset,
212  _Inout_ void *Context
213  )
232 {
233  QWORD addr, local, remote;
234  ADDRINFO ai = { 0 };
235  DWORD sz;
236  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
237  size_t off;
238  INTSTATUS status;
239 
240  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
241 
242  if (gGuest.Guest64)
243  {
244  sz = sizeof(ai.Addr64);
245  }
246  else
247  {
248  sz = gGuest.OSVersion >= 9200 ? sizeof(ai.Addr32.Win8AndAbove) : sizeof(ai.Addr32.Win7);
249  }
250 
251  off = *Offset;
252  *Offset += gGuest.WordSize;
253 
254  if (off + gGuest.WordSize > Size)
255  {
257  }
258 
259  addr = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
260  status = IntKernVirtMemRead(addr, sz, &ai, NULL);
261  if (!INT_SUCCESS(status))
262  {
263  return status;
264  }
265 
266  if (gGuest.Guest64)
267  {
268  remote = ai.Addr64.Remote;
269  local = ai.Addr64.Local;
270  }
271  else
272  {
273  remote = gGuest.OSVersion >= 9200 ? ai.Addr32.Win8AndAbove.Remote : ai.Addr32.Win7.Remote;
274  local = gGuest.OSVersion >= 9200 ? ai.Addr32.Win8AndAbove.Local : ai.Addr32.Win7.Local;
275  }
276 
277  status = IntKernVirtMemRead(remote, sizeof(net->RemoteAddress), &net->RemoteAddress, NULL);
278  if (!INT_SUCCESS(status))
279  {
280  return status;
281  }
282 
283  return IntWinNetGetLocalAddr(local, net);
284 }
285 
286 
287 static INTSTATUS
289  _In_reads_bytes_(Size) const BYTE *Buffer,
290  _In_ size_t Size,
291  _Inout_ size_t *Offset,
292  _Inout_ void *Context
293  )
314 {
315  INTSTATUS status;
316  size_t off, skip;
317  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
318 
319  *Offset = ROUND_UP(*Offset, sizeof(DWORD));
320 
321  skip = 4;
322  off = *Offset;
323 
324  // Make sure we can fit both ports and the state.
325  if (off + 2 * sizeof(WORD) + sizeof(DWORD) > Size)
326  {
328  goto _exit;
329  }
330 
331  if (gGuest.Guest64 && IS_KERNEL_POINTER_WIN(TRUE, *(const QWORD *)(Buffer + off)))
332  {
333  skip = 8;
335  goto _exit;
336  }
337 
338  net->State = IntNetConvertState(*(const DWORD *)(Buffer + off));
339  if (net->State == introNetStateUnknown ||
340  net->State == introNetStateListening ||
341  net->State == introNetStateClosed)
342  {
344  goto _exit;
345  }
346 
347  net->LocalPort = NET_BYTE_ORDER(*(const WORD *)(Buffer + off + 4));
348  net->RemotePort = NET_BYTE_ORDER(*(const WORD *)(Buffer + off + 6));
349 
350  if (!VALID_PORT(net->LocalPort) || !VALID_PORT(net->RemotePort))
351  {
353  goto _exit;
354  }
355 
356  skip += 4;
357  status = INT_STATUS_SUCCESS;
358 
359 _exit:
360  *Offset += skip;
361 
362  return status;
363 }
364 
365 
366 static INTSTATUS
368  _In_reads_bytes_(Size) const BYTE *Buffer,
369  _In_ size_t Size,
370  _Inout_ size_t *Offset,
371  _Inout_ void *Context
372  )
386 {
387  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
388  QWORD addr;
389  size_t off;
390 
391  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
392 
393  off = *Offset;
394  *Offset += gGuest.WordSize;
395 
396  if (off + gGuest.WordSize > Size)
397  {
399  }
400 
401  addr = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
402 
404  if (NULL == net->OwnerProcess)
405  {
407  }
408 
409  return INT_STATUS_SUCCESS;
410 }
411 
412 
413 static INTSTATUS
415  _In_reads_bytes_(Size) const BYTE *Buffer,
416  _In_ size_t Size,
417  _Inout_ size_t *Offset,
418  _Inout_ void *Context
419  )
438 {
439  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
440  QWORD addrinfo;
441  size_t off;
442  INTSTATUS status;
443  WORD port;
444 
445  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
446 
447  off = *Offset;
448  *Offset += gGuest.WordSize;
449 
450  // Make sure we can fit the both the address and address family pointers
451  // and the local port, which is at an offset of 2 bytes from those pointers.
452  if (off + 2ull * gGuest.WordSize + 2 + sizeof(WORD) > Size)
453  {
455  }
456 
457  addrinfo = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
458  if (0 == addrinfo)
459  {
460  // Handle dual stack sockets.
461  if (introNetAfIpv4 == net->AddressFamily)
462  {
463  net->LocalAddress.Ipv4[0] = 127;
464  net->LocalAddress.Ipv4[3] = 1;
465  }
466  else
467  {
468  net->LocalAddress.Ipv6[15] = 1;
469  }
470  }
471  else
472  {
473  status = IntWinNetGetLocalAddr(addrinfo, net);
474  if (!INT_SUCCESS(status))
475  {
476  return status;
477  }
478  }
479 
480  // Get to the next pointer.
481  off += gGuest.WordSize;
482 
483  // Note that this will increase the offset.
484  status = IntWinNetGetAddrFam(Buffer, Size, &off, Context);
485  if (!INT_SUCCESS(status))
486  {
487  return status;
488  }
489 
490  port = NET_BYTE_ORDER(*(const WORD *)(Buffer + off + 2));
491  if (port != net->LocalPort)
492  {
494  }
495 
497 
498  *Offset += 2ull * gGuest.WordSize;
499 
500  return INT_STATUS_SUCCESS;
501 }
502 
503 
504 static INTSTATUS
506  _In_ const QWORD Gva,
508  _In_ size_t Count,
509  _In_ DWORD PoolTag,
510  _In_ BOOLEAN LogErrors,
511  _Inout_ INTRONET_ENDPOINT *Endpoint
512  )
526 {
527  QWORD alignedAddr;
528  const POOL_HEADER *ph;
529  BYTE *map;
530  size_t sz, originalOffset;
531  ssize_t offset;
532  INTSTATUS status;
533 
535  {
537  }
538 
539  if (NULL == Invariants)
540  {
542  }
543 
544  if (0 == Count || Count > INT_STRUCT_MAX_INVARIANT_CNT)
545  {
547  }
548 
549  if (NULL == Endpoint)
550  {
552  }
553 
554  alignedAddr = Gva & PAGE_MASK;
555  originalOffset = ROUND_DOWN(Gva & PAGE_OFFSET, WIN_POOL_HEADER_SIZE);
556 
557  status = IntVirtMemMap(alignedAddr, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
558  if (!INT_SUCCESS(status))
559  {
560  return status;
561  }
562 
563  ph = NULL;
564  sz = 0;
565  offset = originalOffset;
566  while (offset >= 0)
567  {
568  DWORD tag, type;
569 
570  ph = (const POOL_HEADER *)((size_t)map + offset);
571 
572  tag = gGuest.Guest64 ? ph->Header64.PoolTag : ph->Header32.PoolTag;
573  type = gGuest.Guest64 ? ph->Header64.PoolType : ph->Header32.PoolType;
574 
576  sz *= WIN_POOL_BLOCK_SIZE;
577 
578  if (tag == PoolTag && offset + sz <= PAGE_SIZE && type == NonPagedPoolMustSucceed)
579  {
580  break;
581  }
582 
583  offset -= WIN_POOL_HEADER_SIZE;
584  ph = NULL;
585  }
586 
587  if (NULL == ph)
588  {
589  status = INT_STATUS_NOT_FOUND;
590  goto _exit;
591  }
592 
593  if (offset + sz < originalOffset)
594  {
596  goto _exit;
597  }
598 
599  status = IntStructFill(map + offset, sz, Invariants, Count, LogErrors, Endpoint);
600  if (!INT_SUCCESS(status))
601  {
602  if (LogErrors)
603  {
604  IntDumpBuffer(map, alignedAddr, PAGE_SIZE, 16, 1, TRUE, TRUE);
605  }
606  }
607 
608 _exit:
609 
610  IntVirtMemUnmap(&map);
611 
612  return status;
613 }
614 
615 
616 static INTSTATUS
618  _In_ QWORD Gva,
619  _Inout_ INTRONET_ENDPOINT *Endpoint
620  )
629 {
630  INTSTATUS status;
631 
632  static BOOLEAN found = FALSE;
633  static INT_STRUCT_INVARIANT invs[] =
634  {
635  {
637  .Getter = IntWinNetGetAddrFam,
638  },
639  {
640  .Offset = INT_OFFSET_NOT_INITIALIZED,
641  .Getter = IntWinNetGetAddrInfo,
642  },
643  {
644  .Offset = INT_OFFSET_NOT_INITIALIZED,
645  .Getter = IntWinNetGetPortsAndState,
646  },
647  {
648  .Offset = INT_OFFSET_NOT_INITIALIZED,
649  .Getter = IntWinNetGetOwner,
650  },
651  };
652 
653  status = IntWinNetFillTcpStruct(Gva, invs, ARRAYSIZE(invs), POOL_TAG_TCPE, found, Endpoint);
654  if (!INT_SUCCESS(status))
655  {
656  WARNING("[WARNING] Failed to fill endpoint from 0x%016llx: 0x%08x\n", Gva, status);
657  if (!found)
658  {
659  for (size_t i = 0; i < ARRAYSIZE(invs); i++)
660  {
662  }
663  }
664  }
665  else
666  {
667  found = TRUE;
668  }
669 
670  return status;
671 }
672 
673 
674 static INTSTATUS
676  _In_ QWORD Gva,
677  _Inout_ INTRONET_ENDPOINT *Endpoint
678  )
687 {
688  INTSTATUS status;
689 
690  static BOOLEAN found = FALSE;
691  static INT_STRUCT_INVARIANT invs[] =
692  {
693  {
695  .Getter = IntWinNetGetOwner,
696  },
697 
698  {
699  .Offset = INT_OFFSET_NOT_INITIALIZED,
701  },
702  };
703 
704  status = IntWinNetFillTcpStruct(Gva, invs, ARRAYSIZE(invs), POOL_TAG_TCPL, found, Endpoint);
705  if (!INT_SUCCESS(status))
706  {
707  WARNING("[WARNING] Failed to fill listener from 0x%016llx: 0x%08x\n", Gva, status);
708 
709  if (!found)
710  {
711  for (size_t i = 0; i < ARRAYSIZE(invs); i++)
712  {
714  }
715  }
716  }
717  else
718  {
719  found = TRUE;
720  }
721 
722  return status;
723 }
724 
725 
726 static INTSTATUS
728  _In_ QWORD Gva,
729  _In_ PFUNC_IntWinNetCallback Callback,
730  _Inout_opt_ void *Context
731  )
742 {
743 
744  INTSTATUS status;
745  QWORD flink = 0;
746  QWORD head = FIX_GUEST_POINTER(gGuest.Guest64, Gva);
747  size_t iters = 0;
748 
749  status = IntKernVirtMemFetchWordSize(head, &flink);
750  if (!INT_SUCCESS(status))
751  {
752  return status;
753  }
754 
755  while (flink != head && iters++ < WINNET_LIST_ITERS_CAP)
756  {
757  QWORD old = flink;
758  INTRONET_ENDPOINT net = { 0 };
759 
760  status = IntWinNetGetTcpEndpoint(flink, &net);
761  if (!INT_SUCCESS(status))
762  {
763  WARNING("[WARNING] Failed to extract tcp endpoint from 0x%016llx: 0x%08x\n", flink, status);
764  return status;
765  }
766 
767  Callback(&net, Context);
768 
769  status = IntKernVirtMemFetchWordSize(old, &flink);
770  if (!INT_SUCCESS(status))
771  {
772  WARNING("[WARNING] IntKernVirtMemFetchWordSize failed for 0x%016llx: 0x%08x\n", old, status);
773  return status;
774  }
775 
776  flink = FIX_GUEST_POINTER(gGuest.Guest64, flink);
777 
778  // don't trust the guest, this may be a moment when this endpoint is to
779  // be removed from the linked list, so it's list entry will point to itself,
780  // causing an infinite loop here.
781  if (old == flink)
782  {
783  ERROR("[ERROR] Entry @ 0x%016llx points to itself\n", old);
784  return INT_STATUS_SUCCESS;
785  }
786  }
787 
788  return INT_STATUS_SUCCESS;
789 }
790 
791 
792 static INTSTATUS
794  _In_ PFUNC_IntWinNetCallback Callback,
795  _Inout_opt_ void *Context
796  )
820 {
821 #define TCP_DIRECTORY_ENTRY_CNT 0x80
822 #define TCP_DIRECTORY_ENTRY_SIZE (2ull * (gGuest.WordSize))
823 #define TCP_DIRECTORY_SIZE (TCP_DIRECTORY_ENTRY_CNT * TCP_DIRECTORY_ENTRY_SIZE)
824 
825  for (DWORD i = 0; i < gWinNet.Partition.Count; i++)
826  {
827  QWORD ht, dir, addr;
828  DWORD cnt, sz;
829  INTSTATUS status;
830 
831  union
832  {
835  } tbl;
836 
838 
839  ht = 0;
840  status = IntKernVirtMemFetchWordSize(addr, &ht);
841  if (!INT_SUCCESS(status))
842  {
843  ERROR("[ERROR] IntKernVirtMemFetchWordSize failed for 0x%016llx: 0x%08x\n",
844  addr, status);
845  return status;
846  }
847 
848  sz = gGuest.Guest64 ? sizeof(tbl.Ht64) : sizeof(tbl.Ht32);
849  status = IntKernVirtMemRead(ht, sz, &tbl, NULL);
850  if (!INT_SUCCESS(status))
851  {
852  continue;
853  }
854 
855  dir = gGuest.Guest64 ? tbl.Ht64.Directory : tbl.Ht32.Directory;
856  cnt = gGuest.Guest64 ? tbl.Ht64.NonEmptyBuckets : tbl.Ht32.NonEmptyBuckets;
857 
858  // don't iterate a directory with empty buckets.
859  if (cnt == 0)
860  {
861  continue;
862  }
863 
864  for (size_t j = 0; j < TCP_DIRECTORY_ENTRY_CNT; j++)
865  {
867 
868  status = IntWinNetIterateLinkedList(list, Callback, Context);
869  if (!INT_SUCCESS(status))
870  {
871  WARNING("[WARNING] IntWinNetIterateLinkedList failed: 0x%08x\n", status);
872  }
873  }
874 
875  }
876 #undef TCP_DIRECTORY_SIZE
877 #undef TCP_DIRECTORY_ENTRY_SIZE
878 #undef TCP_DIRECTORY_ENTRY_CNT
879 
880  return INT_STATUS_SUCCESS;
881 }
882 
883 
884 static INTSTATUS
886  _In_ INTRONET_PORT Port,
887  _In_ QWORD Gva,
888  _In_ PFUNC_IntWinNetCallback Callback,
889  _Inout_opt_ void *Context
890  )
902 {
903 // Pointers inside the InetPortAssignmentArray are not properly aligned. There may be some refcount bits
904 // or something. This is the mask Windows applies as well.
905 #define WINNET_CLEAN_PTR_MASK ((QWORD)0xFFFFFFFFFFFFFFFCull)
906 
907  INTSTATUS status = INT_STATUS_SUCCESS;
908  QWORD slist = Gva & WINNET_CLEAN_PTR_MASK;
909  QWORD slistOld = 0;
910  size_t iters = 0;
911 
912  while (IS_KERNEL_POINTER_WIN(gGuest.Guest64, slist) && slist != slistOld && iters++ < WINNET_LIST_ITERS_CAP)
913  {
914  INTRONET_ENDPOINT net = { .LocalPort = Port };
915 
916  status = IntWinNetGetTcpListener(slist, &net);
917  if (INT_SUCCESS(status))
918  {
919  // We ignore errors here because we might have "dummy" endpoints active on this port.
920  // We might want to look into those because we might not actually need to iterate the
921  // TCP Partition if we can get to the "real" endpoints from here.
922 
923  Callback(&net, Context);
924  }
925 
926  slistOld = slist;
927  status = IntKernVirtMemFetchWordSize(slistOld, &slist);
928  if (!INT_SUCCESS(status))
929  {
930  return status;
931  }
932 
933  slist &= WINNET_CLEAN_PTR_MASK;
934  }
935 
936  return status;
937 
938 #undef WINNET_CLEAN_PTR_MASK
939 }
940 
941 
942 static INTSTATUS
944  _In_ PFUNC_IntWinNetCallback Callback,
945  _Inout_opt_ void *Context
946  )
967 {
968  INTSTATUS status;
969  DWORD sz = 0x10000;
970  BITMASK *bts;
971  size_t pasz, offListener, offPa;
972 
973  offListener = gGuest.WordSize;
974 
975  if (gGuest.OSVersion >= 19041)
976  {
977  pasz = gGuest.Guest64 ? 4ull * sizeof(QWORD) : 6ull * sizeof(DWORD);
978  offPa = gGuest.Guest64 ? 3 * sizeof(QWORD) : 4 * sizeof(DWORD);
979  offListener = 2 * sizeof(QWORD);
980  }
981  else if (gGuest.OSVersion >= 9600)
982  {
983  pasz = 3ull * gGuest.WordSize;
984  offPa = gGuest.Guest64 ? 3 * sizeof(QWORD) : 4 * sizeof(DWORD);
985  }
986  else
987  {
988  pasz = 2ull * gGuest.WordSize;
989  offPa = gGuest.Guest64 ? 4 * sizeof(QWORD) : 5 * sizeof(DWORD);
990  }
991 
992  bts = BitMaskAlloc(sz);
993  if (NULL == bts)
994  {
995  ERROR("[ERROR] BitMaskAlloc failed\n");
997  }
998 
999 
1000  status = IntKernVirtMemRead(gWinNet.Bitmap.Buffer, sz / 8, bts->Bits, NULL);
1001  if (!INT_SUCCESS(status))
1002  {
1003  ERROR("[ERROR] IntKernVirtMemRead failed to read 0x%x bytes from 0x%016llx: 0x%08x\n",
1004  sz / 8, gWinNet.Bitmap.Buffer, status);
1005  goto _exit;
1006  }
1007 
1008  for (DWORD i = 1; i < sz; i++)
1009  {
1010 #define TCP_PORT_ASSIGNMENT_ARRAY_INDEX(i) (((i) >> 8) & 0xFF)
1011 #define TCP_PORT_ASSIGNMENT_INDEX(i) ((i) & 0xFF)
1012 
1013  QWORD addr = 0;
1014  QWORD arr = 0;
1015  QWORD pa = 0;
1016  QWORD listener = 0;
1017 
1018  if (!BitMaskTest(bts, i))
1019  {
1020  continue;
1021  }
1022 
1024  status = IntKernVirtMemFetchWordSize(addr, &arr);
1025  if (!INT_SUCCESS(status))
1026  {
1027  continue;
1028  }
1029 
1030  arr += offPa;
1031  status = IntKernVirtMemFetchWordSize(arr, &pa);
1032  if (!INT_SUCCESS(status))
1033  {
1034  continue;
1035  }
1036 
1037  pa += pasz * TCP_PORT_ASSIGNMENT_INDEX(i) + offListener;
1038  status = IntKernVirtMemFetchWordSize(pa, &listener);
1039  if (!INT_SUCCESS(status))
1040  {
1041  continue;
1042  }
1043 
1044  status = IntWinNetIterateSlinkedList((INTRONET_PORT)i, listener, Callback, Context);
1045  if (!INT_SUCCESS(status))
1046  {
1047  WARNING("[WARNING] IntWinNetIterateSlinkedList failed: 0x%08x\n", status);
1048  }
1049 
1050 #undef TCP_PORT_ASSIGNMENT_INDEX
1051 #undef TCP_PORT_ASSIGNMENT_ARRAY_INDEX
1052  }
1053 
1054 _exit:
1055  BitMaskFree(&bts);
1056 
1057  return INT_STATUS_SUCCESS;
1058 }
1059 
1060 
1061 static INTSTATUS
1063  _In_reads_bytes_(Size) const BYTE *Buffer,
1064  _In_ size_t Size
1065  )
1080 {
1081  QWORD page;
1082  size_t sz;
1083  INTSTATUS status;
1084 
1085  // Describe the fist 3 elements of a tcpip!_PARTITION undocumented structure.
1086  // Each of those should pe pointers to some nt!_RTL_DYNAMIC_HASH_TABLE that
1087  // reside in the same page, with the THcT pool tag.
1088  const union _PARTITION
1089  {
1090  DWORD Part32[3];
1091  QWORD Part64[3];
1092  } *part = (const union _PARTITION *)Buffer;
1093 
1094  sz = gGuest.Guest64 ? sizeof(part->Part64) : sizeof(part->Part32);
1095 
1096  if (sz > Size)
1097  {
1099  }
1100 
1101  page = (gGuest.Guest64 ? part->Part64[0] : part->Part32[0]) & PAGE_MASK;
1102  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, page))
1103  {
1105  }
1106 
1107  for (size_t i = 0; i < ARRAYSIZE(part->Part64); i++)
1108  {
1109  QWORD p = gGuest.Guest64 ? part->Part64[i] : part->Part32[i];
1110  DWORD ptag;
1111 
1112  if (i == 0)
1113  {
1114  if ((p - WIN_POOL_HEADER_SIZE) < page)
1115  {
1117  }
1118 
1119  status = IntKernVirtMemFetchDword(p - WIN_POOL_HEADER_SIZE + 4, &ptag);
1120  if (!INT_SUCCESS(status))
1121  {
1122  return status;
1123  }
1124 
1125  if (ptag != POOL_TAG_TCHT)
1126  {
1128  }
1129  }
1130  else
1131  {
1132  if ((p & PAGE_MASK) != page)
1133  {
1135  }
1136  }
1137 
1138  }
1139 
1140  return INT_STATUS_SUCCESS;
1141 }
1142 
1143 
1144 static INTSTATUS
1146  _In_ QWORD PartitionAddress,
1147  _In_ QWORD PartitionPointer
1148  )
1176 {
1177  BYTE *map;
1178  QWORD addr;
1179  QWORD cnt;
1180  DWORD sz;
1181  DWORD off;
1182  INTSTATUS status;
1183 
1184  if (gWinNet.Partition.Found)
1185  {
1187  }
1188 
1189  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, PartitionAddress))
1190  {
1192  }
1193 
1194  off = PartitionAddress & PAGE_OFFSET;
1195  if (ROUND_DOWN(off, gGuest.WordSize) != off)
1196  {
1198  }
1199 
1200  status = IntVirtMemMap(PartitionAddress & PAGE_MASK, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1201  if (!INT_SUCCESS(status))
1202  {
1203  return status;
1204  }
1205 
1206  // Check for a valid tcpip!_PARTITION.
1207  status = IntWinNetCheckPartition(map + off, PAGE_SIZE - off);
1208  if (!INT_SUCCESS(status))
1209  {
1210  goto _exit;
1211  }
1212 
1213  // Read the tcpip!PartitionCount value.
1214  addr = PartitionPointer;
1215 
1216  if (gGuest.OSVersion >= 19041)
1217  {
1218  sz = gGuest.WordSize;
1219  addr -= gGuest.WordSize;
1220  }
1221  else if (gGuest.OSVersion >= 9600)
1222  {
1223  sz = sizeof(DWORD);
1224  addr -= sizeof(DWORD);
1225  }
1226  else if (gGuest.OSVersion >= 9200)
1227  {
1228  sz = gGuest.WordSize;
1229  addr -= gGuest.WordSize;
1230  }
1231  else
1232  {
1233  sz = 2;
1234  addr += gGuest.WordSize + 2ull;
1235  }
1236 
1237  cnt = 0;
1238  status = IntKernVirtMemRead(addr, sz, &cnt, NULL);
1239  if (!INT_SUCCESS(status))
1240  {
1241  goto _exit;
1242  }
1243 
1244  if (cnt > TCPIP_MAX_PARTITION_CNT)
1245  {
1246  goto _exit;
1247  }
1248 
1249  if (cnt > 1)
1250  {
1251  // Search for the next partition to determine the size.
1252  status = INT_STATUS_NOT_FOUND;
1253  for (DWORD cur = off + 8 * gGuest.WordSize; cur < PAGE_SIZE; cur += gGuest.WordSize)
1254  {
1255  status = IntWinNetCheckPartition(map + cur, PAGE_SIZE - cur);
1256  if (INT_SUCCESS(status))
1257  {
1258  sz = cur - off;
1259  break;
1260  }
1261  }
1262 
1263  if (!INT_SUCCESS(status))
1264  {
1265  goto _exit;
1266  }
1267  }
1268  else
1269  {
1270  // We don't realy care for it's size if there's only one of them but set it just in case.
1271  sz = PAGE_SIZE - off;
1272  }
1273 
1274  IntDumpGvaEx(PartitionPointer - 8, 0x20, gGuest.Mm.SystemCr3, 16, 1, FALSE, TRUE);
1275 
1276  gWinNet.Partition.Table = PartitionAddress;
1277  gWinNet.Partition.Size = sz;
1278  gWinNet.Partition.Count = (DWORD)cnt;
1279 
1280  TRACE("[WINNET] Found tcpip!ParitionTable @ 0x%016llx pointing to"
1281  " 0x%016llx with %u elements of size 0x%x from 0x%016llx\n",
1282  PartitionAddress, gWinNet.Partition.Table, gWinNet.Partition.Count,
1283  gWinNet.Partition.Size, PartitionPointer);
1284 
1286 
1287  status = INT_STATUS_SUCCESS;
1288 
1289 _exit:
1290  IntVirtMemUnmap(&map);
1291 
1292  return status;
1293 }
1294 
1295 
1296 static INTSTATUS
1298  _In_ QWORD Gva,
1299  _In_ DWORD Tag,
1300  _Out_ QWORD *Alloc
1301  )
1315 {
1316  BYTE *map;
1317  QWORD aligned;
1318  DWORD off;
1319  INTSTATUS status;
1320 
1321  aligned = Gva & PAGE_MASK;
1322  off = ROUND_UP(Gva & PAGE_OFFSET, gGuest.WordSize);
1323 
1324  status = IntVirtMemMap(aligned, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1325  if (!INT_SUCCESS(status))
1326  {
1327  return status;
1328  }
1329 
1330  while (off < PAGE_SIZE)
1331  {
1332  QWORD addr = gGuest.Guest64 ? *(QWORD *)((size_t)map + off) :
1333  *(DWORD *)((size_t)map + off);
1334  DWORD tag;
1335 
1336  status = IntKernVirtMemFetchDword(addr - WIN_POOL_HEADER_SIZE + 4, &tag);
1337  if (INT_SUCCESS(status) && tag == Tag)
1338  {
1339  *Alloc = addr;
1340  goto _exit;
1341  }
1342 
1343  off += gGuest.WordSize;
1344  }
1345 
1346  status = INT_STATUS_NOT_FOUND;
1347 
1348 _exit:
1349  IntVirtMemUnmap(&map);
1350 
1351  return status;
1352 }
1353 
1354 
1355 static INTSTATUS
1357  _In_ QWORD Gva,
1358  _Out_ QWORD *PortPool
1359  )
1382 {
1383  QWORD inetCmp, tcpCmp, pool;
1384  DWORD tag;
1385  INTSTATUS status;
1386 
1387  if (ROUND_DOWN(Gva, WIN_POOL_HEADER_SIZE) != Gva)
1388  {
1390  }
1391 
1392  status = IntKernVirtMemFetchDword(Gva - WIN_POOL_HEADER_SIZE + 4, &tag);
1393  if (!INT_SUCCESS(status) || POOL_TAG_INCS != tag)
1394  {
1396  }
1397 
1398  status = IntWinNetSearchForAlloc(Gva, POOL_TAG_INCO, &inetCmp);
1399  if (!INT_SUCCESS(status))
1400  {
1401  return status;
1402  }
1403 
1404  status = IntWinNetSearchForAlloc(inetCmp, POOL_TAG_TCCO, &tcpCmp);
1405  if (!INT_SUCCESS(status))
1406  {
1407  return status;
1408  }
1409 
1410  pool = 0;
1411  status = IntKernVirtMemFetchWordSize(tcpCmp, &pool);
1412  if (!INT_SUCCESS(status))
1413  {
1414  return status;
1415  }
1416 
1417  if ((pool & PAGE_OFFSET) != 0)
1418  {
1420  }
1421 
1422  *PortPool = pool;
1423 
1424  return INT_STATUS_SUCCESS;
1425 }
1426 
1427 
1428 
1429 static INTSTATUS
1431  _In_ QWORD Gva,
1432  _Out_ QWORD *PortPool
1433  )
1454 {
1455  QWORD addr;
1456  INTSTATUS status;
1457 
1458  union
1459  {
1460  DWORD Ptr32[2];
1461  QWORD Ptr64[2];
1462  } ptrs;
1463 
1464  if ((Gva & PAGE_OFFSET) != 0)
1465  {
1467  }
1468 
1469  status = IntKernVirtMemRead(Gva, 2 * gGuest.WordSize, &ptrs, NULL);
1470  if (!INT_SUCCESS(status))
1471  {
1472  return status;
1473  }
1474 
1475  addr = gGuest.Guest64 ? ptrs.Ptr64[0] : ptrs.Ptr32[0];
1476  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, addr) &&
1477  (addr & PAGE_OFFSET) == 0)
1478  {
1479  *PortPool = Gva;
1480  return INT_STATUS_SUCCESS;
1481  }
1482 
1483  addr = gGuest.Guest64 ? ptrs.Ptr64[1] : ptrs.Ptr32[1];
1484  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, addr) &&
1485  (addr & PAGE_OFFSET) == 0)
1486  {
1487  *PortPool = addr;
1488  return INT_STATUS_SUCCESS;
1489  }
1490 
1492 }
1493 
1494 
1495 static INTSTATUS
1497  _In_ QWORD Gva,
1498  _In_ QWORD Addr
1499  )
1524 {
1525  void *map;
1526  QWORD pool, sz, buf, pa;
1527  size_t i;
1528  DWORD tag;
1529  INTSTATUS status;
1530 
1531  if (gWinNet.Bitmap.Found)
1532  {
1534  }
1535 
1536  if (gGuest.OSVersion > 10586)
1537  {
1538  status = IntWinNetGetTcpPortPoolFromCompartment(Gva, &pool);
1539  }
1540  else
1541  {
1542  status = IntWinNetGetTcpPortPool(Gva, &pool);
1543  }
1544  if (!INT_SUCCESS(status))
1545  {
1546  return status;
1547  }
1548 
1549  status = IntVirtMemMap(pool, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1550  if (!INT_SUCCESS(status))
1551  {
1552  return status;
1553  }
1554 
1555  buf = pa = 0;
1556  status = INT_STATUS_NOT_FOUND;
1557  for (i = 0; i < PAGE_SIZE - 2ull * gGuest.WordSize; i += gGuest.WordSize)
1558  {
1559  size_t p = (size_t)map + i;
1560  sz = gGuest.Guest64 ? ((QWORD *)p)[0] : ((DWORD *)p)[0];
1561  buf = gGuest.Guest64 ? ((QWORD *)p)[1] : ((DWORD *)p)[1];
1562  pa = gGuest.Guest64 ? ((QWORD *)p)[2] : ((DWORD *)p)[2];
1563 
1564  sz &= 0xFFFFFFFF;
1565 
1566  if (0x10000 == sz && IS_KERNEL_POINTER_WIN(gGuest.Guest64, buf) &&
1567  ((buf & PAGE_MASK) == (pool & PAGE_MASK)))
1568  {
1569  status = INT_STATUS_SUCCESS;
1570  break;
1571  }
1572  }
1573 
1574  if (!INT_SUCCESS(status))
1575  {
1576  goto _exit;
1577  }
1578 
1579  status = IntKernVirtMemFetchDword(pa - WIN_POOL_HEADER_SIZE + 4, &tag);
1580  if (!INT_SUCCESS(status) || POOL_TAG_INPA != tag)
1581  {
1583  goto _exit;
1584  }
1585 
1586  gWinNet.Bitmap.PortAssignment = pool + i + 2ull * gGuest.WordSize;
1587  gWinNet.Bitmap.Buffer = buf;
1588 
1589  TRACE("[WINNET] Found tcpip!TcpPortPool @ 0x%016llx, BitmapBuffer @ 0x%016llx, "
1590  "and port assignment array @ 0x%016llx from 0x%016llx:0x%016llx\n", pool,
1592 
1594 
1595  status = INT_STATUS_SUCCESS;
1596 
1597 _exit:
1598  IntVirtMemUnmap(&map);
1599 
1600  return status;
1601 }
1602 
1603 
1604 static INTSTATUS
1606  void
1607  )
1618 {
1620  PKERNEL_DRIVER drv;
1621  DWORD objs;
1622  INTSTATUS status;
1623 
1625  {
1627  }
1628 
1629  drv = IntDriverFindByName(u"tcpip.sys");
1630  if (NULL == drv)
1631  {
1632  ERROR("[ERROR] Failed to find `tcpip.sys`\n");
1633  return INT_STATUS_NOT_FOUND;
1634  }
1635 
1636  TRACE("[WINNET] tcpip.sys @ 0x%016llx\n", drv->BaseVa);
1637 
1638  status = IntPeGetSectionHeaderByName(drv->BaseVa, NULL, ".data", gGuest.Mm.SystemCr3, &sec);
1639  if (!INT_SUCCESS(status))
1640  {
1641  ERROR("[ERROR] Failed to find `.data` section in `tcpip.sys`: 0x%08x\n", status);
1642  return status;
1643  }
1644 
1645  objs = 0;
1646  for (size_t page = 0; page < ROUND_UP((size_t)sec.Misc.VirtualSize, PAGE_SIZE); page += PAGE_SIZE)
1647  {
1648  QWORD gva = drv->BaseVa + sec.VirtualAddress + page;
1649  void *map;
1650 
1651  status = IntVirtMemMap(gva, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1652  if (!INT_SUCCESS(status))
1653  {
1654  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", gva, status);
1655  return status;
1656  }
1657 
1658  for (size_t i = 0; objs < 2 && i < PAGE_SIZE / gGuest.WordSize; i++)
1659  {
1660  QWORD ptr = gGuest.Guest64 ? ((QWORD *)map)[i] : ((DWORD *)map)[i];
1661 
1662  status = IntWinNetFindTcpPartition(ptr, gva + i * gGuest.WordSize);
1663  if (INT_SUCCESS(status))
1664  {
1665  objs++;
1666  continue;
1667  }
1668 
1669  status = IntWinNetFindTcpBitmap(ptr, gva + i * gGuest.WordSize);
1670  if (INT_SUCCESS(status))
1671  {
1672  objs++;
1673  continue;
1674  }
1675  }
1676 
1677  IntVirtMemUnmap(&map);
1678 
1679  if (objs == 2)
1680  {
1681  status = INT_STATUS_SUCCESS;
1682  break;
1683  }
1684  }
1685 
1686  return status;
1687 }
1688 
1689 
1690 static INTSTATUS
1692  _In_ PFUNC_IntWinNetCallback Callback,
1693  _Inout_opt_ void *Context
1694  )
1703 {
1704  INTSTATUS status;
1705 
1706  status = IntWinNetFindTcpObjects();
1707  if (!INT_SUCCESS(status))
1708  {
1709  ERROR("[ERROR] IntWinNetFindTcpObjects failed ( 0x%016llx, 0x%016llx): 0x%08x\n",
1711  return status;
1712  }
1713 
1714  status = IntWinNetParseTcpPartition(Callback, Context);
1715  if (!INT_SUCCESS(status))
1716  {
1717  WARNING("[WARNING] IntWinNetParseTcpPartition failed: 0x%08x\n", status);
1718  }
1719 
1720  status = IntWinNetParseTcpBitmap(Callback, Context);
1721  if (!INT_SUCCESS(status))
1722  {
1723  WARNING("[WARNING] IntWinNetParseTcpBitmap failed: 0x%08x\n", status);
1724  }
1725 
1726  return status;
1727 }
1728 
1729 
1732  _In_ const INTRONET_ENDPOINT *Endpoint,
1733  _Inout_opt_ void *Context
1734  )
1745 {
1746  INTSTATUS status;
1748  const WIN_PROCESS_OBJECT *proc = (WIN_PROCESS_OBJECT *)Context;
1749 
1750  if (proc != Endpoint->OwnerProcess)
1751  {
1753  }
1754 
1755  IntAlertFillConnection(Endpoint, evt);
1756 
1757  status = IntNotifyIntroEvent(introEventConnectionEvent, evt, sizeof(*evt));
1758  if (!INT_SUCCESS(status))
1759  {
1760  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
1761  }
1762 
1763  return status;
1764 }
1765 
1766 
1767 INTSTATUS
1769  _In_ WIN_PROCESS_OBJECT *Process
1770  )
1779 {
1781  {
1783  }
1784 
1786 }
1787 
1788 
1791  _In_ const INTRONET_ENDPOINT *Endpoint,
1792  _Inout_opt_ void *Context
1793  )
1802 {
1803  char local[INTRONET_MIN_BUFFER_SIZE] = { 0 };
1804  char remote[INTRONET_MIN_BUFFER_SIZE] = { 0 };
1805 
1806  UNREFERENCED_PARAMETER(Context);
1807 
1808  IntNetAddrToStr(Endpoint->AddressFamily, &Endpoint->LocalAddress, local);
1809  IntNetAddrToStr(Endpoint->AddressFamily, &Endpoint->RemoteAddress, remote);
1810 
1811  LOG("[WINNET] Endpoint @ 0x%016llx Local %s:%d Remote %s:%d State %s Owner 0x%016llx (%s:%u)\n",
1812  Endpoint->Endpoint, local, Endpoint->LocalPort, remote, Endpoint->RemotePort,
1813  IntNetStateToString(Endpoint->State), Endpoint->Endpoint,
1814  Endpoint->OwnerProcess->Name, Endpoint->OwnerProcess->Pid);
1815 
1816  return INT_STATUS_SUCCESS;
1817 }
1818 
1819 
1820 //
1821 // IntWinNetDumpConnections
1822 //
1823 INTSTATUS
1825  void
1826  )
1832 {
1834 }
1835 
This is the structure as documented in ntddk.h.
Definition: wddefs.h:775
BYTE Bits[]
The bit array.
Definition: bitmask.h:18
INTSTATUS IntWinNetSendProcessConnections(WIN_PROCESS_OBJECT *Process)
Send connection events for all active connections whose owner is the given process.
Definition: winnet.c:1768
static INTSTATUS IntWinNetGetTcpPortPool(QWORD Gva, QWORD *PortPool)
Get the address of a tcp port pool for windows versions before RS1.
Definition: winnet.c:1430
Represents a bit mask.
Definition: bitmask.h:15
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
#define ROUND_UP(what, to)
Definition: introdefs.h:158
DWORD PoolType
Definition: wddefs.h:441
DWORD InAddr
Definition: wddefs.h:1948
#define TCP_DIRECTORY_ENTRY_SIZE
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
#define TCP_PORT_ASSIGNMENT_ARRAY_INDEX(i)
static INTSTATUS IntWinNetGetTcpListener(QWORD Gva, INTRONET_ENDPOINT *Endpoint)
Search for a tcp endpoint structure from a given address.
Definition: winnet.c:675
EVENT_CONNECTION_EVENT Connection
Definition: alerts.h:30
DWORD PoolTag
Definition: wddefs.h:420
union _ADDRINFO::@196 Addr32
uint8_t BYTE
Definition: intro_types.h:47
#define _In_
Definition: intro_sal.h:21
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTSTATUS IntWinNetDumpConnections(void)
Dump all active guest connections.
Definition: winnet.c:1824
INTRONET_PORT LocalPort
Local port.
Definition: intronet.h:37
uint16_t WORD
Definition: intro_types.h:48
#define TCP_PORT_ASSIGNMENT_INDEX(i)
POOL_HEADER32 Header32
Definition: wddefs.h:462
QWORD BaseVa
The guest virtual address of the kernel module that owns this driver object.
Definition: drivers.h:41
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define PAGE_OFFSET
Definition: pgtable.h:32
QWORD Table
Will hold the address of the TCP partition table.
Definition: winnet.c:45
#define ARRAYSIZE(A)
Definition: introdefs.h:101
static INTSTATUS IntWinNetGetAddrInfo(const BYTE *Buffer, size_t Size, size_t *Offset, void *Context)
Callback to get the address information from a guest buffer.
Definition: winnet.c:208
DWORD BlockSize
Definition: wddefs.h:440
static INTRO_NET_AF IntWinNetConvertAf(WORD Af)
Convert a windows address family value to an introcore one.
Definition: winnet.c:64
#define ADDR_FAM_OFFSET
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
INTRONET_PORT RemotePort
Remote port.
Definition: intronet.h:46
static INTSTATUS IntWinNetParseTcpPartition(PFUNC_IntWinNetCallback Callback, void *Context)
Iterate through the tcpip!PartitionTable and invoke the callback on each found connection.
Definition: winnet.c:793
INTRO_NET_AF AddressFamily
Address family.
Definition: intronet.h:29
#define AF_INET
IPv4.
Definition: wddefs.h:1913
WIN_PROCESS_OBJECT * OwnerProcess
Pointer to the process that owns the connection.
Definition: intronet.h:57
INTSTATUS IntKernVirtMemFetchWordSize(QWORD GuestVirtualAddress, void *Data)
Reads a guest pointer from the guest kernel memory.
Definition: introcore.c:847
struct _ADDRINFO::@196::@197 Win7
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define POOL_TAG_TCHT
Tcp Hash Table.
Definition: wddefs.h:1820
BYTE BitMaskTest(BITMASK *BitMask, DWORD BitPos)
Tests a bit in a BITMASK.
Definition: bitmask.c:151
DWORD OSVersion
Os version.
Definition: guests.h:281
Describe information about the TCP partition containing endpoints.
Definition: winnet.c:43
static INTSTATUS IntWinNetGetLocalAddr(QWORD Gva, INTRONET_ENDPOINT *Endpoint)
Get the local address of a connection from a given address.
Definition: winnet.c:168
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
#define AF_INET6
IPv6.
Definition: wddefs.h:1914
INTSTATUS IntStructFill(const void *Buffer, size_t Size, INT_STRUCT_INVARIANT *Invariants, size_t Count, BOOLEAN LogErrors, void *Context)
Fill an internal structure with information gathered from the guest by applying a list of invariants ...
Definition: structs.c:8
#define NET_BYTE_ORDER(Word)
Switch a WORD from network endianness to little endina.
Definition: winnet.c:29
BITMASK * BitMaskAlloc(size_t Size)
Creates a new BITMASK.
Definition: bitmask.c:56
static INTSTATUS IntWinNetGetTcpEndpoint(QWORD Gva, INTRONET_ENDPOINT *Endpoint)
Search for a TCP endpoint structure from a given address.
Definition: winnet.c:617
#define POOL_TAG_INCO
Inet Compartment.
Definition: wddefs.h:1814
#define WIN_POOL_BLOCK_SIZE
Definition: wddefs.h:474
const char * IntNetStateToString(INTRO_NET_STATE State)
Converts a connection state to a string.
Definition: intronet.h:69
QWORD Local
Definition: wddefs.h:1920
#define WIN_POOL_HEADER_SIZE
Definition: wddefs.h:469
#define INT_STATUS_BUFFER_OVERFLOW
Definition: introstatus.h:200
static INTSTATUS IntWinNetFindTcpObjects(void)
Iterate through the .data section of tcpip.sys and attempt to find the objects that we need to initia...
Definition: winnet.c:1605
#define INTRO_OPT_EVENT_CONNECTIONS
Enable connection events.
Definition: intro_types.h:482
static INTSTATUS IntWinNetDumpConnection(const INTRONET_ENDPOINT *Endpoint, void *Context)
Log a connection.
Definition: winnet.c:1790
#define POOL_TAG_INCS
Inet Compartment Set.
Definition: wddefs.h:1816
static INTSTATUS IntWinNetSendConnectionEvent(const INTRONET_ENDPOINT *Endpoint, void *Context)
Send a connection event.
Definition: winnet.c:1731
#define LOG(fmt,...)
Definition: glue.h:61
Describes a kernel driver.
Definition: drivers.h:30
KERNEL_DRIVER * IntDriverFindByName(const void *Name)
Searches for a driver by its name.
Definition: drivers.c:266
#define WINNET_LIST_ITERS_CAP
Upper limit for iterating through guest linked lists.
Definition: winnet.c:35
#define INTRONET_MIN_BUFFER_SIZE
The minimum buffer size needed for the textual representation of an IP address.
Definition: intronet.h:12
DWORD Count
Will hold the number of elements inside the partition table.
Definition: winnet.c:46
static INTSTATUS IntWinNetCheckPartition(const BYTE *Buffer, size_t Size)
Check wether a buffer contains a valid undocumented tcpip!_PARTITION object.
Definition: winnet.c:1062
DWORD Size
Will hold the size of a partition element.
Definition: winnet.c:47
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
#define _Inout_opt_
Definition: intro_sal.h:31
#define INT_STATUS_ALIGNMENT_INCONSISTENCY
Definition: introstatus.h:189
#define _Inout_
Definition: intro_sal.h:20
INTSTATUS IntKernVirtMemFetchDword(QWORD GuestVirtualAddress, DWORD *Data)
Reads 4 bytes from the guest kernel memory.
Definition: introcore.c:829
#define INT_STATUS_ALREADY_INITIALIZED
Definition: introstatus.h:263
static INTSTATUS IntWinNetGetOwner(const BYTE *Buffer, size_t Size, size_t *Offset, void *Context)
Callback to get the owner process from a guest buffer.
Definition: winnet.c:367
static INTSTATUS IntWinNetSearchForAlloc(QWORD Gva, DWORD Tag, QWORD *Alloc)
Search for an allocation with given tag.
Definition: winnet.c:1297
POOL_HEADER64 Header64
Definition: wddefs.h:463
Structure that describe the kernel objects needed by introcore to extract connections.
Definition: winnet.c:40
DWORD BlockSize
Definition: wddefs.h:411
This is the structure as documented in ntddk.h.
Definition: wddefs.h:800
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
static INTSTATUS IntWinNetGetAddrFam(const BYTE *Buffer, size_t Size, size_t *Offset, void *Context)
Callback to get the address family from a guest buffer.
Definition: winnet.c:88
#define TCPIP_MAX_PARTITION_CNT
Maximum allowed count for the tcpip!PartitionCount value.
Definition: winnet.c:26
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
unsigned long long QWORD
Definition: intro_types.h:53
DWORD IntNetAddrToStr(const INTRO_NET_AF Family, const INTRONET_ADDRESS *Address, CHAR *String)
Converts an IP address to a string.
Definition: intronet.c:11
QWORD Current
The currently used options.
Definition: guests.h:236
An endpoint.
Definition: intronet.h:26
Describe information about the TCP port pool containing listeners.
Definition: winnet.c:53
BOOLEAN Found
TRUE if we managed to find the elements above.
Definition: winnet.c:49
#define POOL_TAG_TCCO
Tcp Compartment.
Definition: wddefs.h:1819
static INTSTATUS IntWinNetGetTcpPortPoolFromCompartment(QWORD Gva, QWORD *PortPool)
Get the address of a tcp port pool for windows RS1 and newer.
Definition: winnet.c:1356
#define TRUE
Definition: intro_types.h:30
UINT32 VirtualAddress
Definition: winpe.h:85
#define IS_KERNEL_POINTER_WIN(is64, p)
Checks if a guest virtual address resides inside the Windows kernel address space.
Definition: wddefs.h:76
static INTSTATUS IntWinNetGetPortsAndState(const BYTE *Buffer, size_t Size, size_t *Offset, void *Context)
Callback to get the ports and state from a guest buffer.
Definition: winnet.c:288
static INTSTATUS IntWinNetFindTcpBitmap(QWORD Gva, QWORD Addr)
Get tcpip!TcpPortPool information form the guest and initialize gWinNet.
Definition: winnet.c:1496
TIMER_FRIENDLY void IntDumpBuffer(const void *Buffer, QWORD Gva, DWORD Length, DWORD RowLength, DWORD ElementLength, BOOLEAN LogHeader, BOOLEAN DumpAscii)
This function dumps a given buffer in a user friendly format.
Definition: dumper.c:48
#define TRACE(fmt,...)
Definition: glue.h:58
#define INT_STRUCT_MAX_INVARIANT_CNT
Upper limit of the number of invariants to be applied to a bufffer.
Definition: structs.h:56
#define INT_STATUS_INVALID_PARAMETER_5
Definition: introstatus.h:74
DWORD PoolType
Definition: wddefs.h:412
static INTSTATUS IntWinNetGetListenerAddressInfo(const BYTE *Buffer, size_t Size, size_t *Offset, void *Context)
Callback to get the address family and local address from a guest buffer.
Definition: winnet.c:414
union _IMAGE_SECTION_HEADER::@214 Misc
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:367
#define VALID_PORT(Port)
Check if a port is valid. Not the best check, but a valid check.
Definition: winnet.c:32
INTRO_NET_STATE State
Connection state.
Definition: intronet.h:32
Informational event containing the connections opened by a process. See EVENT_CONNECTION_EVENT.
Definition: intro_types.h:113
#define IntPeGetSectionHeaderByName(Base, Buff, Name, Cr3, Sec)
Definition: winpe.h:765
static INTSTATUS IntWinNetIterateSlinkedList(INTRONET_PORT Port, QWORD Gva, PFUNC_IntWinNetCallback Callback, void *Context)
Iterate through a single linked list of tcpip endpoint structures and invoke the callback on each val...
Definition: winnet.c:885
WORD INTRONET_PORT
Definition: intronet.h:22
#define INT_STATUS_ALREADY_INITIALIZED_HINT
Definition: introstatus.h:323
static INTSTATUS IntWinNetParseTcpBitmap(PFUNC_IntWinNetCallback Callback, void *Context)
Will iterate through the guest port bitmap and invoke the callback on each valid connection.
Definition: winnet.c:943
#define WARNING(fmt,...)
Definition: glue.h:60
#define _Inout_updates_(expr)
Definition: intro_sal.h:32
DWORD PoolTag
Definition: wddefs.h:447
#define ROUND_DOWN(what, to)
Definition: introdefs.h:159
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
struct _ADDRINFO::@196::@198 Win8AndAbove
struct _WINNET_STATE gWinNet
uint32_t DWORD
Definition: intro_types.h:49
UINT32 VirtualSize
Definition: winpe.h:83
TIMER_FRIENDLY void IntDumpGvaEx(QWORD Gva, DWORD Length, QWORD Cr3, DWORD RowLength, DWORD ElementLength, BOOLEAN LogHeader, BOOLEAN DumpAscii)
This function dumps a given GVA in a user friendly format. This function uses IntDumpBuffer to perfor...
Definition: dumper.c:204
static INTSTATUS IntWinNetIterateConnections(PFUNC_IntWinNetCallback Callback, void *Context)
Iterate through guest connections and invoke the callback on each one.
Definition: winnet.c:1691
#define POOL_TAG_TCPE
Tcp Endpoint.
Definition: wddefs.h:1823
INTSTATUS(* PFUNC_IntWinNetCallback)(const INTRONET_ENDPOINT *Connection, void *Context)
Definition: winnet.h:12
#define _In_reads_bytes_(expr)
Definition: intro_sal.h:25
#define POOL_TAG_TCPL
Tcp Listener.
Definition: wddefs.h:1824
struct _WINNET_STATE::_PARTITION Partition
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
BYTE Ipv6[16]
IPv6 address.
Definition: intronet.h:18
__must_check INTSTATUS IntVirtMemMap(QWORD Gva, DWORD Length, QWORD Cr3, DWORD Flags, void **HostPtr)
Maps a guest virtual memory range inside Introcore virtual address space.
Definition: introcore.c:2134
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
#define _Function_class_(expr)
Definition: intro_sal.h:40
struct _LOCAL_ADDRESS::@199 Addr32
INTRONET_ADDRESS RemoteAddress
Remote address.
Definition: intronet.h:42
#define INT_STATUS_INVALID_DATA_TYPE
Definition: introstatus.h:139
#define FIX_GUEST_POINTER(is64, x)
Masks the unused part of a Windows guest virtual address.
Definition: wddefs.h:87
PWIN_PROCESS_OBJECT IntWinProcFindObjectByEprocess(QWORD Eprocess)
Finds a process by the address of its _EPROCESS structure.
Definition: winprocesshp.c:96
QWORD PortAssignment
Will hold the address of the array of inet port assignment arrays in the port pool.
Definition: winnet.c:55
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
struct _ADDRINFO::@195 Addr64
#define POOL_TAG_INNL
Used to search for address family.
Definition: wddefs.h:1817
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
size_t Offset
Offset from where the field/structure is to be extracted.
Definition: structs.h:42
Event structure for connections.
Definition: intro_types.h:2006
struct _WINNET_STATE::_BITMAP Bitmap
void IntAlertFillConnection(const INTRONET_ENDPOINT *Connection, EVENT_CONNECTION_EVENT *Event)
Saves information about a guest connection in an event.
Definition: alerts.c:1331
INTRONET_ADDRESS LocalAddress
Local address.
Definition: intronet.h:35
enum _INTRO_NET_AF INTRO_NET_AF
Address family.
#define POOL_TAG_INPA
Inet Port Array.
Definition: wddefs.h:1815
QWORD Buffer
Will hold the address of the bitmap buffer in the port pool.
Definition: winnet.c:56
void BitMaskFree(BITMASK **BitMask)
Frees a BITMASK allocated by BitMaskAlloc.
Definition: bitmask.c:83
BOOLEAN Found
TRUE if we managed to find the elements above.
Definition: winnet.c:58
#define PAGE_MASK
Definition: pgtable.h:35
#define TCP_DIRECTORY_ENTRY_CNT
QWORD Remote
Definition: wddefs.h:1922
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271
INTRO_NET_STATE IntNetConvertState(const DWORD State)
Converts a guest connection state to an Introcore connection state.
Definition: intronet.c:210
BYTE Ipv4[4]
IPv4 address.
Definition: intronet.h:19
struct _LOCAL_ADDRESS::@200 Addr64
static INTSTATUS IntWinNetFillTcpStruct(const QWORD Gva, INT_STRUCT_INVARIANT *Invariants, size_t Count, DWORD PoolTag, BOOLEAN LogErrors, INTRONET_ENDPOINT *Endpoint)
Wrapper over IntStructFill to map a guest page and perform the search there.
Definition: winnet.c:505
#define WINNET_CLEAN_PTR_MASK
#define INT_STATUS_INVALID_DATA_SIZE
Definition: introstatus.h:142
static INTSTATUS IntWinNetFindTcpPartition(QWORD PartitionAddress, QWORD PartitionPointer)
Get tcpip!PartitionTable information from the guest and initialize gWinNet.
Definition: winnet.c:1145
static INTSTATUS IntWinNetIterateLinkedList(QWORD Gva, PFUNC_IntWinNetCallback Callback, void *Context)
Iterate through a doubly linked list of tcpip!_TCP_ENDPOINT structures and invoke the callback on eac...
Definition: winnet.c:727
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:83
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
#define INT_OFFSET_NOT_INITIALIZED
Specifies that an offset value is yet to be searched for.
Definition: structs.h:50
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68