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 
17 #define NET_BYTE_ORDER(Word) (((Word) >> 8) | ((Word) << 8))
18 
20 #define VALID_PORT(Port) (0 != (Port) && 49151 != (Port))
21 
23 #define WINNET_LIST_ITERS_CAP 64
24 struct _WINNET_STATE
29 {
31  struct _PARTITION
32  {
36 
38  } Partition;
39 
41  struct _BITMAP
42  {
45 
47  } Bitmap;
48 } gWinNet;
49 
50 
51 static inline INTRO_NET_AF
53  _In_ WORD Af
54  )
62 {
63  switch (Af)
64  {
65  case AF_INET:
66  return introNetAfIpv4;
67  case AF_INET6:
68  return introNetAfIpv6;
69  }
70 
71  return introNetAfUnknown;
72 }
73 
74 
75 static INTSTATUS
77  _In_reads_bytes_(Size) const BYTE *Buffer,
78  _In_ size_t Size,
79  _Inout_ size_t *Offset,
80  _Inout_ void *Context
81  )
96 {
97 #define ADDR_FAM_OFFSET (gGuest.Guest64 ? (gGuest.OSVersion >= 9200 ? 0x18 : 0x14) : 0xC)
98 
99  POOL_HEADER ph;
100  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
101  size_t off;
102  QWORD addr;
103  INTSTATUS status;
104  DWORD tag;
105  WORD fam;
106 
107  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
108 
109  off = *Offset;
110  *Offset += gGuest.WordSize;
111 
112  if (off + gGuest.WordSize > Size)
113  {
115  }
116 
117  addr = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
118 
119  if (ROUND_DOWN(addr, WIN_POOL_HEADER_SIZE) != addr)
120  {
122  }
123 
124  status = IntKernVirtMemRead(addr - WIN_POOL_HEADER_SIZE, WIN_POOL_HEADER_SIZE, &ph, NULL);
125  if (!INT_SUCCESS(status))
126  {
127  return status;
128  }
129 
130  tag = gGuest.Guest64 ? ph.Header64.PoolTag : ph.Header32.PoolTag;
131 
132  if (tag != POOL_TAG_INNL)
133  {
135  }
136 
137  status = IntKernVirtMemRead(addr + ADDR_FAM_OFFSET, sizeof(fam), &fam, NULL);
138  if (!INT_SUCCESS(status))
139  {
140  return status;
141  }
142 
143  net->AddressFamily = IntWinNetConvertAf(fam);
144  if (net->AddressFamily == introNetAfUnknown)
145  {
147  }
148 
149  return INT_STATUS_SUCCESS;
150 
151 #undef ADDR_FAM_OFFSET
152 }
153 
154 
155 static INTSTATUS
157  _In_ QWORD Gva,
158  _Out_ INTRONET_ENDPOINT *Endpoint
159  )
171 {
172  INTSTATUS status;
173  QWORD addr;
174  LOCAL_ADDRESS la;
175 
176  addr = FIX_GUEST_POINTER(gGuest.Guest64, Gva);
177  status = IntKernVirtMemRead(addr, gGuest.Guest64 ? sizeof(la.Addr64) : sizeof(la.Addr32), &la, NULL);
178  if (!INT_SUCCESS(status))
179  {
180  return status;
181  }
182 
183  addr = gGuest.Guest64 ? la.Addr64.InAddr : la.Addr32.InAddr;
184  status = IntKernVirtMemFetchWordSize(addr, &addr);
185  if (!INT_SUCCESS(status))
186  {
187  return status;
188  }
189 
190  addr = FIX_GUEST_POINTER(gGuest.Guest64, addr);
191  return IntKernVirtMemRead(addr, sizeof(Endpoint->LocalAddress), &Endpoint->LocalAddress, NULL);
192 }
193 
194 
195 static INTSTATUS
197  _In_reads_bytes_(Size) const BYTE *Buffer,
198  _In_ size_t Size,
199  _Inout_ size_t *Offset,
200  _Inout_ void *Context
201  )
220 {
221  QWORD addr, local, remote;
222  ADDRINFO ai = { 0 };
223  DWORD sz;
224  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
225  size_t off;
226  INTSTATUS status;
227 
228  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
229 
230  if (gGuest.Guest64)
231  {
232  sz = sizeof(ai.Addr64);
233  }
234  else
235  {
236  sz = gGuest.OSVersion >= 9200 ? sizeof(ai.Addr32.Win8AndAbove) : sizeof(ai.Addr32.Win7);
237  }
238 
239  off = *Offset;
240  *Offset += gGuest.WordSize;
241 
242  if (off + gGuest.WordSize > Size)
243  {
245  }
246 
247  addr = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
248  status = IntKernVirtMemRead(addr, sz, &ai, NULL);
249  if (!INT_SUCCESS(status))
250  {
251  return status;
252  }
253 
254  if (gGuest.Guest64)
255  {
256  remote = ai.Addr64.Remote;
257  local = ai.Addr64.Local;
258  }
259  else
260  {
261  remote = gGuest.OSVersion >= 9200 ? ai.Addr32.Win8AndAbove.Remote : ai.Addr32.Win7.Remote;
262  local = gGuest.OSVersion >= 9200 ? ai.Addr32.Win8AndAbove.Local : ai.Addr32.Win7.Local;
263  }
264 
265  status = IntKernVirtMemRead(remote, sizeof(net->RemoteAddress), &net->RemoteAddress, NULL);
266  if (!INT_SUCCESS(status))
267  {
268  return status;
269  }
270 
271  return IntWinNetGetLocalAddr(local, net);
272 }
273 
274 
275 static INTSTATUS
277  _In_reads_bytes_(Size) const BYTE *Buffer,
278  _In_ size_t Size,
279  _Inout_ size_t *Offset,
280  _Inout_ void *Context
281  )
302 {
303  INTSTATUS status;
304  size_t off, skip;
305  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
306 
307  *Offset = ROUND_UP(*Offset, sizeof(DWORD));
308 
309  skip = 4;
310  off = *Offset;
311 
312  // Make sure we can fit both ports and the state.
313  if (off + 2 * sizeof(WORD) + sizeof(DWORD) > Size)
314  {
316  goto _exit;
317  }
318 
319  if (gGuest.Guest64 && IS_KERNEL_POINTER_WIN(TRUE, *(const QWORD *)(Buffer + off)))
320  {
321  skip = 8;
323  goto _exit;
324  }
325 
326  net->State = IntNetConvertState(*(const DWORD *)(Buffer + off));
327  if (net->State == introNetStateUnknown ||
328  net->State == introNetStateListening ||
329  net->State == introNetStateClosed)
330  {
332  goto _exit;
333  }
334 
335  net->LocalPort = NET_BYTE_ORDER(*(const WORD *)(Buffer + off + 4));
336  net->RemotePort = NET_BYTE_ORDER(*(const WORD *)(Buffer + off + 6));
337 
338  if (!VALID_PORT(net->LocalPort) || !VALID_PORT(net->RemotePort))
339  {
341  goto _exit;
342  }
343 
344  skip += 4;
345  status = INT_STATUS_SUCCESS;
346 
347 _exit:
348  *Offset += skip;
349 
350  return status;
351 }
352 
353 
354 static INTSTATUS
356  _In_reads_bytes_(Size) const BYTE *Buffer,
357  _In_ size_t Size,
358  _Inout_ size_t *Offset,
359  _Inout_ void *Context
360  )
374 {
375  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
376  QWORD addr;
377  size_t off;
378 
379  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
380 
381  off = *Offset;
382  *Offset += gGuest.WordSize;
383 
384  if (off + gGuest.WordSize > Size)
385  {
387  }
388 
389  addr = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
390 
392  if (NULL == net->OwnerProcess)
393  {
395  }
396 
397  return INT_STATUS_SUCCESS;
398 }
399 
400 
401 static INTSTATUS
403  _In_reads_bytes_(Size) const BYTE *Buffer,
404  _In_ size_t Size,
405  _Inout_ size_t *Offset,
406  _Inout_ void *Context
407  )
426 {
427  INTRONET_ENDPOINT *net = (INTRONET_ENDPOINT *)Context;
428  QWORD addrinfo;
429  size_t off;
430  INTSTATUS status;
431  WORD port;
432 
433  *Offset = ROUND_UP(*Offset, gGuest.WordSize);
434 
435  off = *Offset;
436  *Offset += gGuest.WordSize;
437 
438  // Make sure we can fit the both the address and address family pointers
439  // and the local port, which is at an offset of 2 bytes from those pointers.
440  if (off + 2ull * gGuest.WordSize + 2 + sizeof(WORD) > Size)
441  {
443  }
444 
445  addrinfo = gGuest.Guest64 ? *(const QWORD *)(Buffer + off) : *(const DWORD *)(Buffer + off);
446  if (0 == addrinfo)
447  {
448  // Handle dual stack sockets.
449  if (introNetAfIpv4 == net->AddressFamily)
450  {
451  net->LocalAddress.Ipv4[0] = 127;
452  net->LocalAddress.Ipv4[3] = 1;
453  }
454  else
455  {
456  net->LocalAddress.Ipv6[15] = 1;
457  }
458  }
459  else
460  {
461  status = IntWinNetGetLocalAddr(addrinfo, net);
462  if (!INT_SUCCESS(status))
463  {
464  return status;
465  }
466  }
467 
468  // Get to the next pointer.
469  off += gGuest.WordSize;
470 
471  // Note that this will increase the offset.
472  status = IntWinNetGetAddrFam(Buffer, Size, &off, Context);
473  if (!INT_SUCCESS(status))
474  {
475  return status;
476  }
477 
478  port = NET_BYTE_ORDER(*(const WORD *)(Buffer + off + 2));
479  if (port != net->LocalPort)
480  {
482  }
483 
485 
486  *Offset += 2ull * gGuest.WordSize;
487 
488  return INT_STATUS_SUCCESS;
489 }
490 
491 
492 static INTSTATUS
494  _In_ const QWORD Gva,
496  _In_ size_t Count,
497  _In_ DWORD PoolTag,
498  _In_ BOOLEAN LogErrors,
499  _Inout_ INTRONET_ENDPOINT *Endpoint
500  )
514 {
515  QWORD alignedAddr;
516  const POOL_HEADER *ph;
517  BYTE *map;
518  size_t sz, originalOffset;
519  ssize_t offset;
520  INTSTATUS status;
521 
523  {
525  }
526 
527  if (NULL == Invariants)
528  {
530  }
531 
532  if (0 == Count || Count > INT_STRUCT_MAX_INVARIANT_CNT)
533  {
535  }
536 
537  if (NULL == Endpoint)
538  {
540  }
541 
542  alignedAddr = Gva & PAGE_MASK;
543  originalOffset = ROUND_DOWN(Gva & PAGE_OFFSET, WIN_POOL_HEADER_SIZE);
544 
545  status = IntVirtMemMap(alignedAddr, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
546  if (!INT_SUCCESS(status))
547  {
548  return status;
549  }
550 
551  ph = NULL;
552  sz = 0;
553  offset = originalOffset;
554  while (offset >= 0)
555  {
556  DWORD tag, type;
557 
558  ph = (const POOL_HEADER *)((size_t)map + offset);
559 
560  tag = gGuest.Guest64 ? ph->Header64.PoolTag : ph->Header32.PoolTag;
561  type = gGuest.Guest64 ? ph->Header64.PoolType : ph->Header32.PoolType;
562 
564  sz *= WIN_POOL_BLOCK_SIZE;
565 
566  if (tag == PoolTag && offset + sz <= PAGE_SIZE && type == NonPagedPoolMustSucceed)
567  {
568  break;
569  }
570 
571  offset -= WIN_POOL_HEADER_SIZE;
572  ph = NULL;
573  }
574 
575  if (NULL == ph)
576  {
577  status = INT_STATUS_NOT_FOUND;
578  goto _exit;
579  }
580 
581  if (offset + sz < originalOffset)
582  {
584  goto _exit;
585  }
586 
587  status = IntStructFill(map + offset, sz, Invariants, Count, LogErrors, Endpoint);
588  if (!INT_SUCCESS(status))
589  {
590  if (LogErrors)
591  {
592  IntDumpBuffer(map, alignedAddr, PAGE_SIZE, 16, 1, TRUE, TRUE);
593  }
594  }
595 
596 _exit:
597 
598  IntVirtMemUnmap(&map);
599 
600  return status;
601 }
602 
603 
604 static INTSTATUS
606  _In_ QWORD Gva,
607  _Inout_ INTRONET_ENDPOINT *Endpoint
608  )
617 {
618  INTSTATUS status;
619 
620  static BOOLEAN found = FALSE;
621  static INT_STRUCT_INVARIANT invs[] =
622  {
623  {
625  .Getter = IntWinNetGetAddrFam,
626  },
627  {
628  .Offset = INT_OFFSET_NOT_INITIALIZED,
629  .Getter = IntWinNetGetAddrInfo,
630  },
631  {
632  .Offset = INT_OFFSET_NOT_INITIALIZED,
633  .Getter = IntWinNetGetPortsAndState,
634  },
635  {
636  .Offset = INT_OFFSET_NOT_INITIALIZED,
637  .Getter = IntWinNetGetOwner,
638  },
639  };
640 
641  status = IntWinNetFillTcpStruct(Gva, invs, ARRAYSIZE(invs), POOL_TAG_TCPE, found, Endpoint);
642  if (!INT_SUCCESS(status))
643  {
644  WARNING("[WARNING] Failed to fill endpoint from 0x%016llx: 0x%08x\n", Gva, status);
645  if (!found)
646  {
647  for (size_t i = 0; i < ARRAYSIZE(invs); i++)
648  {
650  }
651  }
652  }
653  else
654  {
655  found = TRUE;
656  }
657 
658  return status;
659 }
660 
661 
662 static INTSTATUS
664  _In_ QWORD Gva,
665  _Inout_ INTRONET_ENDPOINT *Endpoint
666  )
675 {
676  INTSTATUS status;
677 
678  static BOOLEAN found = FALSE;
679  static INT_STRUCT_INVARIANT invs[] =
680  {
681  {
683  .Getter = IntWinNetGetOwner,
684  },
685 
686  {
687  .Offset = INT_OFFSET_NOT_INITIALIZED,
689  },
690  };
691 
692  status = IntWinNetFillTcpStruct(Gva, invs, ARRAYSIZE(invs), POOL_TAG_TCPL, found, Endpoint);
693  if (!INT_SUCCESS(status))
694  {
695  WARNING("[WARNING] Failed to fill listener from 0x%016llx: 0x%08x\n", Gva, status);
696 
697  if (!found)
698  {
699  for (size_t i = 0; i < ARRAYSIZE(invs); i++)
700  {
702  }
703  }
704  }
705  else
706  {
707  found = TRUE;
708  }
709 
710  return status;
711 }
712 
713 
714 static INTSTATUS
716  _In_ QWORD Gva,
717  _In_ PFUNC_IntWinNetCallback Callback,
718  _Inout_opt_ void *Context
719  )
730 {
731 
732  INTSTATUS status;
733  QWORD flink = 0;
734  QWORD head = FIX_GUEST_POINTER(gGuest.Guest64, Gva);
735  size_t iters = 0;
736 
737  status = IntKernVirtMemFetchWordSize(head, &flink);
738  if (!INT_SUCCESS(status))
739  {
740  return status;
741  }
742 
743  while (flink != head && iters++ < WINNET_LIST_ITERS_CAP)
744  {
745  QWORD old = flink;
746  INTRONET_ENDPOINT net = { 0 };
747 
748  status = IntWinNetGetTcpEndpoint(flink, &net);
749  if (!INT_SUCCESS(status))
750  {
751  WARNING("[WARNING] Failed to extract tcp endpoint from 0x%016llx: 0x%08x\n", flink, status);
752  return status;
753  }
754 
755  Callback(&net, Context);
756 
757  status = IntKernVirtMemFetchWordSize(old, &flink);
758  if (!INT_SUCCESS(status))
759  {
760  WARNING("[WARNING] IntKernVirtMemFetchWordSize failed for 0x%016llx: 0x%08x\n", old, status);
761  return status;
762  }
763 
764  flink = FIX_GUEST_POINTER(gGuest.Guest64, flink);
765 
766  // don't trust the guest, this may be a moment when this endpoint is to
767  // be removed from the linked list, so it's list entry will point to itself,
768  // causing an infinite loop here.
769  if (old == flink)
770  {
771  ERROR("[ERROR] Entry @ 0x%016llx points to itself\n", old);
772  return INT_STATUS_SUCCESS;
773  }
774  }
775 
776  return INT_STATUS_SUCCESS;
777 }
778 
779 
780 static INTSTATUS
782  _In_ PFUNC_IntWinNetCallback Callback,
783  _Inout_opt_ void *Context
784  )
808 {
809 #define TCP_DIRECTORY_ENTRY_CNT 0x80
810 #define TCP_DIRECTORY_ENTRY_SIZE (2ull * (gGuest.WordSize))
811 #define TCP_DIRECTORY_SIZE (TCP_DIRECTORY_ENTRY_CNT * TCP_DIRECTORY_ENTRY_SIZE)
812 
813  for (DWORD i = 0; i < gWinNet.Partition.Count; i++)
814  {
815  QWORD ht, dir, addr;
816  DWORD cnt, sz;
817  INTSTATUS status;
818 
819  union
820  {
823  } tbl;
824 
826 
827  ht = 0;
828  status = IntKernVirtMemFetchWordSize(addr, &ht);
829  if (!INT_SUCCESS(status))
830  {
831  ERROR("[ERROR] IntKernVirtMemFetchWordSize failed for 0x%016llx: 0x%08x\n",
832  addr, status);
833  return status;
834  }
835 
836  sz = gGuest.Guest64 ? sizeof(tbl.Ht64) : sizeof(tbl.Ht32);
837  status = IntKernVirtMemRead(ht, sz, &tbl, NULL);
838  if (!INT_SUCCESS(status))
839  {
840  continue;
841  }
842 
843  dir = gGuest.Guest64 ? tbl.Ht64.Directory : tbl.Ht32.Directory;
844  cnt = gGuest.Guest64 ? tbl.Ht64.NonEmptyBuckets : tbl.Ht32.NonEmptyBuckets;
845 
846  // don't iterate a directory with empty buckets.
847  if (cnt == 0)
848  {
849  continue;
850  }
851 
852  for (size_t j = 0; j < TCP_DIRECTORY_ENTRY_CNT; j++)
853  {
855 
856  status = IntWinNetIterateLinkedList(list, Callback, Context);
857  if (!INT_SUCCESS(status))
858  {
859  WARNING("[WARNING] IntWinNetIterateLinkedList failed: 0x%08x\n", status);
860  }
861  }
862 
863  }
864 #undef TCP_DIRECTORY_SIZE
865 #undef TCP_DIRECTORY_ENTRY_SIZE
866 #undef TCP_DIRECTORY_ENTRY_CNT
867 
868  return INT_STATUS_SUCCESS;
869 }
870 
871 
872 static INTSTATUS
874  _In_ INTRONET_PORT Port,
875  _In_ QWORD Gva,
876  _In_ PFUNC_IntWinNetCallback Callback,
877  _Inout_opt_ void *Context
878  )
890 {
891 // Pointers inside the InetPortAssignmentArray are not properly aligned. There may be some refcount bits
892 // or something. This is the mask Windows applies as well.
893 #define WINNET_CLEAN_PTR_MASK ((QWORD)0xFFFFFFFFFFFFFFFCull)
894 
895  INTSTATUS status = INT_STATUS_SUCCESS;
896  QWORD slist = Gva & WINNET_CLEAN_PTR_MASK;
897  QWORD slistOld = 0;
898  size_t iters = 0;
899 
900  while (IS_KERNEL_POINTER_WIN(gGuest.Guest64, slist) && slist != slistOld && iters++ < WINNET_LIST_ITERS_CAP)
901  {
902  INTRONET_ENDPOINT net = { .LocalPort = Port };
903 
904  status = IntWinNetGetTcpListener(slist, &net);
905  if (INT_SUCCESS(status))
906  {
907  // We ignore errors here because we might have "dummy" endpoints active on this port.
908  // We might want to look into those because we might not actually need to iterate the
909  // TCP Partition if we can get to the "real" endpoints from here.
910 
911  Callback(&net, Context);
912  }
913 
914  slistOld = slist;
915  status = IntKernVirtMemFetchWordSize(slistOld, &slist);
916  if (!INT_SUCCESS(status))
917  {
918  return status;
919  }
920 
921  slist &= WINNET_CLEAN_PTR_MASK;
922  }
923 
924  return status;
925 
926 #undef WINNET_CLEAN_PTR_MASK
927 }
928 
929 
930 static INTSTATUS
932  _In_ PFUNC_IntWinNetCallback Callback,
933  _Inout_opt_ void *Context
934  )
955 {
956  INTSTATUS status;
957  DWORD sz = 0x10000;
958  BITMASK *bts;
959  size_t pasz, offListener, offPa;
960 
961  offListener = gGuest.WordSize;
962 
963  if (gGuest.OSVersion >= 19041)
964  {
965  pasz = gGuest.Guest64 ? 4ull * sizeof(QWORD) : 6ull * sizeof(DWORD);
966  offPa = gGuest.Guest64 ? 3 * sizeof(QWORD) : 4 * sizeof(DWORD);
967  offListener = 2 * sizeof(QWORD);
968  }
969  else if (gGuest.OSVersion >= 9600)
970  {
971  pasz = 3ull * gGuest.WordSize;
972  offPa = gGuest.Guest64 ? 3 * sizeof(QWORD) : 4 * sizeof(DWORD);
973  }
974  else
975  {
976  pasz = 2ull * gGuest.WordSize;
977  offPa = gGuest.Guest64 ? 4 * sizeof(QWORD) : 5 * sizeof(DWORD);
978  }
979 
980  bts = BitMaskAlloc(sz);
981  if (NULL == bts)
982  {
983  ERROR("[ERROR] BitMaskAlloc failed\n");
985  }
986 
987 
988  status = IntKernVirtMemRead(gWinNet.Bitmap.Buffer, sz / 8, bts->Bits, NULL);
989  if (!INT_SUCCESS(status))
990  {
991  ERROR("[ERROR] IntKernVirtMemRead failed to read 0x%x bytes from 0x%016llx: 0x%08x\n",
992  sz / 8, gWinNet.Bitmap.Buffer, status);
993  goto _exit;
994  }
995 
996  for (DWORD i = 1; i < sz; i++)
997  {
998 #define TCP_PORT_ASSIGNMENT_ARRAY_INDEX(i) (((i) >> 8) & 0xFF)
999 #define TCP_PORT_ASSIGNMENT_INDEX(i) ((i) & 0xFF)
1000 
1001  QWORD addr = 0;
1002  QWORD arr = 0;
1003  QWORD pa = 0;
1004  QWORD listener = 0;
1005 
1006  if (!BitMaskTest(bts, i))
1007  {
1008  continue;
1009  }
1010 
1012  status = IntKernVirtMemFetchWordSize(addr, &arr);
1013  if (!INT_SUCCESS(status))
1014  {
1015  continue;
1016  }
1017 
1018  arr += offPa;
1019  status = IntKernVirtMemFetchWordSize(arr, &pa);
1020  if (!INT_SUCCESS(status))
1021  {
1022  continue;
1023  }
1024 
1025  pa += pasz * TCP_PORT_ASSIGNMENT_INDEX(i) + offListener;
1026  status = IntKernVirtMemFetchWordSize(pa, &listener);
1027  if (!INT_SUCCESS(status))
1028  {
1029  continue;
1030  }
1031 
1032  status = IntWinNetIterateSlinkedList((INTRONET_PORT)i, listener, Callback, Context);
1033  if (!INT_SUCCESS(status))
1034  {
1035  WARNING("[WARNING] IntWinNetIterateSlinkedList failed: 0x%08x\n", status);
1036  }
1037 
1038 #undef TCP_PORT_ASSIGNMENT_INDEX
1039 #undef TCP_PORT_ASSIGNMENT_ARRAY_INDEX
1040  }
1041 
1042 _exit:
1043  BitMaskFree(&bts);
1044 
1045  return INT_STATUS_SUCCESS;
1046 }
1047 
1048 
1049 static INTSTATUS
1051  _In_reads_bytes_(Size) const BYTE *Buffer,
1052  _In_ size_t Size
1053  )
1068 {
1069  QWORD page;
1070  size_t sz;
1071  INTSTATUS status;
1072 
1073  // Describe the fist 3 elements of a tcpip!_PARTITION undocumented structure.
1074  // Each of those should pe pointers to some nt!_RTL_DYNAMIC_HASH_TABLE that
1075  // reside in the same page, with the THcT pool tag.
1076  const union _PARTITION
1077  {
1078  DWORD Part32[3];
1079  QWORD Part64[3];
1080  } *part = (const union _PARTITION *)Buffer;
1081 
1082  sz = gGuest.Guest64 ? sizeof(part->Part64) : sizeof(part->Part32);
1083 
1084  if (sz > Size)
1085  {
1087  }
1088 
1089  page = (gGuest.Guest64 ? part->Part64[0] : part->Part32[0]) & PAGE_MASK;
1090  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, page))
1091  {
1093  }
1094 
1095  for (size_t i = 0; i < ARRAYSIZE(part->Part64); i++)
1096  {
1097  QWORD p = gGuest.Guest64 ? part->Part64[i] : part->Part32[i];
1098  DWORD ptag;
1099 
1100  if (i == 0)
1101  {
1102  if ((p - WIN_POOL_HEADER_SIZE) < page)
1103  {
1105  }
1106 
1107  status = IntKernVirtMemFetchDword(p - WIN_POOL_HEADER_SIZE + 4, &ptag);
1108  if (!INT_SUCCESS(status))
1109  {
1110  return status;
1111  }
1112 
1113  if (ptag != POOL_TAG_TCHT)
1114  {
1116  }
1117  }
1118  else
1119  {
1120  if ((p & PAGE_MASK) != page)
1121  {
1123  }
1124  }
1125 
1126  }
1127 
1128  return INT_STATUS_SUCCESS;
1129 }
1130 
1131 
1132 static INTSTATUS
1134  _In_ QWORD PartitionAddress,
1135  _In_ QWORD PartitionPointer
1136  )
1164 {
1165  BYTE *map;
1166  QWORD addr;
1167  QWORD cnt;
1168  DWORD sz;
1169  DWORD off;
1170  INTSTATUS status;
1171 
1172  if (gWinNet.Partition.Found)
1173  {
1175  }
1176 
1177  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, PartitionAddress))
1178  {
1180  }
1181 
1182  off = PartitionAddress & PAGE_OFFSET;
1183  if (ROUND_DOWN(off, gGuest.WordSize) != off)
1184  {
1186  }
1187 
1188  status = IntVirtMemMap(PartitionAddress & PAGE_MASK, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1189  if (!INT_SUCCESS(status))
1190  {
1191  return status;
1192  }
1193 
1194  // Check for a valid tcpip!_PARTITION.
1195  status = IntWinNetCheckPartition(map + off, PAGE_SIZE - off);
1196  if (!INT_SUCCESS(status))
1197  {
1198  goto _exit;
1199  }
1200 
1201  // Read the tcpip!PartitionCount value.
1202  addr = PartitionPointer;
1203 
1204  if (gGuest.OSVersion >= 19041)
1205  {
1206  sz = gGuest.WordSize;
1207  addr -= gGuest.WordSize;
1208  }
1209  else if (gGuest.OSVersion >= 9600)
1210  {
1211  sz = sizeof(DWORD);
1212  addr -= sizeof(DWORD);
1213  }
1214  else if (gGuest.OSVersion >= 9200)
1215  {
1216  sz = gGuest.WordSize;
1217  addr -= gGuest.WordSize;
1218  }
1219  else
1220  {
1221  sz = 2;
1222  addr += gGuest.WordSize + 2ull;
1223  }
1224 
1225  cnt = 0;
1226  status = IntKernVirtMemRead(addr, sz, &cnt, NULL);
1227  if (!INT_SUCCESS(status))
1228  {
1229  goto _exit;
1230  }
1231 
1232  if (cnt > 1)
1233  {
1234  // Search for the next partition to determine the size.
1235  status = INT_STATUS_NOT_FOUND;
1236  for (DWORD cur = off + 8 * gGuest.WordSize; cur < PAGE_SIZE; cur += gGuest.WordSize)
1237  {
1238  status = IntWinNetCheckPartition(map + cur, PAGE_SIZE - cur);
1239  if (INT_SUCCESS(status))
1240  {
1241  sz = cur - off;
1242  break;
1243  }
1244  }
1245 
1246  if (!INT_SUCCESS(status))
1247  {
1248  goto _exit;
1249  }
1250  }
1251  else
1252  {
1253  // We don't realy care for it's size if there's only one of them but set it just in case.
1254  sz = PAGE_SIZE - off;
1255  }
1256 
1257  IntDumpGvaEx(PartitionPointer - 8, 0x20, gGuest.Mm.SystemCr3, 16, 1, FALSE, TRUE);
1258 
1259  gWinNet.Partition.Table = PartitionAddress;
1260  gWinNet.Partition.Size = sz;
1261  gWinNet.Partition.Count = (DWORD)cnt;
1262 
1263  TRACE("[WINNET] Found tcpip!ParitionTable @ 0x%016llx pointing to"
1264  " 0x%016llx with %u elements of size 0x%x from 0x%016llx\n",
1265  PartitionAddress, gWinNet.Partition.Table, gWinNet.Partition.Count,
1266  gWinNet.Partition.Size, PartitionPointer);
1267 
1269 
1270  status = INT_STATUS_SUCCESS;
1271 
1272 _exit:
1273  IntVirtMemUnmap(&map);
1274 
1275  return status;
1276 }
1277 
1278 
1279 static INTSTATUS
1281  _In_ QWORD Gva,
1282  _In_ DWORD Tag,
1283  _Out_ QWORD *Alloc
1284  )
1298 {
1299  BYTE *map;
1300  QWORD aligned;
1301  DWORD off;
1302  INTSTATUS status;
1303 
1304  aligned = Gva & PAGE_MASK;
1305  off = ROUND_UP(Gva & PAGE_OFFSET, gGuest.WordSize);
1306 
1307  status = IntVirtMemMap(aligned, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1308  if (!INT_SUCCESS(status))
1309  {
1310  return status;
1311  }
1312 
1313  while (off < PAGE_SIZE)
1314  {
1315  QWORD addr = gGuest.Guest64 ? *(QWORD *)((size_t)map + off) :
1316  *(DWORD *)((size_t)map + off);
1317  DWORD tag;
1318 
1319  status = IntKernVirtMemFetchDword(addr - WIN_POOL_HEADER_SIZE + 4, &tag);
1320  if (INT_SUCCESS(status) && tag == Tag)
1321  {
1322  *Alloc = addr;
1323  goto _exit;
1324  }
1325 
1326  off += gGuest.WordSize;
1327  }
1328 
1329  status = INT_STATUS_NOT_FOUND;
1330 
1331 _exit:
1332  IntVirtMemUnmap(&map);
1333 
1334  return status;
1335 }
1336 
1337 
1338 static INTSTATUS
1340  _In_ QWORD Gva,
1341  _Out_ QWORD *PortPool
1342  )
1365 {
1366  QWORD inetCmp, tcpCmp, pool;
1367  DWORD tag;
1368  INTSTATUS status;
1369 
1370  if (ROUND_DOWN(Gva, WIN_POOL_HEADER_SIZE) != Gva)
1371  {
1373  }
1374 
1375  status = IntKernVirtMemFetchDword(Gva - WIN_POOL_HEADER_SIZE + 4, &tag);
1376  if (!INT_SUCCESS(status) || POOL_TAG_INCS != tag)
1377  {
1379  }
1380 
1381  status = IntWinNetSearchForAlloc(Gva, POOL_TAG_INCO, &inetCmp);
1382  if (!INT_SUCCESS(status))
1383  {
1384  return status;
1385  }
1386 
1387  status = IntWinNetSearchForAlloc(inetCmp, POOL_TAG_TCCO, &tcpCmp);
1388  if (!INT_SUCCESS(status))
1389  {
1390  return status;
1391  }
1392 
1393  pool = 0;
1394  status = IntKernVirtMemFetchWordSize(tcpCmp, &pool);
1395  if (!INT_SUCCESS(status))
1396  {
1397  return status;
1398  }
1399 
1400  if ((pool & PAGE_OFFSET) != 0)
1401  {
1403  }
1404 
1405  *PortPool = pool;
1406 
1407  return INT_STATUS_SUCCESS;
1408 }
1409 
1410 
1411 
1412 static INTSTATUS
1414  _In_ QWORD Gva,
1415  _Out_ QWORD *PortPool
1416  )
1437 {
1438  QWORD addr;
1439  INTSTATUS status;
1440 
1441  union
1442  {
1443  DWORD Ptr32[2];
1444  QWORD Ptr64[2];
1445  } ptrs;
1446 
1447  if ((Gva & PAGE_OFFSET) != 0)
1448  {
1450  }
1451 
1452  status = IntKernVirtMemRead(Gva, 2 * gGuest.WordSize, &ptrs, NULL);
1453  if (!INT_SUCCESS(status))
1454  {
1455  return status;
1456  }
1457 
1458  addr = gGuest.Guest64 ? ptrs.Ptr64[0] : ptrs.Ptr32[0];
1459  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, addr) &&
1460  (addr & PAGE_OFFSET) == 0)
1461  {
1462  *PortPool = Gva;
1463  return INT_STATUS_SUCCESS;
1464  }
1465 
1466  addr = gGuest.Guest64 ? ptrs.Ptr64[1] : ptrs.Ptr32[1];
1467  if (IS_KERNEL_POINTER_WIN(gGuest.Guest64, addr) &&
1468  (addr & PAGE_OFFSET) == 0)
1469  {
1470  *PortPool = addr;
1471  return INT_STATUS_SUCCESS;
1472  }
1473 
1475 }
1476 
1477 
1478 static INTSTATUS
1480  _In_ QWORD Gva,
1481  _In_ QWORD Addr
1482  )
1507 {
1508  void *map;
1509  QWORD pool, sz, buf, pa;
1510  size_t i;
1511  DWORD tag;
1512  INTSTATUS status;
1513 
1514  if (gWinNet.Bitmap.Found)
1515  {
1517  }
1518 
1519  if (gGuest.OSVersion > 10586)
1520  {
1521  status = IntWinNetGetTcpPortPoolFromCompartment(Gva, &pool);
1522  }
1523  else
1524  {
1525  status = IntWinNetGetTcpPortPool(Gva, &pool);
1526  }
1527  if (!INT_SUCCESS(status))
1528  {
1529  return status;
1530  }
1531 
1532  status = IntVirtMemMap(pool, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1533  if (!INT_SUCCESS(status))
1534  {
1535  return status;
1536  }
1537 
1538  buf = pa = 0;
1539  status = INT_STATUS_NOT_FOUND;
1540  for (i = 0; i < PAGE_SIZE - 2ull * gGuest.WordSize; i += gGuest.WordSize)
1541  {
1542  size_t p = (size_t)map + i;
1543  sz = gGuest.Guest64 ? ((QWORD *)p)[0] : ((DWORD *)p)[0];
1544  buf = gGuest.Guest64 ? ((QWORD *)p)[1] : ((DWORD *)p)[1];
1545  pa = gGuest.Guest64 ? ((QWORD *)p)[2] : ((DWORD *)p)[2];
1546 
1547  sz &= 0xFFFFFFFF;
1548 
1549  if (0x10000 == sz && IS_KERNEL_POINTER_WIN(gGuest.Guest64, buf) &&
1550  ((buf & PAGE_MASK) == (pool & PAGE_MASK)))
1551  {
1552  status = INT_STATUS_SUCCESS;
1553  break;
1554  }
1555  }
1556 
1557  if (!INT_SUCCESS(status))
1558  {
1559  goto _exit;
1560  }
1561 
1562  status = IntKernVirtMemFetchDword(pa - WIN_POOL_HEADER_SIZE + 4, &tag);
1563  if (!INT_SUCCESS(status) || POOL_TAG_INPA != tag)
1564  {
1566  goto _exit;
1567  }
1568 
1569  gWinNet.Bitmap.PortAssignment = pool + i + 2ull * gGuest.WordSize;
1570  gWinNet.Bitmap.Buffer = buf;
1571 
1572  TRACE("[WINNET] Found tcpip!TcpPortPool @ 0x%016llx, BitmapBuffer @ 0x%016llx, "
1573  "and port assignment array @ 0x%016llx from 0x%016llx:0x%016llx\n", pool,
1575 
1577 
1578  status = INT_STATUS_SUCCESS;
1579 
1580 _exit:
1581  IntVirtMemUnmap(&map);
1582 
1583  return status;
1584 }
1585 
1586 
1587 static INTSTATUS
1589  void
1590  )
1601 {
1603  PKERNEL_DRIVER drv;
1604  DWORD objs;
1605  INTSTATUS status;
1606 
1608  {
1610  }
1611 
1612  drv = IntDriverFindByName(u"tcpip.sys");
1613  if (NULL == drv)
1614  {
1615  ERROR("[ERROR] Failed to find `tcpip.sys`\n");
1616  return INT_STATUS_NOT_FOUND;
1617  }
1618 
1619  TRACE("[WINNET] tcpip.sys @ 0x%016llx\n", drv->BaseVa);
1620 
1621  status = IntPeGetSectionHeaderByName(drv->BaseVa, NULL, ".data", gGuest.Mm.SystemCr3, &sec);
1622  if (!INT_SUCCESS(status))
1623  {
1624  ERROR("[ERROR] Failed to find `.data` section in `tcpip.sys`: 0x%08x\n", status);
1625  return status;
1626  }
1627 
1628  objs = 0;
1629  for (size_t page = 0; page < ROUND_UP((size_t)sec.Misc.VirtualSize, PAGE_SIZE); page += PAGE_SIZE)
1630  {
1631  QWORD gva = drv->BaseVa + sec.VirtualAddress + page;
1632  void *map;
1633 
1634  status = IntVirtMemMap(gva, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &map);
1635  if (!INT_SUCCESS(status))
1636  {
1637  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", gva, status);
1638  return status;
1639  }
1640 
1641  for (size_t i = 0; objs < 2 && i < PAGE_SIZE / gGuest.WordSize; i++)
1642  {
1643  QWORD ptr = gGuest.Guest64 ? ((QWORD *)map)[i] : ((DWORD *)map)[i];
1644 
1645  status = IntWinNetFindTcpPartition(ptr, gva + i * gGuest.WordSize);
1646  if (INT_SUCCESS(status))
1647  {
1648  objs++;
1649  continue;
1650  }
1651 
1652  status = IntWinNetFindTcpBitmap(ptr, gva + i * gGuest.WordSize);
1653  if (INT_SUCCESS(status))
1654  {
1655  objs++;
1656  continue;
1657  }
1658  }
1659 
1660  IntVirtMemUnmap(&map);
1661 
1662  if (objs == 2)
1663  {
1664  status = INT_STATUS_SUCCESS;
1665  break;
1666  }
1667  }
1668 
1669  return status;
1670 }
1671 
1672 
1673 static INTSTATUS
1675  _In_ PFUNC_IntWinNetCallback Callback,
1676  _Inout_opt_ void *Context
1677  )
1686 {
1687  INTSTATUS status;
1688 
1689  status = IntWinNetFindTcpObjects();
1690  if (!INT_SUCCESS(status))
1691  {
1692  ERROR("[ERROR] IntWinNetFindTcpObjects failed ( 0x%016llx, 0x%016llx): 0x%08x\n",
1694  return status;
1695  }
1696 
1697  status = IntWinNetParseTcpPartition(Callback, Context);
1698  if (!INT_SUCCESS(status))
1699  {
1700  WARNING("[WARNING] IntWinNetParseTcpPartition failed: 0x%08x\n", status);
1701  }
1702 
1703  status = IntWinNetParseTcpBitmap(Callback, Context);
1704  if (!INT_SUCCESS(status))
1705  {
1706  WARNING("[WARNING] IntWinNetParseTcpBitmap failed: 0x%08x\n", status);
1707  }
1708 
1709  return status;
1710 }
1711 
1712 
1715  _In_ const INTRONET_ENDPOINT *Endpoint,
1716  _Inout_opt_ void *Context
1717  )
1728 {
1729  INTSTATUS status;
1731  const WIN_PROCESS_OBJECT *proc = (WIN_PROCESS_OBJECT *)Context;
1732 
1733  if (proc != Endpoint->OwnerProcess)
1734  {
1736  }
1737 
1738  IntAlertFillConnection(Endpoint, evt);
1739 
1740  status = IntNotifyIntroEvent(introEventConnectionEvent, evt, sizeof(*evt));
1741  if (!INT_SUCCESS(status))
1742  {
1743  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
1744  }
1745 
1746  return status;
1747 }
1748 
1749 
1750 INTSTATUS
1752  _In_ WIN_PROCESS_OBJECT *Process
1753  )
1762 {
1764  {
1766  }
1767 
1769 }
1770 
1771 
1774  _In_ const INTRONET_ENDPOINT *Endpoint,
1775  _Inout_opt_ void *Context
1776  )
1785 {
1786  char local[INTRONET_MIN_BUFFER_SIZE] = { 0 };
1787  char remote[INTRONET_MIN_BUFFER_SIZE] = { 0 };
1788 
1789  UNREFERENCED_PARAMETER(Context);
1790 
1791  IntNetAddrToStr(Endpoint->AddressFamily, &Endpoint->LocalAddress, local);
1792  IntNetAddrToStr(Endpoint->AddressFamily, &Endpoint->RemoteAddress, remote);
1793 
1794  LOG("[WINNET] Endpoint @ 0x%016llx Local %s:%d Remote %s:%d State %s Owner 0x%016llx (%s:%u)\n",
1795  Endpoint->Endpoint, local, Endpoint->LocalPort, remote, Endpoint->RemotePort,
1796  IntNetStateToString(Endpoint->State), Endpoint->Endpoint,
1797  Endpoint->OwnerProcess->Name, Endpoint->OwnerProcess->Pid);
1798 
1799  return INT_STATUS_SUCCESS;
1800 }
1801 
1802 
1803 //
1804 // IntWinNetDumpConnections
1805 //
1806 INTSTATUS
1808  void
1809  )
1815 {
1817 }
1818 
This is the structure as documented in ntddk.h.
Definition: wddefs.h:630
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:1751
static INTSTATUS IntWinNetGetTcpPortPool(QWORD Gva, QWORD *PortPool)
Get the address of a tcp port pool for windows versions before RS1.
Definition: winnet.c:1413
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:1803
#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:663
EVENT_CONNECTION_EVENT Connection
Definition: alerts.h:30
DWORD PoolTag
Definition: wddefs.h:420
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:207
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTSTATUS IntWinNetDumpConnections(void)
Dump all active guest connections.
Definition: winnet.c:1807
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
union _ADDRINFO::@192 Addr32
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:33
#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:196
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:52
#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:781
INTRO_NET_AF AddressFamily
Address family.
Definition: intronet.h:29
#define AF_INET
IPv4.
Definition: wddefs.h:1768
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
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define POOL_TAG_TCHT
Tcp Hash Table.
Definition: wddefs.h:1675
BYTE BitMaskTest(BITMASK *BitMask, DWORD BitPos)
Tests a bit in a BITMASK.
Definition: bitmask.c:151
DWORD OSVersion
Os version.
Definition: guests.h:277
Describe information about the TCP partition containing endpoints.
Definition: winnet.c:31
static INTSTATUS IntWinNetGetLocalAddr(QWORD Gva, INTRONET_ENDPOINT *Endpoint)
Get the local address of a connection from a given address.
Definition: winnet.c:156
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
#define AF_INET6
IPv6.
Definition: wddefs.h:1769
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:17
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:605
#define POOL_TAG_INCO
Inet Compartment.
Definition: wddefs.h:1669
#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:1775
#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:1588
#define INTRO_OPT_EVENT_CONNECTIONS
Enable connection events.
Definition: intro_types.h:466
static INTSTATUS IntWinNetDumpConnection(const INTRONET_ENDPOINT *Endpoint, void *Context)
Log a connection.
Definition: winnet.c:1773
#define POOL_TAG_INCS
Inet Compartment Set.
Definition: wddefs.h:1671
static INTSTATUS IntWinNetSendConnectionEvent(const INTRONET_ENDPOINT *Endpoint, void *Context)
Send a connection event.
Definition: winnet.c:1714
#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:23
#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:34
static INTSTATUS IntWinNetCheckPartition(const BYTE *Buffer, size_t Size)
Check wether a buffer contains a valid undocumented tcpip!_PARTITION object.
Definition: winnet.c:1050
DWORD Size
Will hold the size of a partition element.
Definition: winnet.c:35
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
struct _ADDRINFO::@192::@194 Win8AndAbove
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:355
static INTSTATUS IntWinNetSearchForAlloc(QWORD Gva, DWORD Tag, QWORD *Alloc)
Search for an allocation with given tag.
Definition: winnet.c:1280
POOL_HEADER64 Header64
Definition: wddefs.h:463
Structure that describe the kernel objects needed by introcore to extract connections.
Definition: winnet.c:28
DWORD BlockSize
Definition: wddefs.h:411
This is the structure as documented in ntddk.h.
Definition: wddefs.h:655
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:76
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:286
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:232
An endpoint.
Definition: intronet.h:26
Describe information about the TCP port pool containing listeners.
Definition: winnet.c:41
union _IMAGE_SECTION_HEADER::@209 Misc
BOOLEAN Found
TRUE if we managed to find the elements above.
Definition: winnet.c:37
#define POOL_TAG_TCCO
Tcp Compartment.
Definition: wddefs.h:1674
static INTSTATUS IntWinNetGetTcpPortPoolFromCompartment(QWORD Gva, QWORD *PortPool)
Get the address of a tcp port pool for windows RS1 and newer.
Definition: winnet.c:1339
#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:276
struct _ADDRINFO::@192::@193 Win7
static INTSTATUS IntWinNetFindTcpBitmap(QWORD Gva, QWORD Addr)
Get tcpip!TcpPortPool information form the guest and initialize gWinNet.
Definition: winnet.c:1479
#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:402
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
#define VALID_PORT(Port)
Check if a port is valid. Not the best check, but a valid check.
Definition: winnet.c:20
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:873
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:931
#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
struct _LOCAL_ADDRESS::@196 Addr64
#define PAGE_SIZE
Definition: common.h:53
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
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:180
static INTSTATUS IntWinNetIterateConnections(PFUNC_IntWinNetCallback Callback, void *Context)
Iterate through guest connections and invoke the callback on each one.
Definition: winnet.c:1674
#define POOL_TAG_TCPE
Tcp Endpoint.
Definition: wddefs.h:1678
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:1679
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:370
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
#define _Function_class_(expr)
Definition: intro_sal.h:40
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
struct _LOCAL_ADDRESS::@195 Addr32
PWIN_PROCESS_OBJECT IntWinProcFindObjectByEprocess(QWORD Eprocess)
Finds a process by the address of its _EPROCESS structure.
Definition: winprocesshp.c:23
TIMER_FRIENDLY void IntDumpBuffer(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
QWORD PortAssignment
Will hold the address of the array of inet port assignment arrays in the port pool.
Definition: winnet.c:43
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
#define POOL_TAG_INNL
Used to search for address family.
Definition: wddefs.h:1672
#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:1843
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:1328
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:1670
QWORD Buffer
Will hold the address of the bitmap buffer in the port pool.
Definition: winnet.c:44
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:46
#define PAGE_MASK
Definition: pgtable.h:35
struct _ADDRINFO::@191 Addr64
#define TCP_DIRECTORY_ENTRY_CNT
QWORD Remote
Definition: wddefs.h:1777
#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:267
INTRO_NET_STATE IntNetConvertState(const DWORD State)
Converts a guest connection state to an Introcore connection state.
Definition: intronet.c:168
BYTE Ipv4[4]
IPv4 address.
Definition: intronet.h:19
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:493
#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:1133
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:715
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:81
#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