Bitdefender Hypervisor Memory Introspection
msr_protection.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "msr_protection.h"
6 #include "alerts.h"
7 #include "guests.h"
8 #include "hook_msr.h"
9 
10 
12 static void *gSysenterEipHook;
13 static void *gSysenterEspHook;
14 static void *gSysenterCsHook;
15 static void *gSyscallLstarHook;
16 static void *gSyscallStarHook;
17 
18 
19 static INTSTATUS
22  _In_ PEXCEPTION_KM_ORIGINATOR Originator,
23  _In_ INTRO_ACTION Action,
25  )
39 {
40  INTSTATUS status;
41  PEVENT_MSR_VIOLATION pMsrViol;
42 
43  pMsrViol = &gAlert.Msr;
44  memzero(pMsrViol, sizeof(*pMsrViol));
45 
46  pMsrViol->Header.Action = Action;
47  pMsrViol->Header.Reason = Reason;
48 
50  pMsrViol->Header.MitreID = idRootkit;
51 
52  IntAlertMsrFill(Victim, Originator, pMsrViol);
53 
55 
57 
58  IntAlertFillCodeBlocks(Originator->Original.Rip, gGuest.Mm.SystemCr3, FALSE, &pMsrViol->CodeBlocks);
60 
61  IntAlertFillVersionInfo(&pMsrViol->Header);
62 
63  status = IntNotifyIntroEvent(introEventMsrViolation, pMsrViol, sizeof(*pMsrViol));
64  if (!INT_SUCCESS(status))
65  {
66  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
67  }
68 
69  return status;
70 }
71 
72 
73 static INTSTATUS
75  _In_ DWORD Msr,
76  _In_ DWORD Flags,
77  _Out_ INTRO_ACTION *Action,
78  _In_opt_ void *Context,
79  _In_opt_ QWORD OriginalValue,
80  _In_opt_ const QWORD *NewValue
81  )
99 {
100  INTSTATUS status;
101  EXCEPTION_VICTIM_ZONE victim;
102  EXCEPTION_KM_ORIGINATOR originator;
103  BOOLEAN exitAfterInformation;
104  INTRO_ACTION_REASON reason;
105 
106  UNREFERENCED_PARAMETER(Flags);
107  UNREFERENCED_PARAMETER(Context);
108 
109  if (NULL == Action)
110  {
112  }
113 
114  if (NULL == NewValue)
115  {
117  }
118 
119  // If the MSR is sysenter/syscall MSR, and the OldValue is zero, we allow the write.
120  if (((Msr == IG_IA32_SYSENTER_CS) || (Msr == IG_IA32_SYSENTER_EIP) ||
121  (Msr == IG_IA32_SYSENTER_ESP) || (Msr == IG_IA32_STAR) || (Msr == IG_IA32_LSTAR)) &&
122  (0 == OriginalValue))
123  {
124  *Action = introGuestAllowed;
125  return INT_STATUS_SUCCESS;
126  }
127 
128  // If the same value is written again in the MSR, we're done.
129  if (OriginalValue == *NewValue)
130  {
131  *Action = introGuestAllowed;
132  return INT_STATUS_SUCCESS;
133  }
134 
136 
137  *Action = introGuestNotAllowed;
138  reason = introReasonUnknown;
139  exitAfterInformation = FALSE;
140 
141  memzero(&originator, sizeof(originator));
142  memzero(&victim, sizeof(victim));
143 
144  status = IntExceptKernelGetOriginator(&originator, 0);
145  if (status == INT_STATUS_EXCEPTION_BLOCK)
146  {
147  reason = introReasonNoException;
148  exitAfterInformation = TRUE;
149  }
150  else if (!INT_SUCCESS(status))
151  {
152  reason = introReasonInternalError;
153  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
154  exitAfterInformation = TRUE;
155  }
156 
157  status = IntExceptGetVictimMsr(*NewValue, OriginalValue, Msr, &victim);
158  if (!INT_SUCCESS(status))
159  {
160  reason = introReasonInternalError;
161  ERROR("[ERROR] Failed getting zone details: 0x%08x\n", status);
162  exitAfterInformation = TRUE;
163  }
164 
165  if (exitAfterInformation)
166  {
167  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
168  }
169  else
170  {
171  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventMsrViolation);
172  }
173 
175 
177  {
178  EVENT_MSR_VIOLATION *pMsrViol = &gAlert.Msr;
179 
180  memzero(pMsrViol, sizeof(*pMsrViol));
181 
182  pMsrViol->Header.Action = *Action;
183  pMsrViol->Header.Reason = reason;
184 
186 
188  pMsrViol->Header.MitreID = idRootkit;
189 
191 
192  IntAlertFillLixKmModule(originator.Original.Driver, &pMsrViol->Originator.Module);
194 
195  pMsrViol->Victim.Msr = Msr;
196 
197  pMsrViol->WriteInfo.NewValue[0] = *NewValue;
198  pMsrViol->WriteInfo.OldValue[0] = OriginalValue;
199 
200  IntAlertFillVersionInfo(&pMsrViol->Header);
201 
204 
205  status = IntNotifyIntroEvent(introEventMsrViolation, pMsrViol, sizeof(*pMsrViol));
206  if (!INT_SUCCESS(status))
207  {
208  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
209  }
210  }
211 
213 
214  return INT_STATUS_SUCCESS;
215 }
216 
217 
218 static INTSTATUS
220  _In_ DWORD Msr,
221  _In_ DWORD Flags,
222  _Out_ INTRO_ACTION *Action,
223  _In_opt_ void *Context,
224  _In_opt_ QWORD OriginalValue,
225  _In_opt_ const QWORD *NewValue
226  )
244 {
245  INTSTATUS status;
246  EXCEPTION_VICTIM_ZONE victim;
247  EXCEPTION_KM_ORIGINATOR originator;
248  BOOLEAN exitAfterInformation;
249  INTRO_ACTION_REASON reason;
250 
251  UNREFERENCED_PARAMETER(Flags);
252  UNREFERENCED_PARAMETER(Context);
253 
254  if (NULL == Action)
255  {
257  }
258 
259  if (NULL == NewValue)
260  {
262  }
263 
264  // LAN: If the MSR is sysenter/syscall MSR, and the OldValue is zero, we allow the write.
265  if (((Msr == IG_IA32_SYSENTER_CS) || (Msr == IG_IA32_SYSENTER_EIP) ||
266  (Msr == IG_IA32_SYSENTER_ESP) || (Msr == IG_IA32_STAR) || (Msr == IG_IA32_LSTAR)) &&
267  (0 == OriginalValue))
268  {
269  *Action = introGuestAllowed;
270  return INT_STATUS_SUCCESS;
271  }
272 
273  // If the same value is written again in the MSR, we're done.
274  if (OriginalValue == *NewValue)
275  {
276  *Action = introGuestAllowed;
277  return INT_STATUS_SUCCESS;
278  }
279 
281 
282  memzero(&victim, sizeof(victim));
283  memzero(&originator, sizeof(originator));
284 
285  // By default we do not allow this
286  *Action = introGuestNotAllowed;
287  reason = introReasonUnknown;
288  exitAfterInformation = FALSE;
289 
290  status = IntExceptKernelGetOriginator(&originator, 0);
291  if (status == INT_STATUS_EXCEPTION_BLOCK)
292  {
293  reason = introReasonNoException;
294  exitAfterInformation = TRUE;
295  }
296  else if (!INT_SUCCESS(status))
297  {
298  reason = introReasonInternalError;
299  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
300  exitAfterInformation = TRUE;
301  }
302 
303  status = IntExceptGetVictimMsr(*NewValue, OriginalValue, Msr, &victim);
304  if (!INT_SUCCESS(status))
305  {
306  reason = introReasonInternalError;
307  ERROR("[ERROR] Failed getting zone details: 0x%08x\n", status);
308  exitAfterInformation = TRUE;
309  }
310 
311  if (exitAfterInformation)
312  {
313  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
314  }
315  else
316  {
317  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventMsrViolation);
318  }
319 
321 
323  {
324  IntWinMsrSendAlert(&victim, &originator, *Action, reason);
325  }
326 
328 
329  return status;
330 }
331 
332 
333 INTSTATUS
335  void
336  )
343 {
344  INTSTATUS status;
345  BOOLEAN hookX64Msrs, hookX86Msrs;
347 
348  if (gMsrHookSet)
349  {
351  }
352 
354  {
355  hookX64Msrs = gGuest.Guest64;
356  hookX86Msrs = !hookX64Msrs;
357 
358  pCallback = IntWinMsrHandleWrite;
359  }
360  else
361  {
362  hookX64Msrs = hookX86Msrs = TRUE;
363 
364  pCallback = IntLixMsrHandleWrite;
365  }
366 
367  if (hookX86Msrs)
368  {
369  TRACE("[MSR] Adding protection on MSR IA32_SYSENTER_EIP...\n");
370 
372  if (!INT_SUCCESS(status))
373  {
374  ERROR("[ERROR] Failed hooking the MSR!\n");
375  return status;
376  }
377 
378 
379  TRACE("[MSR] Adding protection on MSR IA32_SYSENTER_ESP...\n");
380 
382  if (!INT_SUCCESS(status))
383  {
384  ERROR("[ERROR] Failed hooking the MSR!\n");
385  return status;
386  }
387 
388 
389  TRACE("[MSR] Adding protection on MSR IA32_SYSENTER_CS...\n");
390 
392  if (!INT_SUCCESS(status))
393  {
394  ERROR("[ERROR] Failed hooking the MSR!\n");
395  return status;
396  }
397  }
398 
399  if (hookX64Msrs)
400  {
401  TRACE("[MSR] Adding protection on MSR IA32_STAR...\n");
402 
404  if (!INT_SUCCESS(status))
405  {
406  ERROR("[ERROR] Failed hooking the MSR!\n");
407  return status;
408  }
409 
410  TRACE("[MSR] Adding protection on MSR IA32_LSTAR...\n");
411 
413  if (!INT_SUCCESS(status))
414  {
415  ERROR("[ERROR] Failed hooking the MSR!\n");
416  return status;
417  }
418  }
419 
420  gMsrHookSet = TRUE;
421 
422  return INT_STATUS_SUCCESS;
423 }
424 
425 
426 INTSTATUS
428  void
429  )
436 {
437  if (!gMsrHookSet)
438  {
440  }
441 
442  if (NULL != gSyscallLstarHook)
443  {
444  TRACE("[MSR] Removing protection on MSR IA32_LSTAR...\n");
445 
447 
448  gSyscallLstarHook = NULL;
449  }
450 
451  if (NULL != gSyscallStarHook)
452  {
453  TRACE("[MSR] Removing protection on MSR IA32_STAR...\n");
454 
456 
457  gSyscallStarHook = NULL;
458  }
459 
460  if (NULL != gSysenterCsHook)
461  {
462  TRACE("[MSR] Removing protection on MSR IA32_SYSENTER_CS...\n");
463 
465 
466  gSysenterCsHook = NULL;
467  }
468 
469  if (NULL != gSysenterEipHook)
470  {
471  TRACE("[MSR] Removing protection on MSR IA32_SYSENTER_Eip...\n");
472 
474 
475  gSysenterEipHook = NULL;
476  }
477 
478  if (NULL != gSysenterEspHook)
479  {
480  TRACE("[MSR] Removing protection on MSR IA32_SYSENTER_ESP...\n");
481 
483 
484  gSysenterEspHook = NULL;
485  }
486 
487  gMsrHookSet = FALSE;
488 
489  return INT_STATUS_SUCCESS;
490 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
#define _In_opt_
Definition: intro_sal.h:16
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
#define IG_IA32_SYSENTER_ESP
Definition: glueiface.h:139
QWORD IntAlertCoreGetFlags(QWORD ProtectionFlag, INTRO_ACTION_REASON Reason)
Returns the flags for an alert.
Definition: alerts.c:366
An internal error occurred (no memory, pages not present, etc.).
Definition: intro_types.h:195
BOOLEAN IntPolicyCoreForceBetaIfNeeded(QWORD Flag, INTRO_ACTION *Action)
Checks if a forced action should be taken even if the log-only mode is active.
Definition: introcore.c:2803
#define _In_
Definition: intro_sal.h:21
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1199
Write access.
Definition: glueiface.h:174
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
BOOLEAN IntPolicyCoreTakeAction(QWORD Flag, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason)
Returns the action that should be taken for a core introspection option.
Definition: introcore.c:2693
QWORD NewValue[8]
The written value. Only the first Size bytes are valid.
Definition: intro_types.h:981
#define STATS_EXIT(id)
Definition: stats.h:160
void IntAlertMsrFill(const EXCEPTION_VICTIM_ZONE *Victim, const EXCEPTION_KM_ORIGINATOR *Originator, EVENT_MSR_VIOLATION *MsrViolation)
Saves information about a MSR write attempt in an event.
Definition: alerts.c:1150
INTRO_MODULE ReturnModule
The module to which the current code return to.
Definition: intro_types.h:1326
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1198
static void * gSysenterEspHook
IA32_SYSENTER_ESP hook.
#define IG_IA32_SYSENTER_EIP
Definition: glueiface.h:140
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
KERNEL_DRIVER * Driver
The driver that's modifying the memory.
Definition: exceptions.h:949
int INTSTATUS
The status data type.
Definition: introstatus.h:24
INTSTATUS IntMsrSyscallUnprotect(void)
Remove protection from all protected MSRs.
INTRO_EXEC_CONTEXT ExecContext
Information about the instruction that triggered the alert.
Definition: intro_types.h:1339
static BOOLEAN gMsrHookSet
True if the SYSCALL/SYSENTER MSRs are protected.
Rootkit.
Definition: intro_types.h:1144
Describes a kernel-mode originator.
Definition: exceptions.h:943
static void * gSysenterEipHook
IA32_SYSENTER_EIP hook.
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
struct _EXCEPTION_KM_ORIGINATOR::@63 Return
void IntAlertFillVersionInfo(INTRO_VIOLATION_HEADER *Header)
Fills version information for an alert.
Definition: alerts.c:327
struct _EVENT_MSR_VIOLATION::@291 Originator
static void * gSysenterCsHook
IA32_SYSENTER_CS hook.
#define INTRO_OPT_PROT_KM_MSR_SYSCALL
Definition: intro_types.h:427
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
INTSTATUS IntAlertFillCodeBlocks(QWORD Rip, QWORD Cr3, BOOLEAN Execute, INTRO_CODEBLOCKS *CodeBlocks)
Fills the code blocks pattern for an alert.
Definition: alerts.c:71
static INTSTATUS IntWinMsrSendAlert(PEXCEPTION_VICTIM_ZONE Victim, PEXCEPTION_KM_ORIGINATOR Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Send an MSR alert.
static void * gSyscallStarHook
IA32_STAR hook.
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
void IntAlertFillLixKmModule(const KERNEL_DRIVER *Driver, INTRO_MODULE *EventModule)
Saves information about a kernel module inside an alert.
Definition: alerts.c:1235
INTRO_CODEBLOCKS CodeBlocks
Code blocks extracted for the alert.
Definition: intro_types.h:1337
#define INT_STATUS_EXCEPTION_BLOCK
Definition: introstatus.h:421
#define STATS_ENTER(id)
Definition: stats.h:153
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1196
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
#define memzero(a, s)
Definition: introcrt.h:35
Event structure for MSR violation.
Definition: intro_types.h:1316
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
#define IG_IA32_STAR
Definition: glueiface.h:145
#define TRUE
Definition: intro_types.h:30
INTSTATUS IntExceptGetVictimMsr(QWORD NewValue, QWORD OldValue, DWORD Msr, EXCEPTION_VICTIM_ZONE *Victim)
This function is used to get the information about the MSR victim.
static void * gSyscallLstarHook
IA32_LSTAR hook.
#define TRACE(fmt,...)
Definition: glue.h:58
void IntExceptKernelLogInformation(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_KM_ORIGINATOR *Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Print the information about a kernel-mode violation and dumps the code-blocks.
Kernel-mode exception.
Definition: exceptions.h:61
#define IG_IA32_LSTAR
Definition: glueiface.h:146
INTSTATUS IntHookMsrRemoveHook(HOOK_MSR *Hook)
Remove a model specific register hook.
Definition: hook_msr.c:138
#define INT_STATUS_ALREADY_INITIALIZED_HINT
Definition: introstatus.h:323
static INTSTATUS IntWinMsrHandleWrite(DWORD Msr, DWORD Flags, INTRO_ACTION *Action, void *Context, QWORD OriginalValue, const QWORD *NewValue)
Handles a model specific register write attempt done by a Windows guest.
INTRO_MODULE Module
The module that did the malicious access.
Definition: intro_types.h:1325
#define WARNING(fmt,...)
Definition: glue.h:60
Describes the modified zone.
Definition: exceptions.h:893
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
EVENT_MSR_VIOLATION Msr
Definition: alerts.h:17
uint32_t DWORD
Definition: intro_types.h:49
#define INT_STATUS_INVALID_PARAMETER_6
Definition: introstatus.h:77
enum _INTRO_ACTION INTRO_ACTION
Event actions.
QWORD Rip
The RIP from where the call to the exported function came.
Definition: exceptions.h:950
QWORD OldValue[8]
The original value. Only the first Size bytes are valid.
Definition: intro_types.h:980
INTSTATUS IntHookMsrSetHook(DWORD Msr, DWORD Flags, PFUNC_MsrReadWriteHookCallback Callback, void *Context, void **Hook)
Set a model-specific register write hook.
Definition: hook_msr.c:11
QWORD Cr3
The value of the guest CR3 register when the event was generated.
Definition: intro_types.h:970
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
void IntAlertFillWinProcessByCr3(QWORD ProcessCr3, INTRO_PROCESS *EventProcess)
Saves information about a Windows process inside an alert. The process is searched by its kernel CR3...
Definition: alerts.c:756
INTSTATUS IntMsrSyscallProtect(void)
Enable protection for all SYSCALL and SYSENTER MSRs.
INTSTATUS(* PFUNC_MsrReadWriteHookCallback)(DWORD Msr, DWORD Flags, INTRO_ACTION *Action, void *Context, QWORD OriginalValue, QWORD *NewValue)
Model specific register access callback.
Definition: hook_msr.h:23
struct _EXCEPTION_KM_ORIGINATOR::@64 Original
static INTSTATUS IntLixMsrHandleWrite(DWORD Msr, DWORD Flags, INTRO_ACTION *Action, void *Context, QWORD OriginalValue, const QWORD *NewValue)
Handles a model specific register write attempt done by a Linux guest.
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1194
union _EVENT_MSR_VIOLATION::@292 Victim
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1197
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1318
The action was blocked because there was no exception for it.
Definition: intro_types.h:189
DWORD Msr
The ID of the MSR as defined by the Intel documentation.
Definition: intro_types.h:1331
Sent when a MSR violation triggers an alert.See EVENT_MSR_VIOLATION.
Definition: intro_types.h:86
void IntAlertFillLixCurrentProcess(INTRO_PROCESS *EventProcess)
Saves the current Linux process inside an event.
Definition: alerts.c:1310
INTSTATUS IntExceptKernelGetOriginator(EXCEPTION_KM_ORIGINATOR *Originator, DWORD Options)
This function is used to get the information about the kernel-mode originator.
INTSTATUS IntAlertFillExecContext(QWORD Cr3, INTRO_EXEC_CONTEXT *ExecContext)
Fills the current execution context.
Definition: alerts.c:31
#define IG_IA32_SYSENTER_CS
Definition: glueiface.h:138
void IntExcept(EXCEPTION_VICTIM_ZONE *Victim, void *Originator, EXCEPTION_TYPE Type, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason, INTRO_EVENT_TYPE EventClass)
This function is the entry point for the exception mechanism.
Definition: exceptions.c:3357
INTRO_WRITE_INFO WriteInfo
The original value of the MSR and the value that the guest tried to write.
Definition: intro_types.h:1335
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68