Bitdefender Hypervisor Memory Introspection
lixnet.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "lixnet.h"
6 #include "alerts.h"
7 #include "guests.h"
8 #include "intronet.h"
9 #include "introstatus.h"
10 
11 #define LIX_FDTABLE_MAX_FDS_CAP 2048u
12 
13 typedef struct _SOCK_PROTO
17 {
19  CHAR Name[32];
20 } SOCK_PROTO;
21 
22 
23 static void
25  _In_ INTRONET_ENDPOINT *Connection
26  )
32 {
33  INTSTATUS status;
34  PEVENT_CONNECTION_EVENT pConnectionEvent;
35 
36  pConnectionEvent = &gAlert.Connection;
37 
38  IntAlertFillConnection(Connection, pConnectionEvent);
39 
40  status = IntNotifyIntroEvent(introEventConnectionEvent, pConnectionEvent, sizeof(*pConnectionEvent));
41  if (!INT_SUCCESS(status))
42  {
43  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
44  }
45 }
46 
47 
49 static INTSTATUS
51  _In_ QWORD SocketGva,
52  _Out_ INTRONET_ENDPOINT *Connection
53  )
69 {
70  INTSTATUS status;
71 
72  QWORD sock, proto;
73  CHAR protoName[32];
74  DWORD sockState = 0;
75 
76  DWORD iProto;
77 
78  static SOCK_PROTO protos[] =
79  {
80  {
81  .Name = "TCP"
82  },
83  {
84  .Name = "TCPv6"
85  },
86  };
87 
88  status = IntKernVirtMemFetchQword(SocketGva + LIX_FIELD(Socket, Sk), &sock);
89  if (!INT_SUCCESS(status))
90  {
91  ERROR("[ERROR] IntKernVirtMemFetchQword failed for GVA 0x%016llx: 0x%08x\n",
92  SocketGva + LIX_FIELD(Socket, Sk), status);
93  return status;
94  }
95 
96  status = IntKernVirtMemFetchQword(sock + LIX_FIELD(Sock, Proto), &proto);
97  if (!INT_SUCCESS(status))
98  {
99  ERROR("[ERROR] IntKernVirtMemFetchQword failed for GVA 0x%016llx: 0x%08x\n",
100  sock + LIX_FIELD(Sock, Proto), status);
101  return status;
102  }
103 
104  if (!IS_KERNEL_POINTER_LIX(proto))
105  {
106  WARNING("[WARNING] Sock 0x%016llx has NULL proto pointer.", sock);
108  }
109 
110  for (iProto = 0; iProto < ARRAYSIZE(protos); iProto++)
111  {
112  if (proto == protos[iProto].Gva)
113  {
114  break;
115  }
116  }
117 
118  if (iProto == ARRAYSIZE(protos))
119  {
120  status = IntKernVirtMemRead(proto + LIX_FIELD(Ungrouped, ProtoName), sizeof(protoName), protoName, NULL);
121  if (!INT_SUCCESS(status))
122  {
123  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
124  proto + LIX_FIELD(Ungrouped, ProtoName), status);
125  return INT_STATUS_SUCCESS;
126  }
127 
128  for (iProto = 0; iProto < ARRAYSIZE(protos); iProto++)
129  {
130  if (!strcmp(protos[iProto].Name, protoName))
131  {
132  protos[iProto].Gva = proto;
133  break;
134  }
135  }
136 
137  if (iProto == ARRAYSIZE(protos))
138  {
140  }
141  }
142 
143  status = IntKernVirtMemRead(sock + LIX_FIELD(Sock, State), 1, &sockState, NULL);
144  if (!INT_SUCCESS(status))
145  {
146  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
147  sock + LIX_FIELD(Sock, State), status);
148  return status;
149  }
150 
151  Connection->State = IntNetConvertState(sockState);
152  Connection->AddressFamily = (iProto == 0 ? introNetAfIpv4 : introNetAfIpv6);
153  Connection->Endpoint = sock;
154 
155  memset(&Connection->LocalAddress, 0, sizeof(Connection->LocalAddress));
156  memset(&Connection->RemoteAddress, 0, sizeof(Connection->RemoteAddress));
157 
158  if (introNetAfIpv6 == Connection->AddressFamily)
159  {
160  status = IntKernVirtMemRead(sock + LIX_FIELD(Sock, RcvSaddr), 16, &Connection->LocalAddress, NULL);
161  if (!INT_SUCCESS(status))
162  {
163  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
164  sock + LIX_FIELD(Sock, V6RcvSaddr), status);
165  return status;
166  }
167 
168  status = IntKernVirtMemRead(sock + LIX_FIELD(Sock, V6Daddr), 16, &Connection->RemoteAddress, NULL);
169  if (!INT_SUCCESS(status))
170  {
171  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
172  sock + LIX_FIELD(Sock, V6RcvSaddr), status);
173  return status;
174  }
175  }
176  else
177  {
178  status = IntKernVirtMemRead(sock + LIX_FIELD(Sock, RcvSaddr), 4, &Connection->LocalAddress, NULL);
179  if (!INT_SUCCESS(status))
180  {
181  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
182  sock + LIX_FIELD(Sock, RcvSaddr), status);
183  return status;
184  }
185 
186  status = IntKernVirtMemRead(sock + LIX_FIELD(Sock, Daddr), 4, &Connection->RemoteAddress, NULL);
187  if (!INT_SUCCESS(status))
188  {
189  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
190  sock + LIX_FIELD(Sock, RcvSaddr), status);
191  return status;
192  }
193  }
194 
195  status = IntKernVirtMemRead(sock + LIX_FIELD(Sock, Num), 2, &Connection->LocalPort, NULL);
196  if (!INT_SUCCESS(status))
197  {
198  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
199  sock + LIX_FIELD(Sock, Num), status);
200  return status;
201  }
202 
203  status = IntKernVirtMemRead(sock + LIX_FIELD(Sock, Dport), 2, &Connection->RemotePort, NULL);
204  if (!INT_SUCCESS(status))
205  {
206  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
207  sock + LIX_FIELD(Sock, Dport), status);
208  return status;
209  }
210 
211  return INT_STATUS_SUCCESS;
212 }
213 
214 
215 static BOOLEAN
217  _In_ QWORD StructFileGva,
218  _Out_ QWORD *SocketGva
219  )
230 {
231  INTSTATUS status;
232  QWORD dentry, inode;
233  UINT16 imode;
234 
235  *SocketGva = 0;
236 
237  status = IntKernVirtMemFetchQword(StructFileGva + LIX_FIELD(Ungrouped, FileDentry), &dentry);
238  if (!INT_SUCCESS(status))
239  {
240  ERROR("[ERROR] IntKernVirtMemFetchQword failed for GVA 0x%016llx: 0x%08x\n",
241  StructFileGva + LIX_FIELD(Ungrouped, FileDentry), status);
242  return FALSE;
243  }
244 
245  status = IntKernVirtMemFetchQword(dentry + LIX_FIELD(Dentry, Inode), &inode);
246  if (!INT_SUCCESS(status))
247  {
248  ERROR("[ERROR] IntKernVirtMemFetchQword failed for GVA 0x%016llx: 0x%08x\n",
249  dentry + LIX_FIELD(Dentry, Inode), status);
250  return FALSE;
251  }
252 
253  status = IntKernVirtMemRead(inode + LIX_FIELD(Inode, Imode), sizeof(imode), &imode, NULL);
254  if (!INT_SUCCESS(status))
255  {
256  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx: 0x%08x\n",
257  dentry + LIX_FIELD(Dentry, Inode), status);
258  return FALSE;
259  }
260 
261  if (!S_ISSOCK(imode))
262  {
263  return FALSE;
264  }
265 
266  *SocketGva = inode - LIX_FIELD(Ungrouped, SocketAllocVfsInode);
267  return TRUE;
268 
269 }
270 
271 
272 INTSTATUS
274  _In_ LIX_TASK_OBJECT *Task,
276  )
291 {
292  INTSTATUS status;
293 
294  INTRONET_ENDPOINT conn;
295 
296  QWORD files, fdt, fd;
297  DWORD maxFds;
298 
300  {
301  BUG_ON(TRUE);
302 
304  }
305 
307  {
309  }
310 
311  if (NULL == Task)
312  {
314  }
315 
316  if (NULL == Callback)
317  {
319  }
320 
321  status = IntKernVirtMemFetchQword(Task->Gva + LIX_FIELD(TaskStruct, Files), &files);
322  if (!INT_SUCCESS(status))
323  {
324  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: 0x%08x\n",
325  Task->Gva + LIX_FIELD(TaskStruct, Files), status);
326  return status;
327  }
328 
329  status = IntKernVirtMemFetchQword(files + LIX_FIELD(Files, Fdt), &fdt);
330  if (!INT_SUCCESS(status))
331  {
332  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: 0x%08x\n", files + LIX_FIELD(Files, Fdt), status);
333  return status;
334  }
335 
336  status = IntKernVirtMemFetchDword(fdt + LIX_FIELD(FdTable, MaxFds), &maxFds);
337  if (!INT_SUCCESS(status))
338  {
339  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: 0x%08x\n", fdt + LIX_FIELD(FdTable, MaxFds), status);
340  return status;
341  }
342 
343  status = IntKernVirtMemFetchQword(fdt + LIX_FIELD(FdTable, Fd), &fd);
344  if (!INT_SUCCESS(status))
345  {
346  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: 0x%08x\n", files + LIX_FIELD(FdTable, Fd), status);
347  return status;
348  }
349 
350  conn.OwnerTask = Task;
351 
352  maxFds = MIN(maxFds, LIX_FDTABLE_MAX_FDS_CAP);
353  for (DWORD iFd = 0; iFd < maxFds; iFd++)
354  {
355  QWORD file;
356  QWORD socketGva;
357 
358  status = IntKernVirtMemFetchQword(fd + iFd * sizeof(QWORD), &file);
359  if (!INT_SUCCESS(status))
360  {
361  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: 0x%08x\n", fd + iFd * 8ull, status);
362  return status;
363  }
364 
365  if ((!IS_KERNEL_POINTER_LIX(file)) || (!IntLixNetFileIsSocket(file, &socketGva)))
366  {
367  continue;
368  }
369 
370  status = IntLixNetGetConnectionFromSocket(socketGva, &conn);
371  if (!INT_SUCCESS(status))
372  {
373  ERROR("[ERROR] IntLixSocketGetConnection failed for socket %llx : 0x%08x\n", socketGva, status);
374  continue;
375  }
376 
377  if (INT_STATUS_NOT_NEEDED_HINT == status)
378  {
379  continue;
380  }
381 
382  Callback(&conn);
383  }
384 
385  return INT_STATUS_SUCCESS;
386 }
387 
388 
389 static void
391  INTRONET_ENDPOINT *Endpoint
392  )
400 {
401  CHAR ipString[INTRONET_MIN_BUFFER_SIZE];
402 
404 
405  IntNetAddrToStr(Endpoint->AddressFamily, &Endpoint->RemoteAddress, ipString);
406  TRACE("[CONNECTION] Owner %s | Family: %u | State %s | LocalPort: %hu | RemoteAddress: %s | Endpoint %016llx\n",
407  Endpoint->OwnerTask->Comm, Endpoint->AddressFamily, IntNetStateToString(Endpoint->State),
408  Endpoint->LocalPort, ipString, Endpoint->Endpoint);
409 }
410 
411 
412 INTSTATUS
414  _In_ LIX_TASK_OBJECT *Task
415  )
424 {
425  if (NULL == Task)
426  {
428  }
429 
431 }
432 
433 
434 INTSTATUS
436  void
437  )
446 {
447  INTSTATUS status;
448 
450  {
451  BUG_ON(TRUE);
452 
454  }
455 
457  {
459  }
460 
462  if (!INT_SUCCESS(status))
463  {
464  ERROR("[ERROR] IntLixTaskIterateTasks failed: 0x%08x\n", status);
465  }
466 
467  return status;
468 }
void(* PFUNC_IterateConnectionsCallback)(INTRONET_ENDPOINT *Endpoint)
Definition: lixnet.h:13
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
EVENT_CONNECTION_EVENT Connection
Definition: alerts.h:30
#define _In_
Definition: intro_sal.h:21
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTRONET_PORT LocalPort
Local port.
Definition: intronet.h:37
An internal structure used to cache the "struct proto" addresses of required connection types...
Definition: lixnet.c:16
CHAR Name[32]
The protocol name as defined in Linux kernel.
Definition: lixnet.c:19
#define _Success_(expr)
Definition: intro_sal.h:47
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define BUG_ON(cond)
Definition: introdefs.h:236
static void IntLixNetProcessConnection(INTRONET_ENDPOINT *Endpoint)
Callback for IntLixNetIterateTaskConnections that processes each TCP/IP connection.
Definition: lixnet.c:390
#define ARRAYSIZE(A)
Definition: introdefs.h:101
struct _SOCK_PROTO SOCK_PROTO
An internal structure used to cache the "struct proto" addresses of required connection types...
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
INTRO_NET_AF AddressFamily
Address family.
Definition: intronet.h:29
int INTSTATUS
The status data type.
Definition: introstatus.h:24
LIX_TASK_OBJECT * OwnerTask
Pointer to the task that owns the connection.
Definition: intronet.h:53
const char * IntNetStateToString(INTRO_NET_STATE State)
Converts a connection state to a string.
Definition: intronet.h:69
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
#define INTRO_OPT_EVENT_CONNECTIONS
Enable connection events.
Definition: intro_types.h:482
static INTSTATUS IntLixNetGetConnectionFromSocket(QWORD SocketGva, INTRONET_ENDPOINT *Connection)
Fills an INTRONET_ENDPOINT structure from a TCP/IP socket GVA.
Definition: lixnet.c:50
#define MIN(a, b)
Definition: introdefs.h:146
static BOOLEAN IntLixNetFileIsSocket(QWORD StructFileGva, QWORD *SocketGva)
Check if a give file object is a socked and return the socket GVA.
Definition: lixnet.c:216
#define INTRONET_MIN_BUFFER_SIZE
The minimum buffer size needed for the textual representation of an IP address.
Definition: intronet.h:12
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
INTSTATUS IntKernVirtMemFetchDword(QWORD GuestVirtualAddress, DWORD *Data)
Reads 4 bytes from the guest kernel memory.
Definition: introcore.c:829
#define IS_KERNEL_POINTER_LIX(p)
Definition: lixguest.h:11
INTSTATUS IntKernVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD *Data)
Reads 8 bytes from the guest kernel memory.
Definition: introcore.c:811
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
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
#define TRUE
Definition: intro_types.h:30
#define LIX_FDTABLE_MAX_FDS_CAP
The maximum number of file descriptors to be iterated.
Definition: lixnet.c:11
#define LIX_FIELD(Structure, Field)
Macro used to access fields inside the LIX_OPAQUE_FIELDS structure.
Definition: lixguest.h:429
#define TRACE(fmt,...)
Definition: glue.h:58
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 WARNING(fmt,...)
Definition: glue.h:60
uint32_t DWORD
Definition: intro_types.h:49
static void IntLixNetSendConnectionEvent(INTRONET_ENDPOINT *Connection)
Sends a connection event to the integrator.
Definition: lixnet.c:24
char Comm[LIX_COMM_SIZE]
The short name of the executable.
Definition: lixprocess.h:44
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
INTSTATUS IntLixNetSendGuestConnections(void)
Sends all active in-guest TCP/IP connections as events to the integrator.
Definition: lixnet.c:435
INTRONET_ADDRESS RemoteAddress
Remote address.
Definition: intronet.h:42
#define S_ISSOCK(m)
Definition: lixddefs.h:253
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
INTSTATUS IntLixNetIterateTaskConnections(LIX_TASK_OBJECT *Task, PFUNC_IterateConnectionsCallback Callback)
Iterates all TCP/IP connections of a process and supplies them to callback.
Definition: lixnet.c:273
uint16_t UINT16
Definition: intro_types.h:38
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
INTSTATUS IntLixTaskIterateTasks(PFUNC_LixTaskIterateTasks Callback)
Call the Callback parameter for each task saved internally.
Definition: lixprocess.c:4892
Event structure for connections.
Definition: intro_types.h:2006
INTSTATUS IntLixNetSendTaskConnections(LIX_TASK_OBJECT *Task)
Logs and sends to the integrator all connections opened by a Linux proces..
Definition: lixnet.c:413
void IntAlertFillConnection(const INTRONET_ENDPOINT *Connection, EVENT_CONNECTION_EVENT *Event)
Saves information about a guest connection in an event.
Definition: alerts.c:1331
QWORD Gva
The GVA of the "struct proto" object.
Definition: lixnet.c:18
char CHAR
Definition: intro_types.h:56
QWORD Endpoint
Guest virtual address of the endpoint/socket object.
Definition: intronet.h:61
#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
Status values returned by most functions that can signal different success or failure states...
#define FALSE
Definition: intro_types.h:34