Bitdefender Hypervisor Memory Introspection
wininfinityhook.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "wininfinityhook.h"
6 #include "decoder.h"
7 #include "hook.h"
8 #include "winpe.h"
9 #include "alerts.h"
10 
11 
32 
33 
41 #define WMI_PTR_MASK 0xFFFFFFFFFFFFFFFE
42 
43 
44 //#define OPT_SET_WMI_SPP_STATS
45 
49 typedef struct _WIN_LOGGER_CTX_STATE
50 {
56 
58 
62 
65 
68 
72 
77 
81 
85 
87 
88 
93 
94 
95 static INTSTATUS
97  _In_ QWORD OldValue,
98  _In_ QWORD NewValue,
99  _In_opt_ INTEGRITY_REGION *IntegrityRegion,
100  _Inout_ INTRO_ACTION *Action
101  );
102 
103 
104 static INTSTATUS
106  void
107  )
124 {
125  INTSTATUS status;
126  QWORD currentCpuGetClock = 0;
127  KERNEL_DRIVER *pDrv = NULL;
129 
130  if ((gLoggerCtxState.WmiLoggerCtx & 1) || (gLoggerCtxState.WmiLoggerCtx & WMI_PTR_MASK) == 0)
131  {
133  }
134 
135  status = IntKernVirtMemRead(gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
137  &currentCpuGetClock,
138  NULL);
139  if (!INT_SUCCESS(status))
140  {
141  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
142  return status;
143  }
144 
145  pDrv = IntDriverFindByAddress(currentCpuGetClock);
146  if (NULL != pDrv && pDrv->BaseVa == gGuest.KernelVa)
147  {
148  // If the new GetCpuClock resides in the kernel, always consider it.
149  gLoggerCtxState.CurrentGetCpuClock = currentCpuGetClock;
150 
151  return INT_STATUS_SUCCESS;
152  }
153 
154  if (gLoggerCtxState.CurrentGetCpuClock == 0)
155  {
156  // We have a relocation on which we don't previously have any known good GetCpuClock value
157  // and the new value doesn't reside in kernel. At least send an alert for this case.
158  status = IntWinInfHookIntegrityHandleWrite(gLoggerCtxState.CurrentGetCpuClock, currentCpuGetClock, NULL,
159  &action);
160  if (!INT_SUCCESS(status))
161  {
162  ERROR("[ERROR] IntWinInfHookIntegrityHandleWrite failed: 0x%08x\n", status);
163  return status;
164  }
165 
166  // If we allowed the "relocation" to take place with a non-kernel resident GetCpuClock, then
167  // we must consider the current GetCpuClock as the real one and protect it.
168  if (action == introGuestAllowed)
169  {
170  gLoggerCtxState.CurrentGetCpuClock = currentCpuGetClock;
171  return INT_STATUS_SUCCESS;
172  }
173 
175  }
176 
177  if (currentCpuGetClock == gLoggerCtxState.CurrentGetCpuClock)
178  {
179  return INT_STATUS_SUCCESS;
180  }
181 
182  // If allowed, we must not write the old GetCpuClock, but rather
183  // we should consider the current cpugetclock to be the "old one" for further writes
184 
185  status = IntWinInfHookIntegrityHandleWrite(gLoggerCtxState.CurrentGetCpuClock, currentCpuGetClock, NULL, &action);
186  if (!INT_SUCCESS(status))
187  {
188  ERROR("[ERROR] IntWinInfHookIntegrityHandleWrite failed: 0x%08x\n", status);
189  return status;
190  }
191 
192  if (introGuestAllowed == action)
193  {
194  gLoggerCtxState.CurrentGetCpuClock = currentCpuGetClock;
195  }
196  else if (introGuestNotAllowed == action)
197  {
198  IntPauseVcpus();
199 
200  status = IntKernVirtMemWrite(gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
202  &gLoggerCtxState.CurrentGetCpuClock);
203  if (!INT_SUCCESS(status))
204  {
205  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
206  }
207 
208  IntResumeVcpus();
209  }
210 
211  // Since we have the old CpuGetClock and we overwritten it, we should protect the new WMI_LOGGER_CONTEXT.
212  return status;
213 }
214 
215 
216 static INTSTATUS
218  _In_ EXCEPTION_VICTIM_ZONE *Victim,
219  _In_ EXCEPTION_KM_ORIGINATOR *Originator,
220  _In_ INTRO_ACTION Action,
222  )
239 {
240  INTSTATUS status;
241  PEVENT_EPT_VIOLATION pEptViol;
242  PIG_ARCH_REGS regs;
243 
244  regs = &gVcpu->Regs;
245 
246  pEptViol = &gAlert.Ept;
247  memzero(pEptViol, sizeof(*pEptViol));
248 
249  pEptViol->Header.Action = Action;
250  pEptViol->Header.Reason = Reason;
251  pEptViol->Header.MitreID = idRootkit;
252 
254 
255  IntAlertEptFillFromKmOriginator(Originator, pEptViol);
256  IntAlertEptFillFromVictimZone(Victim, pEptViol);
257 
259 
261 
262  IntAlertFillCodeBlocks(Originator->Original.Rip, regs->Cr3, FALSE, &pEptViol->CodeBlocks);
263  IntAlertFillExecContext(0, &pEptViol->ExecContext);
264 
265  IntAlertFillVersionInfo(&pEptViol->Header);
266 
267  status = IntNotifyIntroEvent(introEventEptViolation, pEptViol, sizeof(*pEptViol));
268  if (!INT_SUCCESS(status))
269  {
270  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
271  }
272 
273  return status;
274 }
275 
276 
277 static INTSTATUS
279  _Inout_ INTRO_ACTION *Action
280  )
290 {
291  EXCEPTION_VICTIM_ZONE victim = { 0 };
292  EXCEPTION_KM_ORIGINATOR originator = { 0 };
293  INTRO_ACTION_REASON reason;
294  INTSTATUS status;
295  BOOLEAN exitAfterInformation = FALSE;
296 
298 
299  *Action = introGuestNotAllowed;
300  reason = introReasonUnknown;
301 
302  status = IntExceptKernelGetOriginator(&originator, 0);
303  if (status == INT_STATUS_EXCEPTION_BLOCK)
304  {
305  reason = introReasonNoException;
306  exitAfterInformation = TRUE;
307  }
308  else if (!INT_SUCCESS(status))
309  {
310  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
311  reason = introReasonInternalError;
312  exitAfterInformation = TRUE;
313  }
314 
315  status = IntExceptGetVictimEpt(NULL,
316  0,
317  gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
319  ZONE_WRITE,
320  &victim);
321  if (!INT_SUCCESS(status))
322  {
323  reason = introReasonInternalError;
324  ERROR("[ERROR] Failed getting zone details: 0x%08x\n", status);
325  exitAfterInformation = TRUE;
326  }
327 
328  if (exitAfterInformation)
329  {
330  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
331  }
332  else
333  {
334  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventIntegrityViolation);
335  }
336 
338 
340  {
341  IntWinInfHookEptSppSendAlert(&victim, &originator, *Action, reason);
342 
343  LOG("[INFINITY-HOOK] Detected modification of WMI_LOGGER_CONTEXT.GetCpuClock. Rip: 0x%016llx\n",
344  gVcpu->Regs.Rip);
345  }
346 
348 
349  return status;
350 }
351 
352 
353 static INTSTATUS
355  _In_opt_ void *Context,
356  _In_ void *Hook,
357  _In_ QWORD Address,
358  _Inout_ INTRO_ACTION *Action
359  )
374 {
375  INTSTATUS status;
376 
377  UNREFERENCED_PARAMETER(Context);
379  UNREFERENCED_PARAMETER(Address);
380 
381  gLoggerCtxState.WmiInteresting++;
382 
383  TRACE("[SPP WMI STATS] Total writes: 0x%016llx, interesting: 0x%016llx\n",
384  gLoggerCtxState.WmiTotal, gLoggerCtxState.WmiInteresting);
385 
386  status = IntWinInfHookEptSppHandleWrite(Action);
387  if (!INT_SUCCESS(status))
388  {
389  ERROR("[ERROR] IntWinInfHookEptSppHandleWrite failed: 0x%08x\n", status);
390  }
391 
392  return INT_STATUS_SUCCESS;
393 }
394 
395 
396 static INTSTATUS
398  _In_opt_ void *Context,
399  _In_ void *Hook,
400  _In_ QWORD Address,
401  _Out_ INTRO_ACTION *Action
402  )
419 {
420  UNREFERENCED_PARAMETER(Context);
422  UNREFERENCED_PARAMETER(Address);
423 
424  gLoggerCtxState.WmiTotal++;
425 
426  if (gLoggerCtxState.WmiTotal % 1000 == 0)
427  {
428  LOG("[SPP WMI STATS] Total writes: 0x%016llx, interesting: 0x%016llx\n", gLoggerCtxState.WmiTotal,
429  gLoggerCtxState.WmiInteresting);
430  }
431 
432  *Action = introGuestAllowed;
433 
434  return INT_STATUS_SUCCESS;
435 }
436 
437 
438 static INTSTATUS
440  void
441  )
451 {
453 
454  LOG("[INFO] Request to hook logger CTX 0x%016llx through SPP\n", gLoggerCtxState.WmiLoggerCtx);
455 
456  if (NULL == gLoggerCtxState.WmiLoggerHookObject)
457  {
460  &gLoggerCtxState.WmiLoggerHookObject);
461  if (!INT_SUCCESS(status))
462  {
463  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
464  return status;
465  }
466 
467  status = IntHookObjectHookRegion(gLoggerCtxState.WmiLoggerHookObject,
469  gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
473  NULL,
474  0,
475  NULL);
476  if (!INT_SUCCESS(status))
477  {
478  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
479  return status;
480  }
481 
482  LOG("[INFO] Successfully hooked logger CTX 0x%016llx\n", gLoggerCtxState.WmiLoggerCtx);
483  }
484 
485 #ifdef OPT_SET_WMI_SPP_STATS
486  if (NULL == gLoggerCtxState.WmiLoggerHookObjectStats)
487  {
489  &gLoggerCtxState.WmiLoggerHookObjectStats);
490  if (!INT_SUCCESS(status))
491  {
492  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
493  return status;
494  }
495 
496  status = IntHookObjectHookRegion(gLoggerCtxState.WmiLoggerHookObjectStats,
498  gLoggerCtxState.WmiLoggerCtx & 0xFFFFFFFFFFFFFF80,
499  0x80,
502  NULL,
503  0,
504  NULL);
505  if (!INT_SUCCESS(status))
506  {
507  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
508  return status;
509  }
510  }
511 #endif
512 
513  return status;
514 }
515 
516 
517 static INTSTATUS
519  _In_opt_ void *Context,
520  _In_ void *Hook,
521  _In_ QWORD Address,
522  _Out_ INTRO_ACTION *Action
523  )
543 {
544  OPERAND_VALUE writtenValue = { 0 };
545  INTSTATUS status;
546  QWORD newValue;
547  KERNEL_DRIVER *pDrv = NULL;
548  BOOLEAN shouldSkipHook = FALSE;
549 
550  UNREFERENCED_PARAMETER(Context);
552  UNREFERENCED_PARAMETER(Address);
553 
554  gLoggerCtxState.SiloInteresting++;
555 
556  LOG("[SPP SILO WMI] Ptr changed, total: 0x%016llx, interesting: 0x%016llx\n", gLoggerCtxState.SiloTotal,
557  gLoggerCtxState.SiloInteresting);
558 
559  status = IntDecGetWrittenValueFromInstruction(&gVcpu->Instruction, &gVcpu->Regs, NULL, &writtenValue);
560  if (!INT_SUCCESS(status))
561  {
562  ERROR("[ERROR] IntDecGetWrittenValueFromInstruction failed: 0x%08x\n", status);
563  return status;
564  }
565 
566  newValue = writtenValue.Value.QwordValues[0] & WMI_PTR_MASK;
567 
568  // If the first bit is set, then at the very first call of EtwpLogKernelEvent, the kernel will call
569  // nt!EtwpCloseLogger on the WMI_LOGGER_CONTEXT, as it means that it is no longer a valid context and is waiting
570  // to be closed.
571  shouldSkipHook = (writtenValue.Value.QwordValues[0] & 1) || newValue == 0;
572 
573  LOG("[INFO] WMI_LOGGER_CONTEXT relocated from 0x%016llx to 0x%016llx (ptr 0x%016llx)\n",
574  gLoggerCtxState.WmiLoggerCtx, newValue, writtenValue.Value.QwordValues[0]);
575 
576  if (NULL != gLoggerCtxState.WmiLoggerHookObject)
577  {
578  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.WmiLoggerHookObject, 0);
579  if (!INT_SUCCESS(status))
580  {
581  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
582  return status;
583  }
584  }
585 
586  if (NULL != gLoggerCtxState.WmiLoggerHookObjectStats)
587  {
588  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.WmiLoggerHookObjectStats, 0);
589  if (!INT_SUCCESS(status))
590  {
591  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
592  return status;
593  }
594  }
595 
596  gLoggerCtxState.WmiLoggerCtx = newValue;
597 
598  if (shouldSkipHook)
599  {
600  goto _allow_and_exit;
601  }
602 
603  // First verify here if the new WMI_LOGGER_CONTEXT.GetCpuClock resides in kernel. If it doesn't, we will send an
604  // integrity alert and don't add the new integrity region. If we know the real WMI_LOGGER_CONTEXT.GetCpuClock from
605  // the kernel we will overwrite it and then protect the new region. But if something fails, we shouldn't protect the
606  // relocated structure.
607  // Only check relocation if rip is not inside kernel. From empiric evidence, the kernel can use the
608  // EtwDbgDataSilo + 0x10 for other things inside the ETW mechanism for short periods of time. So, don't bother
609  // checking those if the rip is in kernel.
611  if (NULL == pDrv || pDrv->BaseVa != gGuest.KernelVa)
612  {
614  if (!INT_SUCCESS(status))
615  {
616  WARNING("[WARNING] IntWinInfCheckCtxLoggerOnRelocation failed: 0x%08x\n", status);
617  goto _allow_and_exit;
618  }
619  }
620  else
621  {
622  LOG("[INFO] WMI_LOGGER_CONTEXT relocated from kernel, RIP 0x%016llx, we'll trust it\n", gVcpu->Regs.Rip);
623  }
624 
626  if (!INT_SUCCESS(status))
627  {
628  ERROR("[ERROR] IntWinInfHookHookSppWmiGetClock failed: 0x%08x\n", status);
629  }
630 
631 _allow_and_exit:
632  *Action = introGuestAllowed;
633 
634  return status;
635 }
636 
637 
638 static INTSTATUS
640  _In_opt_ void *Context,
641  _In_ void *Hook,
642  _In_ QWORD Address,
643  _Out_ INTRO_ACTION *Action
644  )
661 {
662 
663  UNREFERENCED_PARAMETER(Context);
665  UNREFERENCED_PARAMETER(Address);
666 
667  *Action = introGuestAllowed;
668 
669  gLoggerCtxState.SiloTotal++;
670 
671  if (gLoggerCtxState.SiloTotal % 1000 == 0)
672  {
673  LOG("[SPP SILO WMI] Silo changed, total: 0x%016llx, interesting: 0x%016llx\n",
674  gLoggerCtxState.SiloTotal, gLoggerCtxState.SiloInteresting);
675  }
676 
677  return INT_STATUS_SUCCESS;
678 
679 }
680 
681 
682 static INTSTATUS
684  void
685  )
697 {
698  INTSTATUS status;
699 
700  if (NULL != gLoggerCtxState.SiloHookObject || NULL != gLoggerCtxState.SiloHookObjectStats)
701  {
702  ERROR("[ERROR] Silo hook already initialized: %p %p\n", gLoggerCtxState.SiloHookObject,
703  gLoggerCtxState.SiloHookObjectStats);
705  }
706 
707  if (gGuest.SupportSPP)
708  {
709  // the WMI_LOGGER_CONTEXT can (and will) relocate from EtwDebuggerDataSilo
710  // this will cause a very high amount of writes to something that is no more a WMI_LOGGER_CONTEXT
711  // (or might be, but it is no longer the WMI_LOGGER_CONTEXT used by the kernel...)
713  &gLoggerCtxState.SiloHookObject);
714  if (!INT_SUCCESS(status))
715  {
716  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
717  return status;
718  }
719 
720  status = IntHookObjectHookRegion(gLoggerCtxState.SiloHookObject,
722  gLoggerCtxState.LoggerGvaInSilo,
726  NULL,
727  0,
728  NULL);
729  if (!INT_SUCCESS(status))
730  {
731  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
732  return status;
733  }
734 
735 #ifdef OPT_SET_WMI_SPP_STATS
737  gGuest.Mm.SystemCr3, &gLoggerCtxState.SiloHookObjectStats);
738  if (!INT_SUCCESS(status))
739  {
740  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
741  return status;
742  }
743 
744  status = IntHookObjectHookRegion(gLoggerCtxState.SiloHookObjectStats,
746  gLoggerCtxState.LoggerGvaInSilo & 0xFFFFFFFFFFFFFF80,
747  0x80,
750  NULL,
751  0,
752  NULL);
753  if (!INT_SUCCESS(status))
754  {
755  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
756  return status;
757  }
758 #endif
759 
760  LOG("[INFO] Successfully hooked silo ptr\n");
761  return INT_STATUS_SUCCESS;
762  }
763 
765 }
766 
767 
768 static INTSTATUS
770  _In_ EXCEPTION_VICTIM_ZONE *Victim,
771  _In_ EXCEPTION_KM_ORIGINATOR *Originator,
772  _In_ INTRO_ACTION Action,
774  )
794 {
795  INTSTATUS status;
797 
798  pIntViol = &gAlert.Integrity;
799  memzero(pIntViol, sizeof(*pIntViol));
800 
801  pIntViol->BaseAddress = Victim->Integrity.StartVirtualAddress;
802  pIntViol->VirtualAddress = Victim->Integrity.StartVirtualAddress + Victim->Integrity.Offset;
803  pIntViol->Victim.Type = Victim->Object.Type;
804  pIntViol->Size = Victim->Integrity.TotalLength;
805 
807 
808  // Force de-activation of ALERT_FLAG_NOT_RING0. We're always in ring0.
809  pIntViol->Header.Flags &= ~ALERT_FLAG_NOT_RING0;
810 
812  {
813  pIntViol->Header.Flags |= ALERT_FLAG_BETA;
814  }
815 
816  pIntViol->Header.Flags |= ALERT_FLAG_ASYNC;
817 
818  pIntViol->Header.Action = Action;
819  pIntViol->Header.Reason = Reason;
820  pIntViol->Header.MitreID = idRootkit;
821 
823 
824  IntAlertFillWriteInfo(Victim, &pIntViol->WriteInfo);
825 
826  IntAlertFillWinKmModule(Originator->Original.Driver, &pIntViol->Originator.Module);
827 
829 
830  // We can't know from what CPU the write was, but we know where the integrity check failed
831  pIntViol->Header.CpuContext.Valid = FALSE;
832 
834 
835  IntAlertFillVersionInfo(&pIntViol->Header);
836 
837  status = IntNotifyIntroEvent(introEventIntegrityViolation, pIntViol, sizeof(*pIntViol));
838  if (!INT_SUCCESS(status))
839  {
840  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
841  }
842 
843  return INT_STATUS_SUCCESS;
844 }
845 
846 
847 static INTSTATUS
849  _In_ QWORD OldValue,
850  _In_ QWORD NewValue,
851  _In_opt_ INTEGRITY_REGION *IntegrityRegion,
852  _Inout_ INTRO_ACTION *Action
853  )
876 {
877  EXCEPTION_VICTIM_ZONE victim = { 0 };
878  EXCEPTION_KM_ORIGINATOR originator = { 0 };
879  INTSTATUS status;
880  DWORD offset = 0;
881  BOOLEAN exitAfterInformation = FALSE;
882  INTRO_ACTION_REASON reason;
883 
884  UNREFERENCED_PARAMETER(OldValue);
885  UNREFERENCED_PARAMETER(NewValue);
886 
887  *Action = introGuestNotAllowed;
888  reason = introReasonUnknown;
889 
890  // Bail out if the new value is not a kernel pointer. As observer on 20h1, it is equal to 3 at start. We shouldn't
891  // consider it malicious if it doesn't point somewhere in the kernel space.
892  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, NewValue))
893  {
894  *Action = introGuestAllowed;
895  return INT_STATUS_SUCCESS;
896  }
897 
899 
900  if (NULL != IntegrityRegion)
901  {
902  status = IntExceptGetVictimIntegrity(IntegrityRegion, &offset, &victim);
903  if (!INT_SUCCESS(status))
904  {
905  ERROR("[ERROR] Failed getting integrity zone: 0x%08x\n", status);
906  reason = introReasonInternalError;
907  exitAfterInformation = TRUE;
908  }
909  }
910  else
911  {
912  // If we have a NULL IntegrityRegion it means that it is a modification due to a relocation
913  // of the WMI_LOGGER_CONTEXT, so we will complete the victim with some well chosen values.
914 
915  victim.Integrity.Offset = 0;
916  victim.Integrity.StartVirtualAddress = gLoggerCtxState.WmiLoggerCtx +
917  WIN_KM_FIELD(Ungrouped, WmiGetClockOffset);
921  victim.ZoneFlags |= ZONE_WRITE | ZONE_INTEGRITY;
922  victim.WriteInfo.OldValue[0] = OldValue;
923  victim.WriteInfo.NewValue[0] = NewValue;
924  victim.WriteInfo.AccessSize = gGuest.WordSize;
925  }
926 
927  status = IntExceptGetOriginatorFromModification(&victim, &originator);
928  if (status == INT_STATUS_EXCEPTION_BLOCK)
929  {
930  reason = introReasonNoException;
931  exitAfterInformation = TRUE;
932  }
933  else if (!INT_SUCCESS(status))
934  {
935  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
936  reason = introReasonInternalError;
937  exitAfterInformation = TRUE;
938  }
939 
940  if (exitAfterInformation)
941  {
942  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
943  }
944  else
945  {
946  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventIntegrityViolation);
947  }
948 
950 
952  {
953  IntWinInfHookIntegritySendAlert(&victim, &originator, *Action, reason);
954  }
955 
957 
958  return INT_STATUS_SUCCESS;
959 }
960 
961 
962 static INTSTATUS
964  _In_ void *IntegrityRegion
965  )
974 {
975  INTSTATUS status;
976  QWORD getCpuClock = 0;
978 
979  status = IntKernVirtMemRead(gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
981  &getCpuClock,
982  NULL);
983  if (!INT_SUCCESS(status))
984  {
985  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n",
986  gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset), status);
987  return status;
988  }
989 
990  if (gLoggerCtxState.CurrentGetCpuClock == getCpuClock)
991  {
992  IntIntegrityRecalculate(IntegrityRegion);
993  return INT_STATUS_SUCCESS;
994  }
995 
996  status = IntWinInfHookIntegrityHandleWrite(gLoggerCtxState.CurrentGetCpuClock, getCpuClock,
997  IntegrityRegion, &action);
998  if (!INT_SUCCESS(status))
999  {
1000  ERROR("[ERROR] IntWinInfHookIntegrityHandleWrite failed: 0x%08x\n", status);
1001  return status;
1002  }
1003 
1004  if (introGuestAllowed == action)
1005  {
1006  IntIntegrityRecalculate(IntegrityRegion);
1007  gLoggerCtxState.CurrentGetCpuClock = getCpuClock;
1008  }
1009  else if (introGuestNotAllowed == action)
1010  {
1011  LOG("[ROOTKIT] Change of GetCpuClock: New value: 0x%016llx Old value: 0x%016llx\n", getCpuClock,
1012  gLoggerCtxState.CurrentGetCpuClock);
1013 
1014  IntPauseVcpus();
1015 
1016  status = IntKernVirtMemWrite(gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1017  gGuest.WordSize,
1018  &gLoggerCtxState.CurrentGetCpuClock);
1019  if (!INT_SUCCESS(status))
1020  {
1021  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
1022  }
1023 
1024  IntResumeVcpus();
1025  }
1026 
1027  return INT_STATUS_SUCCESS;
1028 }
1029 
1030 
1031 INTSTATUS
1033  _In_ void *IntegrityRegion
1034  )
1057 {
1058  INTSTATUS status;
1059  QWORD wmiLoggerCtx;
1060  BOOLEAN shouldSkipHook = FALSE;
1061 
1062  if (NULL != gLoggerCtxState.WmiLoggerIntegrityObject)
1063  {
1064  status = IntIntegrityDeleteRegion(gLoggerCtxState.WmiLoggerIntegrityObject);
1065  if (!INT_SUCCESS(status))
1066  {
1067  ERROR("[ERROR] IntIntegrityRemoveRegion failed: 0x%08x\n", status);
1068  return status;
1069  }
1070 
1071  gLoggerCtxState.WmiLoggerIntegrityObject = NULL;
1072  }
1073 
1074  status = IntKernVirtMemRead(gLoggerCtxState.LoggerGvaInSilo, gGuest.WordSize, &wmiLoggerCtx, NULL);
1075  if (!INT_SUCCESS(status))
1076  {
1077  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1078  return status;
1079  }
1080 
1081  // If the first bit is set, then at the very first call of EtwpLogKernelEvent, the kernel will call
1082  // nt!EtwpCloseLogger on the WMI_LOGGER_CONTEXT, as it means that it is no longer a valid context and is waiting
1083  // to be closed.
1084  shouldSkipHook = (wmiLoggerCtx & 1) || wmiLoggerCtx == 0;
1085 
1086  wmiLoggerCtx &= WMI_PTR_MASK;
1087 
1088  LOG("[INFO] WMI_LOGGER_CONTEXT relocated from 0x%016llx to %016llx (shouldSkipHook: %s)\n",
1089  gLoggerCtxState.WmiLoggerCtx, wmiLoggerCtx, shouldSkipHook ? "TRUE" : "FALSE");
1090 
1091  gLoggerCtxState.WmiLoggerCtx = wmiLoggerCtx;
1092 
1093  // NOTE: (This is handled on the shouldSkipHook case, but it is worth mentioning)
1094  // We may end up here checking some temporary structure which does not contain GetCpuClock at it's known location
1095  // For now we can just let it be this way, we can either check for the bit 1 to be set of the pointer
1096  // but bare in mind that it is always set when the temporary structure is written, but sometimes it is set even if
1097  // there is no temporary structure (and nobody keeps someone to set the first bit anyway).
1098  if (shouldSkipHook)
1099  {
1100  goto _recalculate_and_exit;
1101  }
1102 
1103  // First verify here if the new WMI_LOGGER_CONTEXT.GetCpuClock resides in kernel. If it doesn't, we will send an
1104  // integrity alert and don't add the new integrity region. If we know the real WMI_LOGGER_CONTEXT.GetCpuClock from
1105  // the kernel we will overwrite it and then protect the new region. But if something fails, we shouldn't protect
1106  // the relocated structure.
1108  if (!INT_SUCCESS(status))
1109  {
1110  WARNING("[WARNING] IntWinInfCheckCtxLoggerOnRelocation failed: 0x%08x\n", status);
1111  goto _recalculate_and_exit;
1112  }
1113 
1114  status = IntIntegrityAddRegion(wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1115  gGuest.WordSize,
1117  NULL,
1119  TRUE,
1120  &gLoggerCtxState.WmiLoggerIntegrityObject);
1121  if (!INT_SUCCESS(status))
1122  {
1123  ERROR("[ERROR] IntIntegrityAddRegion failed: 0x%08x\n", status);
1124  return status;
1125  }
1126 
1127 _recalculate_and_exit:
1128  IntIntegrityRecalculate(IntegrityRegion);
1129 
1130  return INT_STATUS_SUCCESS;
1131 }
1132 
1133 
1134 static INTSTATUS
1136  _Out_ QWORD *EtwpDebuggerData
1137  )
1150 {
1151  INTSTATUS status;
1153  {
1154  .SectionHint = ".data",
1155  .Signature =
1156  {
1157  .Length = 5,
1158  .Offset = 0,
1159  .Pattern =
1160  {
1161  0x2c,
1162  0x08,
1163  0x04,
1164  0x38,
1165  0x0c
1166  },
1167  },
1168  };
1169  DWORD etwpDbgDataRva = 0;
1170  QWORD etwpDbgDataGva;
1171 
1172  // If it was already initialized and we retried the initialization, then don't do the whole searching again,
1173  // it won't change
1174  if (0 != gLoggerCtxState.EtwDbgDataGva)
1175  {
1176  *EtwpDebuggerData = gLoggerCtxState.EtwDbgDataGva;
1177  return INT_STATUS_SUCCESS;
1178  }
1179 
1181  &etwpDbgDataRva);
1182  if (!INT_SUCCESS(status))
1183  {
1184  memcpy(pattern.SectionHint, ".rdata", sizeof(".rdata"));
1185 
1187  &pattern, FALSE, &etwpDbgDataRva);
1188  if (!INT_SUCCESS(status))
1189  {
1190  // On some versions of Win7 this structure seems to be randomly put at the end of .text ...
1192  &pattern, TRUE, &etwpDbgDataRva);
1193  if (!INT_SUCCESS(status))
1194  {
1195  ERROR("[ERROR] IntPeFindFunctionByPattern failed: 0x%08x\n", status);
1196  return status;
1197  }
1198  }
1199  }
1200 
1201  // The signature resides at offset 2 from the EtwDebuggerData structure, so we have to decrease 2.
1202  // We cast to signed INT32 the WIN_KM_FIELD as it is received from CAMI as a DWORD. But we want to decrease,
1203  // so treat it as signed.
1204  etwpDbgDataRva += (INT32)WIN_KM_FIELD(Ungrouped, EtwSignatureOffset);
1205 
1206  etwpDbgDataGva = gGuest.KernelVa + etwpDbgDataRva;
1207 
1208  LOG("[INFO] Found EtwpDebuggerData at RVA 0x%08x -> ptr at 0x%016llx\n", etwpDbgDataRva, etwpDbgDataGva);
1209 
1210  gLoggerCtxState.EtwDbgDataGva = etwpDbgDataGva;
1211 
1212  *EtwpDebuggerData = etwpDbgDataGva;
1213 
1214  return INT_STATUS_SUCCESS;
1215 }
1216 
1217 
1218 static INTSTATUS
1220  _In_opt_ void *Context,
1221  _In_ void *Hook,
1222  _In_ QWORD Address,
1223  _Out_ INTRO_ACTION *Action
1224  )
1243 {
1244  INTSTATUS status;
1245  OPERAND_VALUE writtenValue = { 0 };
1246 
1247  UNREFERENCED_PARAMETER(Context);
1248  UNREFERENCED_PARAMETER(Hook);
1249  UNREFERENCED_PARAMETER(Address);
1250 
1251  *Action = introGuestAllowed;
1252 
1253  status = IntDecGetWrittenValueFromInstruction(&gVcpu->Instruction, &gVcpu->Regs, 0, &writtenValue);
1254  if (!INT_SUCCESS(status))
1255  {
1256  ERROR("[ERROR] IntDecGetWrittenValueFromInstruction failed: 0x%08x\n", status);
1257  goto _exit;
1258  }
1259 
1260  if (writtenValue.Value.QwordValues[0] == 0)
1261  {
1262  goto _exit;
1263  }
1264 
1265  // We'll initialize on the next timer tick.
1266  gLoggerCtxState.FailedToInitialize = FALSE;
1267 
1268  status = IntHookGvaRemoveHook((HOOK_GVA **)&gLoggerCtxState.FirstSiloWriteHookObject, 0);
1269  if (!INT_SUCCESS(status))
1270  {
1271  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
1272  return status;
1273  }
1274 
1275 _exit:
1276  return INT_STATUS_SUCCESS;
1277 }
1278 
1279 
1280 static INTSTATUS
1282  _Out_ QWORD *KernelCtxLogger
1283  )
1300 {
1301  INTSTATUS status;
1302  QWORD etwpDbgDataGva = 0;
1303  QWORD etwpDbgDataSiloPtrGva;
1304  QWORD etwpDbgDataSilo = 0;
1305  QWORD ctxLoggerPtrGva;
1306  QWORD ctxLoggerGva = 0;
1307 
1308  status = IntWinInfHookGetEtwpDebuggerData(&etwpDbgDataGva);
1309  if (!INT_SUCCESS(status))
1310  {
1311  ERROR("[ERROR] IntWinInfHookGetEtwpDebuggerData failed: 0x%08x\n", status);
1312  return status;
1313  }
1314 
1315  etwpDbgDataSiloPtrGva = etwpDbgDataGva + WIN_KM_FIELD(Ungrouped, EtwDbgDataSiloOffset);
1316 
1317  status = IntKernVirtMemRead(etwpDbgDataSiloPtrGva, gGuest.WordSize, &etwpDbgDataSilo, NULL);
1318  if (!INT_SUCCESS(status))
1319  {
1320  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n", etwpDbgDataSiloPtrGva, status);
1321  return status;
1322  }
1323 
1324  if (0 == etwpDbgDataSilo)
1325  {
1326  // We mark failed to initialize so that we don't retry again and again as we expect the write hook to take place
1327  gLoggerCtxState.FailedToInitialize = TRUE;
1328 
1329  LOG("[INFO] EtwpDbgDataSilo is 0, will hook for write and init afterwards...\n");
1330 
1332  etwpDbgDataSiloPtrGva,
1333  gGuest.WordSize,
1336  NULL,
1337  NULL,
1338  0,
1339  (HOOK_GVA **)&gLoggerCtxState.FirstSiloWriteHookObject);
1340  if (!INT_SUCCESS(status))
1341  {
1342  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
1343  return status;
1344  }
1345 
1346  *KernelCtxLogger = 0;
1348  }
1349 
1350  ctxLoggerPtrGva = etwpDbgDataSilo + 2ull * gGuest.WordSize;
1351  LOG("[INFO] Found EtwpDebuggerDataSilo at 0x%016llx -> Logger Ctx Ptr GVA at 0x%016llx\n",
1352  etwpDbgDataSilo, ctxLoggerPtrGva);
1353 
1354  gLoggerCtxState.LoggerGvaInSilo = ctxLoggerPtrGva;
1355 
1356  status = IntKernVirtMemRead(ctxLoggerPtrGva, gGuest.WordSize, &ctxLoggerGva, NULL);
1357  if (!INT_SUCCESS(status))
1358  {
1359  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1360  return status;
1361  }
1362 
1363  LOG("[INFO] Found ctx wmi logger GVA: 0x%016llx\n", ctxLoggerGva);
1364 
1365  *KernelCtxLogger = ctxLoggerGva;
1366  return INT_STATUS_SUCCESS;
1367 }
1368 
1369 
1370 static INTSTATUS
1372  _Out_ QWORD *GetCpuClockGva,
1374  )
1390 {
1391  QWORD wmiLoggerCtx = 0;
1392  QWORD getCpuClock = 0;
1393  INTSTATUS status;
1394  KERNEL_DRIVER *pDrv = NULL;
1395 
1396  status = IntWinInfHookGetCircularCtxLogger(&wmiLoggerCtx);
1397  if (!INT_SUCCESS(status))
1398  {
1399  ERROR("[ERROR] IntWinInfHookGetCircularCtxLogger failed: 0x%08x\n", status);
1400  return status;
1401  }
1402  else if (INT_STATUS_NOT_INITIALIZED_HINT == status)
1403  {
1404  *GetCpuClockGva = 0;
1405 
1406  if (NULL != WmiLoggerCtx)
1407  {
1408  *WmiLoggerCtx = 0;
1409  }
1410 
1411  return status;
1412  }
1413 
1414  if ((wmiLoggerCtx & 1) || (wmiLoggerCtx & WMI_PTR_MASK) == 0)
1415  {
1416  goto _skip_read;
1417  }
1418 
1419  status = IntKernVirtMemRead(wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1420  gGuest.WordSize, &getCpuClock, NULL);
1421  if (!INT_SUCCESS(status))
1422  {
1423  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n",
1424  wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset), status);
1425  return status;
1426  }
1427 
1428  LOG("[INFO] GetCpuClock() at 0x%016llx\n", getCpuClock);
1429 
1430  pDrv = IntDriverFindByAddress(getCpuClock);
1431  if (NULL == pDrv || pDrv->BaseVa != gGuest.KernelVa)
1432  {
1433  WARNING("[WARNING] GetCpuClock does not reside inside the kernel!\n");
1434 
1435  // We have the case where the initial GetCpuClock doesn't reside inside the kernel. We can't do anything
1436  // but send an alert to notify that this is an unusual case. However, by forcing the current getCpuClock to 0
1437  // an alert will be sent while checking the "relocation from 0 to something".
1438  getCpuClock = 0;
1439  }
1440 
1441 _skip_read:
1442  *GetCpuClockGva = getCpuClock;
1443 
1444  if (NULL != WmiLoggerCtx)
1445  {
1446  *WmiLoggerCtx = wmiLoggerCtx;
1447  }
1448 
1449  return INT_STATUS_SUCCESS;
1450 }
1451 
1452 
1453 INTSTATUS
1455  void
1456  )
1477 {
1478  INTSTATUS status;
1479  QWORD getCpuClock = 0;
1480  QWORD wmiLoggerCtx = 0;
1481 
1482  if (__likely(gLoggerCtxState.Initialized))
1483  {
1485  }
1486 
1488  {
1490  }
1491 
1492  // We don't want to init until we have the kernel driver, as on timer, we can wait for swapmems to take place
1493  // and we end up hooking unwanted stuff in some corner cases.
1494  if (__unlikely(NULL == gGuest.KernelDriver))
1495  {
1497  }
1498 
1499  // We don't have to bother retrying to initialize on timer if we failed to init already
1500  if (__unlikely(gLoggerCtxState.FailedToInitialize))
1501  {
1503  }
1504 
1505  status = IntWinInfHookGetWmiLoggerGetCpuClock(&getCpuClock, &wmiLoggerCtx);
1506  if (!INT_SUCCESS(status))
1507  {
1508  ERROR("[ERROR] IntWinInfHookGetWmiLoggerGetCpuClock failed: 0x%08x\n", status);
1509  goto cleanup_and_exit;
1510  }
1511  else if (INT_STATUS_NOT_INITIALIZED_HINT == status)
1512  {
1513  return status;
1514  }
1515 
1516  gLoggerCtxState.WmiLoggerCtx = wmiLoggerCtx;
1517  gLoggerCtxState.CurrentGetCpuClock = getCpuClock;
1518 
1519  if (gGuest.SupportSPP)
1520  {
1521  status = IntWinInfHookSppHookWmiSiloPtr();
1522  if (!INT_SUCCESS(status))
1523  {
1524  ERROR("[ERROR] IntWinInfHookSppHookWmiSiloPtr failed: 0x%08x\n", status);
1525  goto cleanup_and_exit;
1526  }
1527 
1528  // We didn't have yet a WMI_LOGGER_CONTEXT, so basically it is a relocation from 0 to the current one
1530  if (!INT_SUCCESS(status))
1531  {
1532  WARNING("[WARNING] IntWinInfCheckCtxLoggerOnRelocation failed: 0x%08x\n", status);
1533  goto _skip_hooking_spp;
1534  }
1535 
1537  if (!INT_SUCCESS(status))
1538  {
1539  ERROR("[ERROR] IntWinInfHookHookSppWmiGetClock failed: 0x%08x\n", status);
1540  goto cleanup_and_exit;
1541  }
1542 
1543 _skip_hooking_spp:
1544  status = INT_STATUS_SUCCESS;
1545  }
1546  else
1547  {
1548  // First hook the EtwDebuggerDataSilo to verify any change on the WMI_LOGGER_CONTEXT pointer
1549 
1550  if (NULL != gLoggerCtxState.SiloIntegrityObject || NULL != gLoggerCtxState.WmiLoggerIntegrityObject)
1551  {
1552  ERROR("[ERROR] Wmi integrity hook already initialized! %p %p\n",
1553  gLoggerCtxState.SiloIntegrityObject, gLoggerCtxState.WmiLoggerIntegrityObject);
1554 
1556  goto cleanup_and_exit;
1557  }
1558 
1559  status = IntIntegrityAddRegion(gLoggerCtxState.LoggerGvaInSilo,
1560  gGuest.WordSize,
1562  NULL,
1564  TRUE,
1565  &gLoggerCtxState.SiloIntegrityObject);
1566  if (!INT_SUCCESS(status))
1567  {
1568  ERROR("[ERROR] IntIntegrityAddRegion failed: 0x%08x\n", status);
1569  goto cleanup_and_exit;
1570  }
1571 
1572  // We didn't have yet a WMI_LOGGER_CONTEXT, so basically it is a relocation from 0 to the current one
1574  if (!INT_SUCCESS(status))
1575  {
1576  WARNING("[WARNING] IntWinInfCheckCtxLoggerOnRelocation failed: 0x%08x\n", status);
1577  goto _skip_hooking;
1578  }
1579 
1580  // Now hook the currently found WMI_LOGGER_CONTEXT.CpuGetClock
1581  status = IntIntegrityAddRegion(wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1582  gGuest.WordSize,
1584  NULL,
1586  TRUE,
1587  &gLoggerCtxState.WmiLoggerIntegrityObject);
1588  if (!INT_SUCCESS(status))
1589  {
1590  ERROR("[ERROR] IntIntegrityAddRegion failed: 0x%08x\n", status);
1591  goto cleanup_and_exit;
1592  }
1593 
1594 _skip_hooking:
1595  status = INT_STATUS_SUCCESS;
1596  }
1597 
1598  gLoggerCtxState.Initialized = TRUE;
1599 
1600 cleanup_and_exit:
1601  if (!INT_SUCCESS(status))
1602  {
1603  // If we failed to initialize once, we won't magically initialize afterwards...
1604  gLoggerCtxState.FailedToInitialize = TRUE;
1605 
1607  }
1608 
1609  return status;
1610 }
1611 
1612 
1613 INTSTATUS
1615  void
1616  )
1627 {
1628  INTSTATUS status;
1629  BOOLEAN bFailedToInit;
1630 
1631  if (NULL != gLoggerCtxState.WmiLoggerIntegrityObject)
1632  {
1633  status = IntIntegrityRemoveRegion(gLoggerCtxState.WmiLoggerIntegrityObject);
1634  if (!INT_SUCCESS(status))
1635  {
1636  ERROR("[ERROR] IntIntegrityRemoveRegion failed: 0x%08x\n", status);
1637  return status;
1638  }
1639 
1640  gLoggerCtxState.WmiLoggerIntegrityObject = NULL;
1641  }
1642 
1643  if (NULL != gLoggerCtxState.SiloIntegrityObject)
1644  {
1645  status = IntIntegrityRemoveRegion(gLoggerCtxState.SiloIntegrityObject);
1646  if (!INT_SUCCESS(status))
1647  {
1648  ERROR("[ERROR] IntIntegrityRemoveRegion failed: 0x%08x\n", status);
1649  return status;
1650  }
1651 
1652  gLoggerCtxState.SiloIntegrityObject = NULL;
1653  }
1654 
1655  if (NULL != gLoggerCtxState.WmiLoggerHookObject)
1656  {
1657  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.WmiLoggerHookObject, 0);
1658  if (!INT_SUCCESS(status))
1659  {
1660  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1661  return status;
1662  }
1663  }
1664 
1665  if (NULL != gLoggerCtxState.WmiLoggerHookObjectStats)
1666  {
1667  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.WmiLoggerHookObjectStats, 0);
1668  if (!INT_SUCCESS(status))
1669  {
1670  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1671  return status;
1672  }
1673  }
1674 
1675  if (NULL != gLoggerCtxState.SiloHookObject)
1676  {
1677  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.SiloHookObject, 0);
1678  if (!INT_SUCCESS(status))
1679  {
1680  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1681  return status;
1682  }
1683  }
1684 
1685  if (NULL != gLoggerCtxState.SiloHookObjectStats)
1686  {
1687  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.SiloHookObjectStats, 0);
1688  if (!INT_SUCCESS(status))
1689  {
1690  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1691  return status;
1692  }
1693  }
1694 
1695  if (NULL != gLoggerCtxState.FirstSiloWriteHookObject)
1696  {
1697  status = IntHookGvaRemoveHook((HOOK_GVA **)&gLoggerCtxState.FirstSiloWriteHookObject, 0);
1698  if (!INT_SUCCESS(status))
1699  {
1700  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
1701  return status;
1702  }
1703  }
1704 
1705  // Reset the state but keep the FailedToInitialize boolean the same as before.
1706  bFailedToInit = gLoggerCtxState.FailedToInitialize;
1707 
1708  memzero(&gLoggerCtxState, sizeof(gLoggerCtxState));
1709 
1710  gLoggerCtxState.FailedToInitialize = bFailedToInit;
1711 
1712  return INT_STATUS_SUCCESS;
1713 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
#define _In_opt_
Definition: intro_sal.h:16
static INTSTATUS IntWinInfHookHookSppWmiGetClock(void)
Establishes the EPT hook on the WMI_LOGGER_CONTEXT's GetCpuClock field.
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
INTRO_CODEBLOCKS CodeBlocks
Code blocks extracted for the alert.
Definition: intro_types.h:1145
static INTSTATUS IntWinInfHookHandleSiloFirstWrite(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
EPT callback to handle the first write over EtwpDebuggerData, where the pointer of EtwDebuggerDataSil...
#define __unlikely(x)
Definition: common.h:47
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
static INTSTATUS IntWinInfHookSppWmiSiloStatsCallback(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
EPT callback for the SPP region on which exits will always occur on the EtwpDebuggerDataSilo EPT hook...
static INTSTATUS IntWinInfHookWmiGetCpuClockSppCallback(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
EPT callback for writes over WMI_LOGGER_CONTEXT's GetCpuClock.
QWORD IntAlertCoreGetFlags(QWORD ProtectionFlag, INTRO_ACTION_REASON Reason)
Returns the flags for an alert.
Definition: alerts.c:366
void * SiloIntegrityObject
Integrity object for EtwDebuggerDataSilo.WmiCtxLoggerPtr.
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
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:35
INTSTATUS IntKernVirtMemWrite(QWORD KernelGva, DWORD Length, void *Buffer)
Writes data to a guest kernel virtual memory range.
Definition: introcore.c:699
QWORD ZoneFlags
The flags of the modified zone.
Definition: exceptions.h:852
INTSTATUS IntHookObjectDestroy(HOOK_OBJECT_DESCRIPTOR **Object, DWORD Flags)
Destroy an entire hook object. All regions belonging to this object will be removed.
Definition: hook_object.c:357
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
#define _In_
Definition: intro_sal.h:21
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1088
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#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
Infinity hook modifications of WMI_LOGGER_CONTEXT.GetCpuClock.
Definition: intro_types.h:261
#define STATS_EXIT(id)
Definition: stats.h:148
Event structure for integrity violations on monitored structures.
Definition: intro_types.h:1450
INTSTATUS IntIntegrityAddRegion(QWORD VirtualAddress, DWORD Length, INTRO_OBJECT_TYPE Type, void *Context, PFUNC_IntegrityViolationCallback Callback, BOOLEAN CopyContent, void **Descriptor)
Creates an INTEGRITY_REGION object and adds it to the gIntegrityRegions list.
Definition: integrity.c:91
struct _WIN_LOGGER_CTX_STATE WIN_LOGGER_CTX_STATE
QWORD BaseVa
The guest virtual address of the kernel module that owns this driver object.
Definition: drivers.h:41
struct _EXCEPTION_VICTIM_ZONE::@57::@59 WriteInfo
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1087
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
INTRO_OBJECT_TYPE Type
Definition: intro_types.h:1462
int32_t INT32
Definition: intro_types.h:44
BOOLEAN KernelBetaDetections
True if the kernel protection is in beta (log-only) mode.
Definition: guests.h:299
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
#define ALERT_FLAG_ASYNC
If set, the alert was generated in an async manner.
Definition: intro_types.h:637
INTSTATUS IntPeFindFunctionByPatternInBuffer(BYTE *Buffer, DWORD BufferSize, WIN_UNEXPORTED_FUNCTION_PATTERN *Pattern, BOOLEAN IgnoreSectionHint, DWORD *Rva)
Find a function using a pattern.
Definition: winpe.c:3012
int INTSTATUS
The status data type.
Definition: introstatus.h:24
static INTSTATUS IntWinInfHookSppHookWmiSiloPtr(void)
Establishes the EPT hook on EtwpDebuggerDataSilo on the pointer to WMI_LOGGER_CONTEXT.
DWORD TotalLength
The length of the integrity zone.
Definition: exceptions.h:767
DWORD Offset
The offset of the modification.
Definition: exceptions.h:766
INTSTATUS IntIntegrityDeleteRegion(void *Descriptor)
Marks the given integrity region for deletion. It will be removed after calling all the integrity cal...
Definition: integrity.c:348
Rootkit.
Definition: intro_types.h:1033
void * WmiLoggerHookObjectStats
Hook object for SPP statistics on WMI_LOGGER_CONTEXT.GetCpuClock.
Describes a kernel-mode originator.
Definition: exceptions.h:897
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
INSTRUX Instruction
The current instruction, pointed by the guest RIP.
Definition: guests.h:88
WIN_LOGGER_CTX_STATE gLoggerCtxState
EVENT_EPT_VIOLATION Ept
Definition: alerts.h:16
Describes a pattern for a kernel function that is not exported.
Definition: winguest.h:84
INTSTATUS IntWinInfHookUnprotect(void)
Removes the protection against infinity hook.
#define LOG(fmt,...)
Definition: glue.h:61
Describes a kernel driver.
Definition: drivers.h:30
INTSTATUS IntHookGvaSetHook(QWORD Cr3, QWORD Gva, DWORD Length, BYTE Type, void *Callback, void *Context, void *ParentHook, DWORD Flags, HOOK_GVA **GvaHook)
Set a read, write, execute or swap hook on a guest virtual address.
Definition: hook_gva.c:345
void IntAlertFillVersionInfo(INTRO_VIOLATION_HEADER *Header)
Fills version information for an alert.
Definition: alerts.c:327
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1106
#define ZONE_INTEGRITY
Used for integrity zone.
Definition: exceptions.h:702
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1084
EXCEPTION_VICTIM_OBJECT Object
The modified object.
Definition: exceptions.h:849
INTSTATUS IntAlertFillCodeBlocks(QWORD Rip, QWORD Cr3, BOOLEAN Execute, INTRO_CODEBLOCKS *CodeBlocks)
Fills the code blocks pattern for an alert.
Definition: alerts.c:71
QWORD LoggerGvaInSilo
Keeps the address of the pointer to WMI_LOGGER_CONTEXT (basically EtwDebuggerDataSilo + 0x10)...
#define ALERT_FLAG_BETA
If set, the alert is a BETA alert. No action was taken.
Definition: intro_types.h:633
void * SiloHookObject
Hook object for SPP hooking on EtwDebuggerDataSilo - needed for when WMI_LOGGER_CONTEXT is relocated...
void IntAlertEptFillFromKmOriginator(const EXCEPTION_KM_ORIGINATOR *Originator, EVENT_EPT_VIOLATION *EptViolation)
Fills kernel mode originator information inside an EPT alert.
Definition: alerts.c:832
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
BOOLEAN SupportSPP
Set to True if support for SPP was detected.
Definition: guests.h:353
#define _Inout_
Definition: intro_sal.h:20
INTSTATUS IntIntegrityRecalculate(INTEGRITY_REGION *IntegrityRegion)
Recalculates the hash and reads the original content again for a given region.
Definition: integrity.c:242
#define _Out_opt_
Definition: intro_sal.h:30
void IntAlertFillWriteInfo(const EXCEPTION_VICTIM_ZONE *Victim, INTRO_WRITE_INFO *WriteInfo)
Fills the write information for an alert.
Definition: alerts.c:521
#define INT_STATUS_EXCEPTION_BLOCK
Definition: introstatus.h:421
DWORD Size
The size of the modified memory area.
Definition: intro_types.h:1483
Describes an operand value.
Definition: decoder.h:50
#define INT_STATUS_ALREADY_INITIALIZED
Definition: introstatus.h:263
void IntAlertEptFillFromVictimZone(const EXCEPTION_VICTIM_ZONE *Victim, EVENT_EPT_VIOLATION *EptViolation)
Fills the victim information inside an EPT alert.
Definition: alerts.c:868
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
QWORD QwordValues[ND_MAX_REGISTER_SIZE/8]
Definition: decoder.h:57
The modified object is inside an integrity hook.
Definition: exceptions.h:713
static INTSTATUS IntWinInfCheckCtxLoggerOnRelocation(void)
Checks the WMI_LOGGER_CONTEXT when the pointer to the old structure has changed in the EtwDebuggerDat...
#define STATS_ENTER(id)
Definition: stats.h:141
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1085
#define WMI_PTR_MASK
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
void * WmiLoggerIntegrityObject
Integrity object for WMI_LOGGER_CONTEXT.GetCpuClock.
void * SiloHookObjectStats
Hook object for SPP statistics on EtwDebuggerDataSilo.
#define memzero(a, s)
Definition: introcrt.h:35
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:286
INTSTATUS IntExceptGetVictimEpt(void *Context, QWORD Gpa, QWORD Gva, INTRO_OBJECT_TYPE Type, DWORD ZoneFlags, EXCEPTION_VICTIM_ZONE *Victim)
Fills an EXCEPTION_VICTIM_ZONE with relevant information from an EPT violation.
Definition: exceptions.c:742
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:232
INTRO_MODULE Module
The module that modified the translation.
Definition: intro_types.h:1456
struct _EVENT_INTEGRITY_VIOLATION::@295 Victim
QWORD VirtualAddress
The guest virtual address which was modified.
Definition: intro_types.h:1481
#define TRUE
Definition: intro_types.h:30
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1452
union _OPERAND_VALUE::@22 Value
The actual operand value.
#define IS_KERNEL_POINTER_WIN(is64, p)
Checks if a guest virtual address resides inside the Windows kernel address space.
Definition: wddefs.h:76
QWORD StartVirtualAddress
The start address of the integrity zone.
Definition: exceptions.h:765
CHAR SectionHint[8]
Optional section name hint.
Definition: winguest.h:89
INTSTATUS IntExceptGetVictimIntegrity(INTEGRITY_REGION *IntegrityRegion, DWORD *Offset, EXCEPTION_VICTIM_ZONE *Victim)
This function is used to get the information about the modified zone from the integrity region...
#define TRACE(fmt,...)
Definition: glue.h:58
QWORD WmiInteresting
SPP stats for WMI_LOGGER_CONTEXT, containing the number of writes that we are interested into...
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:279
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:62
INTSTATUS IntWinInfHookProtect(void)
This function initializes protection against infinity hook mechanism.
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
INTRO_OBJECT_TYPE Type
The type of the modified object.
Definition: exceptions.h:806
ZONE_TYPE ZoneType
The type of the modified zone.
Definition: exceptions.h:851
INTRO_EXEC_CONTEXT ExecContext
Information about the instruction that triggered the alert.
Definition: intro_types.h:1191
QWORD SiloInteresting
SPP stats for EtwDebuggerDataSilo, containing the number of writes that we are interested into...
static INTSTATUS IntWinInfHookGetCircularCtxLogger(QWORD *KernelCtxLogger)
Fetches the WMI_LOGGER_CONTEXT pointer from EtwDebuggerDataSilo, if possible.
#define INT_STATUS_ALREADY_INITIALIZED_HINT
Definition: introstatus.h:323
#define WARNING(fmt,...)
Definition: glue.h:60
INTSTATUS IntHookGvaRemoveHook(HOOK_GVA **Hook, DWORD Flags)
Remove a GVA hook.
Definition: hook_gva.c:507
Sent when an EPT violation triggers an alert. See EVENT_EPT_VIOLATION.
Definition: intro_types.h:84
#define ALERT_FLAG_NOT_RING0
If set, the alert was triggered in ring 1, 2 or 3.
Definition: intro_types.h:636
Describes the modified zone.
Definition: exceptions.h:847
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:726
WCHAR Name[ALERT_PATH_MAX_LEN]
NULL-terminated string with a human readable description of the modified object.
Definition: intro_types.h:1464
uint32_t DWORD
Definition: intro_types.h:49
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:838
static INTSTATUS IntWinInfHookGetCpuClockIntegrityCallback(void *IntegrityRegion)
Function called whenever a modification has been detected through the integrity mechanism on WMI_LOGG...
BOOLEAN Valid
Set to True if the information in the structure is valid, False otherwise.
Definition: intro_types.h:879
enum _INTRO_ACTION INTRO_ACTION
Event actions.
INTSTATUS IntIntegrityRemoveRegion(void *Descriptor)
Removes an integrity region from the gIntegrityRegions list.
Definition: integrity.c:313
INTRO_WRITE_INFO WriteInfo
The original and the new value.
Definition: intro_types.h:1477
QWORD EtwDbgDataGva
The guest virtual address of EtwpDebuggerData.
QWORD Cr3
The value of the guest CR3 register when the event was generated.
Definition: intro_types.h:884
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
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
static INTSTATUS IntWinInfHookSppViolationCallbackWmiPtrChanged(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
EPT callback for writes over the WMI_LOGGER_CONTEXT pointer inside the EtwDebuggerDataSilo structure...
QWORD CurrentGetCpuClock
Keeps the current, known WMI_LOGGER_CONTEXT.GetCpuClock which is verified on integrity.
EVENT_INTEGRITY_VIOLATION Integrity
Definition: alerts.h:23
static INTSTATUS IntWinInfHookWmiGetCpuClockSppStatsCallback(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
EPT callback for the SPP region on which exits will always occur on the GetCpuClock EPT hook...
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1083
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
KERNEL_DRIVER * IntDriverFindByAddress(QWORD Gva)
Returns the driver in which Gva resides.
Definition: drivers.c:164
BOOLEAN FailedToInitialize
Set if the protection failed to initialize, in order to avoid retrying indefinitely.
QWORD BaseAddress
The guest virtual address at which the monitored integrity region starts.
Definition: intro_types.h:1479
struct _EVENT_INTEGRITY_VIOLATION::@294 Originator
static INTSTATUS IntWinInfHookGetEtwpDebuggerData(QWORD *EtwpDebuggerData)
Finds the EtwpDebuggerData guest virtual address in the guest's kernel.
INTSTATUS IntHookObjectHookRegion(void *Object, QWORD Cr3, QWORD Gla, SIZE_T Length, BYTE Type, void *Callback, void *Context, DWORD Flags, HOOK_REGION_DESCRIPTOR **Region)
Hook a contiguous region of virtual memory inside the provided virtual address space.
Definition: hook_object.c:132
INTSTATUS IntExceptGetOriginatorFromModification(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_KM_ORIGINATOR *Originator)
This function is used for integrity violations to get the information about the kernel-mode originato...
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
#define __likely(x)
Definition: common.h:46
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:837
KERNEL_DRIVER * KernelDriver
Points to the driver object that describes the kernel image.
Definition: guests.h:381
static INTSTATUS IntWinInfHookEptSppHandleWrite(INTRO_ACTION *Action)
Handles a write detected through EPT over WMI_LOGGER_CONTEXT's GetCpuClock function pointer and takes...
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1086
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:57
INTSTATUS IntDecGetWrittenValueFromInstruction(PINSTRUX Instrux, PIG_ARCH_REGS Registers, PBYTE MemoryValue, OPERAND_VALUE *WrittenValue)
Decode a written value from a memory write instruction.
Definition: decoder.c:1861
The action was blocked because there was no exception for it.
Definition: intro_types.h:189
Sent for integrity violation alerts. See EVENT_INTEGRITY_VIOLATION.
Definition: intro_types.h:92
EXCEPTION_VICTIM_INTEGRITY Integrity
Valid if the modified zone is Integrity.
Definition: exceptions.h:862
static INTSTATUS IntWinInfHookEptSppSendAlert(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_KM_ORIGINATOR *Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Sends an alert for an EPT violation. Used only when SPP mechanism is supported.
Holds register state.
Definition: glueiface.h:30
Event structure for EPT violations.
Definition: intro_types.h:1104
static INTSTATUS IntWinInfHookIntegrityHandleWrite(QWORD OldValue, QWORD NewValue, INTEGRITY_REGION *IntegrityRegion, INTRO_ACTION *Action)
Handles a detected modification on GetCpuClock field inside the WMI_LOGGER_CONTEXT structure...
void IntAlertFillWinKmModule(const KERNEL_DRIVER *Driver, INTRO_MODULE *EventModule)
Saves kernel module information inside an alert.
Definition: alerts.c:617
struct _WIN_LOGGER_CTX_STATE * PWIN_LOGGER_CTX_STATE
static INTSTATUS IntWinInfHookIntegritySendAlert(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_KM_ORIGINATOR *Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Sends an integrity alert when a modification on WMI_LOGGER_CONTEXT's GetCpuClock has been detected th...
Write-access hook.
Definition: glueiface.h:299
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 ZONE_WRITE
Used for write violation.
Definition: exceptions.h:698
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:267
INTSTATUS IntHookObjectCreate(DWORD ObjectType, QWORD Cr3, void **Object)
Create a new hook object.
Definition: hook_object.c:81
#define INTRO_OPT_PROT_KM_LOGGER_CONTEXT
Enable protection on WMI_LOGGER_CONTEXT.GetCpuClock used by InfinityHook (Windows only)...
Definition: intro_types.h:469
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:3317
void * WmiLoggerHookObject
Hook object for SPP hooking of WMI_LOGGER_CONTEXT.GetCpuClock.
#define VICTIM_CIRCULAR_KERNEL_CTX_LOGGER
Printable name used for introObjectTypeKmLoggerContext objects.
Definition: intro_types.h:683
static INTSTATUS IntWinInfHookGetWmiLoggerGetCpuClock(QWORD *GetCpuClockGva, QWORD *WmiLoggerCtx)
Gets the GetCpuClock field inside the WMI_LOGGER_CONTEXT structure, and the structure guest virtual a...
#define FALSE
Definition: intro_types.h:34
INTSTATUS IntWinInfHookSiloWmiPtrIntegrityCallback(void *IntegrityRegion)
Integrity callback for modifications over the WMI_LOGGER_CONTEXT pointer inside the EtwDebuggerDataSi...