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  *Action = introGuestNotAllowed;
885  reason = introReasonUnknown;
886 
887  // 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
888  // consider it malicious if it doesn't point somewhere in the kernel space.
889  if (!IS_KERNEL_POINTER_WIN(gGuest.Guest64, NewValue))
890  {
891  *Action = introGuestAllowed;
892  return INT_STATUS_SUCCESS;
893  }
894 
896 
897  if (NULL != IntegrityRegion)
898  {
899  status = IntExceptGetVictimIntegrity(IntegrityRegion, &offset, &victim);
900  if (!INT_SUCCESS(status))
901  {
902  ERROR("[ERROR] Failed getting integrity zone: 0x%08x\n", status);
903  reason = introReasonInternalError;
904  exitAfterInformation = TRUE;
905  }
906  }
907  else
908  {
909  // If we have a NULL IntegrityRegion it means that it is a modification due to a relocation
910  // of the WMI_LOGGER_CONTEXT, so we will complete the victim with some well chosen values.
911 
912  victim.Integrity.Offset = 0;
913  victim.Integrity.StartVirtualAddress = gLoggerCtxState.WmiLoggerCtx +
914  WIN_KM_FIELD(Ungrouped, WmiGetClockOffset);
918  victim.ZoneFlags |= ZONE_WRITE | ZONE_INTEGRITY;
919  victim.WriteInfo.OldValue[0] = OldValue;
920  victim.WriteInfo.NewValue[0] = NewValue;
921  victim.WriteInfo.AccessSize = gGuest.WordSize;
922  }
923 
924  status = IntExceptGetOriginatorFromModification(&victim, &originator);
925  if (status == INT_STATUS_EXCEPTION_BLOCK)
926  {
927  reason = introReasonNoException;
928  exitAfterInformation = TRUE;
929  }
930  else if (!INT_SUCCESS(status))
931  {
932  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
933  reason = introReasonInternalError;
934  exitAfterInformation = TRUE;
935  }
936 
937  if (exitAfterInformation)
938  {
939  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
940  }
941  else
942  {
943  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventIntegrityViolation);
944  }
945 
947 
949  {
950  IntWinInfHookIntegritySendAlert(&victim, &originator, *Action, reason);
951  }
952 
954 
955  return INT_STATUS_SUCCESS;
956 }
957 
958 
959 static INTSTATUS
961  _In_ void *IntegrityRegion
962  )
971 {
972  INTSTATUS status;
973  QWORD getCpuClock = 0;
975 
976  status = IntKernVirtMemRead(gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
978  &getCpuClock,
979  NULL);
980  if (!INT_SUCCESS(status))
981  {
982  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n",
983  gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset), status);
984  return status;
985  }
986 
987  if (gLoggerCtxState.CurrentGetCpuClock == getCpuClock)
988  {
989  IntIntegrityRecalculate(IntegrityRegion);
990  return INT_STATUS_SUCCESS;
991  }
992 
993  status = IntWinInfHookIntegrityHandleWrite(gLoggerCtxState.CurrentGetCpuClock, getCpuClock,
994  IntegrityRegion, &action);
995  if (!INT_SUCCESS(status))
996  {
997  ERROR("[ERROR] IntWinInfHookIntegrityHandleWrite failed: 0x%08x\n", status);
998  return status;
999  }
1000 
1001  if (introGuestAllowed == action)
1002  {
1003  IntIntegrityRecalculate(IntegrityRegion);
1004  gLoggerCtxState.CurrentGetCpuClock = getCpuClock;
1005  }
1006  else if (introGuestNotAllowed == action)
1007  {
1008  LOG("[ROOTKIT] Change of GetCpuClock: New value: 0x%016llx Old value: 0x%016llx\n", getCpuClock,
1009  gLoggerCtxState.CurrentGetCpuClock);
1010 
1011  IntPauseVcpus();
1012 
1013  status = IntKernVirtMemWrite(gLoggerCtxState.WmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1014  gGuest.WordSize,
1015  &gLoggerCtxState.CurrentGetCpuClock);
1016  if (!INT_SUCCESS(status))
1017  {
1018  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
1019  }
1020 
1021  IntResumeVcpus();
1022  }
1023 
1024  return INT_STATUS_SUCCESS;
1025 }
1026 
1027 
1028 INTSTATUS
1030  _In_ void *IntegrityRegion
1031  )
1054 {
1055  INTSTATUS status;
1056  QWORD wmiLoggerCtx = 0;
1057  BOOLEAN shouldSkipHook = FALSE;
1058 
1059  if (NULL != gLoggerCtxState.WmiLoggerIntegrityObject)
1060  {
1061  status = IntIntegrityDeleteRegion(gLoggerCtxState.WmiLoggerIntegrityObject);
1062  if (!INT_SUCCESS(status))
1063  {
1064  ERROR("[ERROR] IntIntegrityRemoveRegion failed: 0x%08x\n", status);
1065  return status;
1066  }
1067 
1068  gLoggerCtxState.WmiLoggerIntegrityObject = NULL;
1069  }
1070 
1071  status = IntKernVirtMemRead(gLoggerCtxState.LoggerGvaInSilo, gGuest.WordSize, &wmiLoggerCtx, NULL);
1072  if (!INT_SUCCESS(status))
1073  {
1074  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1075  return status;
1076  }
1077 
1078  // If the first bit is set, then at the very first call of EtwpLogKernelEvent, the kernel will call
1079  // nt!EtwpCloseLogger on the WMI_LOGGER_CONTEXT, as it means that it is no longer a valid context and is waiting
1080  // to be closed.
1081  shouldSkipHook = (wmiLoggerCtx & 1) || wmiLoggerCtx == 0;
1082 
1083  wmiLoggerCtx &= WMI_PTR_MASK;
1084 
1085  LOG("[INFO] WMI_LOGGER_CONTEXT relocated from 0x%016llx to %016llx (shouldSkipHook: %s)\n",
1086  gLoggerCtxState.WmiLoggerCtx, wmiLoggerCtx, shouldSkipHook ? "TRUE" : "FALSE");
1087 
1088  gLoggerCtxState.WmiLoggerCtx = wmiLoggerCtx;
1089 
1090  // NOTE: (This is handled on the shouldSkipHook case, but it is worth mentioning)
1091  // We may end up here checking some temporary structure which does not contain GetCpuClock at it's known location
1092  // For now we can just let it be this way, we can either check for the bit 1 to be set of the pointer
1093  // but bare in mind that it is always set when the temporary structure is written, but sometimes it is set even if
1094  // there is no temporary structure (and nobody keeps someone to set the first bit anyway).
1095  if (shouldSkipHook)
1096  {
1097  goto _recalculate_and_exit;
1098  }
1099 
1100  // First verify here if the new WMI_LOGGER_CONTEXT.GetCpuClock resides in kernel. If it doesn't, we will send an
1101  // integrity alert and don't add the new integrity region. If we know the real WMI_LOGGER_CONTEXT.GetCpuClock from
1102  // the kernel we will overwrite it and then protect the new region. But if something fails, we shouldn't protect
1103  // the relocated structure.
1105  if (!INT_SUCCESS(status))
1106  {
1107  WARNING("[WARNING] IntWinInfCheckCtxLoggerOnRelocation failed: 0x%08x\n", status);
1108  goto _recalculate_and_exit;
1109  }
1110 
1111  status = IntIntegrityAddRegion(wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1112  gGuest.WordSize,
1114  NULL,
1116  TRUE,
1117  &gLoggerCtxState.WmiLoggerIntegrityObject);
1118  if (!INT_SUCCESS(status))
1119  {
1120  ERROR("[ERROR] IntIntegrityAddRegion failed: 0x%08x\n", status);
1121  return status;
1122  }
1123 
1124 _recalculate_and_exit:
1125  IntIntegrityRecalculate(IntegrityRegion);
1126 
1127  return INT_STATUS_SUCCESS;
1128 }
1129 
1130 
1131 static INTSTATUS
1133  _Out_ QWORD *EtwpDebuggerData
1134  )
1147 {
1148  INTSTATUS status;
1150  {
1151  .SectionHint = ".data",
1152  .Signature =
1153  {
1154  .Length = 5,
1155  .Offset = 0,
1156  .Pattern =
1157  {
1158  0x2c,
1159  0x08,
1160  0x04,
1161  0x38,
1162  0x0c
1163  },
1164  },
1165  };
1166  DWORD etwpDbgDataRva = 0;
1167  QWORD etwpDbgDataGva;
1168 
1169  // If it was already initialized and we retried the initialization, then don't do the whole searching again,
1170  // it won't change
1171  if (0 != gLoggerCtxState.EtwDbgDataGva)
1172  {
1173  *EtwpDebuggerData = gLoggerCtxState.EtwDbgDataGva;
1174  return INT_STATUS_SUCCESS;
1175  }
1176 
1178  &etwpDbgDataRva);
1179  if (!INT_SUCCESS(status))
1180  {
1181  memcpy(pattern.SectionHint, ".rdata", sizeof(".rdata"));
1182 
1184  &pattern, FALSE, &etwpDbgDataRva);
1185  if (!INT_SUCCESS(status))
1186  {
1187  // On some versions of Win7 this structure seems to be randomly put at the end of .text ...
1189  &pattern, TRUE, &etwpDbgDataRva);
1190  if (!INT_SUCCESS(status))
1191  {
1192  ERROR("[ERROR] IntPeFindFunctionByPattern failed: 0x%08x\n", status);
1193  return status;
1194  }
1195  }
1196  }
1197 
1198  // The signature resides at offset 2 from the EtwDebuggerData structure, so we have to decrease 2.
1199  // We cast to signed INT32 the WIN_KM_FIELD as it is received from CAMI as a DWORD. But we want to decrease,
1200  // so treat it as signed.
1201  etwpDbgDataRva += (INT32)WIN_KM_FIELD(Ungrouped, EtwSignatureOffset);
1202 
1203  etwpDbgDataGva = gGuest.KernelVa + etwpDbgDataRva;
1204 
1205  LOG("[INFO] Found EtwpDebuggerData at RVA 0x%08x -> ptr at 0x%016llx\n", etwpDbgDataRva, etwpDbgDataGva);
1206 
1207  gLoggerCtxState.EtwDbgDataGva = etwpDbgDataGva;
1208 
1209  *EtwpDebuggerData = etwpDbgDataGva;
1210 
1211  return INT_STATUS_SUCCESS;
1212 }
1213 
1214 
1215 static INTSTATUS
1217  _In_opt_ void *Context,
1218  _In_ void *Hook,
1219  _In_ QWORD Address,
1220  _Out_ INTRO_ACTION *Action
1221  )
1240 {
1241  INTSTATUS status;
1242  OPERAND_VALUE writtenValue = { 0 };
1243 
1244  UNREFERENCED_PARAMETER(Context);
1245  UNREFERENCED_PARAMETER(Hook);
1246  UNREFERENCED_PARAMETER(Address);
1247 
1248  *Action = introGuestAllowed;
1249 
1250  status = IntDecGetWrittenValueFromInstruction(&gVcpu->Instruction, &gVcpu->Regs, 0, &writtenValue);
1251  if (!INT_SUCCESS(status))
1252  {
1253  ERROR("[ERROR] IntDecGetWrittenValueFromInstruction failed: 0x%08x\n", status);
1254  goto _exit;
1255  }
1256 
1257  if (writtenValue.Value.QwordValues[0] == 0)
1258  {
1259  goto _exit;
1260  }
1261 
1262  // We'll initialize on the next timer tick.
1263  gLoggerCtxState.FailedToInitialize = FALSE;
1264 
1265  status = IntHookGvaRemoveHook((HOOK_GVA **)&gLoggerCtxState.FirstSiloWriteHookObject, 0);
1266  if (!INT_SUCCESS(status))
1267  {
1268  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
1269  return status;
1270  }
1271 
1272 _exit:
1273  return INT_STATUS_SUCCESS;
1274 }
1275 
1276 
1277 static INTSTATUS
1279  _Out_ QWORD *KernelCtxLogger
1280  )
1297 {
1298  INTSTATUS status;
1299  QWORD etwpDbgDataGva = 0;
1300  QWORD etwpDbgDataSiloPtrGva;
1301  QWORD etwpDbgDataSilo = 0;
1302  QWORD ctxLoggerPtrGva;
1303  QWORD ctxLoggerGva = 0;
1304 
1305  status = IntWinInfHookGetEtwpDebuggerData(&etwpDbgDataGva);
1306  if (!INT_SUCCESS(status))
1307  {
1308  ERROR("[ERROR] IntWinInfHookGetEtwpDebuggerData failed: 0x%08x\n", status);
1309  return status;
1310  }
1311 
1312  etwpDbgDataSiloPtrGva = etwpDbgDataGva + WIN_KM_FIELD(Ungrouped, EtwDbgDataSiloOffset);
1313 
1314  status = IntKernVirtMemRead(etwpDbgDataSiloPtrGva, gGuest.WordSize, &etwpDbgDataSilo, NULL);
1315  if (!INT_SUCCESS(status))
1316  {
1317  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n", etwpDbgDataSiloPtrGva, status);
1318  return status;
1319  }
1320 
1321  if (0 == etwpDbgDataSilo)
1322  {
1323  // We mark failed to initialize so that we don't retry again and again as we expect the write hook to take place
1324  gLoggerCtxState.FailedToInitialize = TRUE;
1325 
1326  LOG("[INFO] EtwpDbgDataSilo is 0, will hook for write and init afterwards...\n");
1327 
1329  etwpDbgDataSiloPtrGva,
1330  gGuest.WordSize,
1333  NULL,
1334  NULL,
1335  0,
1336  (HOOK_GVA **)&gLoggerCtxState.FirstSiloWriteHookObject);
1337  if (!INT_SUCCESS(status))
1338  {
1339  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
1340  return status;
1341  }
1342 
1343  *KernelCtxLogger = 0;
1345  }
1346 
1347  ctxLoggerPtrGva = etwpDbgDataSilo + 2ull * gGuest.WordSize;
1348  LOG("[INFO] Found EtwpDebuggerDataSilo at 0x%016llx -> Logger Ctx Ptr GVA at 0x%016llx\n",
1349  etwpDbgDataSilo, ctxLoggerPtrGva);
1350 
1351  gLoggerCtxState.LoggerGvaInSilo = ctxLoggerPtrGva;
1352 
1353  status = IntKernVirtMemRead(ctxLoggerPtrGva, gGuest.WordSize, &ctxLoggerGva, NULL);
1354  if (!INT_SUCCESS(status))
1355  {
1356  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1357  return status;
1358  }
1359 
1360  LOG("[INFO] Found ctx wmi logger GVA: 0x%016llx\n", ctxLoggerGva);
1361 
1362  *KernelCtxLogger = ctxLoggerGva;
1363  return INT_STATUS_SUCCESS;
1364 }
1365 
1366 
1367 static INTSTATUS
1369  _Out_ QWORD *GetCpuClockGva,
1371  )
1387 {
1388  QWORD wmiLoggerCtx = 0;
1389  QWORD getCpuClock = 0;
1390  INTSTATUS status;
1391  KERNEL_DRIVER *pDrv = NULL;
1392 
1393  status = IntWinInfHookGetCircularCtxLogger(&wmiLoggerCtx);
1394  if (!INT_SUCCESS(status))
1395  {
1396  ERROR("[ERROR] IntWinInfHookGetCircularCtxLogger failed: 0x%08x\n", status);
1397  return status;
1398  }
1399  else if (INT_STATUS_NOT_INITIALIZED_HINT == status)
1400  {
1401  *GetCpuClockGva = 0;
1402 
1403  if (NULL != WmiLoggerCtx)
1404  {
1405  *WmiLoggerCtx = 0;
1406  }
1407 
1408  return status;
1409  }
1410 
1411  if ((wmiLoggerCtx & 1) || (wmiLoggerCtx & WMI_PTR_MASK) == 0)
1412  {
1413  goto _skip_read;
1414  }
1415 
1416  status = IntKernVirtMemRead(wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1417  gGuest.WordSize, &getCpuClock, NULL);
1418  if (!INT_SUCCESS(status))
1419  {
1420  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n",
1421  wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset), status);
1422  return status;
1423  }
1424 
1425  LOG("[INFO] GetCpuClock() at 0x%016llx\n", getCpuClock);
1426 
1427  pDrv = IntDriverFindByAddress(getCpuClock);
1428  if (NULL == pDrv || pDrv->BaseVa != gGuest.KernelVa)
1429  {
1430  WARNING("[WARNING] GetCpuClock does not reside inside the kernel!\n");
1431 
1432  // We have the case where the initial GetCpuClock doesn't reside inside the kernel. We can't do anything
1433  // but send an alert to notify that this is an unusual case. However, by forcing the current getCpuClock to 0
1434  // an alert will be sent while checking the "relocation from 0 to something".
1435  getCpuClock = 0;
1436  }
1437 
1438 _skip_read:
1439  *GetCpuClockGva = getCpuClock;
1440 
1441  if (NULL != WmiLoggerCtx)
1442  {
1443  *WmiLoggerCtx = wmiLoggerCtx;
1444  }
1445 
1446  return INT_STATUS_SUCCESS;
1447 }
1448 
1449 
1450 INTSTATUS
1452  void
1453  )
1474 {
1475  INTSTATUS status;
1476  QWORD getCpuClock = 0;
1477  QWORD wmiLoggerCtx = 0;
1478 
1479  if (__likely(gLoggerCtxState.Initialized))
1480  {
1482  }
1483 
1485  {
1487  }
1488 
1489  // We don't want to init until we have the kernel driver, as on timer, we can wait for swapmems to take place
1490  // and we end up hooking unwanted stuff in some corner cases.
1491  if (__unlikely(NULL == gGuest.KernelDriver))
1492  {
1494  }
1495 
1496  // We don't have to bother retrying to initialize on timer if we failed to init already
1497  if (__unlikely(gLoggerCtxState.FailedToInitialize))
1498  {
1500  }
1501 
1502  status = IntWinInfHookGetWmiLoggerGetCpuClock(&getCpuClock, &wmiLoggerCtx);
1503  if (!INT_SUCCESS(status))
1504  {
1505  ERROR("[ERROR] IntWinInfHookGetWmiLoggerGetCpuClock failed: 0x%08x\n", status);
1506  goto cleanup_and_exit;
1507  }
1508  else if (INT_STATUS_NOT_INITIALIZED_HINT == status)
1509  {
1510  return status;
1511  }
1512 
1513  gLoggerCtxState.WmiLoggerCtx = wmiLoggerCtx;
1514  gLoggerCtxState.CurrentGetCpuClock = getCpuClock;
1515 
1516  if (gGuest.SupportSPP)
1517  {
1518  status = IntWinInfHookSppHookWmiSiloPtr();
1519  if (!INT_SUCCESS(status))
1520  {
1521  ERROR("[ERROR] IntWinInfHookSppHookWmiSiloPtr failed: 0x%08x\n", status);
1522  goto cleanup_and_exit;
1523  }
1524 
1525  // We didn't have yet a WMI_LOGGER_CONTEXT, so basically it is a relocation from 0 to the current one
1527  if (!INT_SUCCESS(status))
1528  {
1529  WARNING("[WARNING] IntWinInfCheckCtxLoggerOnRelocation failed: 0x%08x\n", status);
1530  goto _skip_hooking_spp;
1531  }
1532 
1534  if (!INT_SUCCESS(status))
1535  {
1536  ERROR("[ERROR] IntWinInfHookHookSppWmiGetClock failed: 0x%08x\n", status);
1537  goto cleanup_and_exit;
1538  }
1539 
1540 _skip_hooking_spp:
1541  status = INT_STATUS_SUCCESS;
1542  }
1543  else
1544  {
1545  // First hook the EtwDebuggerDataSilo to verify any change on the WMI_LOGGER_CONTEXT pointer
1546 
1547  if (NULL != gLoggerCtxState.SiloIntegrityObject || NULL != gLoggerCtxState.WmiLoggerIntegrityObject)
1548  {
1549  ERROR("[ERROR] Wmi integrity hook already initialized! %p %p\n",
1550  gLoggerCtxState.SiloIntegrityObject, gLoggerCtxState.WmiLoggerIntegrityObject);
1551 
1553  goto cleanup_and_exit;
1554  }
1555 
1556  status = IntIntegrityAddRegion(gLoggerCtxState.LoggerGvaInSilo,
1557  gGuest.WordSize,
1559  NULL,
1561  TRUE,
1562  &gLoggerCtxState.SiloIntegrityObject);
1563  if (!INT_SUCCESS(status))
1564  {
1565  ERROR("[ERROR] IntIntegrityAddRegion failed: 0x%08x\n", status);
1566  goto cleanup_and_exit;
1567  }
1568 
1569  // We didn't have yet a WMI_LOGGER_CONTEXT, so basically it is a relocation from 0 to the current one
1571  if (!INT_SUCCESS(status))
1572  {
1573  WARNING("[WARNING] IntWinInfCheckCtxLoggerOnRelocation failed: 0x%08x\n", status);
1574  goto _skip_hooking;
1575  }
1576 
1577  // Now hook the currently found WMI_LOGGER_CONTEXT.CpuGetClock
1578  status = IntIntegrityAddRegion(wmiLoggerCtx + WIN_KM_FIELD(Ungrouped, WmiGetClockOffset),
1579  gGuest.WordSize,
1581  NULL,
1583  TRUE,
1584  &gLoggerCtxState.WmiLoggerIntegrityObject);
1585  if (!INT_SUCCESS(status))
1586  {
1587  ERROR("[ERROR] IntIntegrityAddRegion failed: 0x%08x\n", status);
1588  goto cleanup_and_exit;
1589  }
1590 
1591 _skip_hooking:
1592  status = INT_STATUS_SUCCESS;
1593  }
1594 
1595  gLoggerCtxState.Initialized = TRUE;
1596 
1597 cleanup_and_exit:
1598  if (!INT_SUCCESS(status))
1599  {
1600  // If we failed to initialize once, we won't magically initialize afterwards...
1601  gLoggerCtxState.FailedToInitialize = TRUE;
1602 
1604  }
1605 
1606  return status;
1607 }
1608 
1609 
1610 INTSTATUS
1612  void
1613  )
1624 {
1625  INTSTATUS status;
1626  BOOLEAN bFailedToInit;
1627 
1628  if (NULL != gLoggerCtxState.WmiLoggerIntegrityObject)
1629  {
1630  status = IntIntegrityRemoveRegion(gLoggerCtxState.WmiLoggerIntegrityObject);
1631  if (!INT_SUCCESS(status))
1632  {
1633  ERROR("[ERROR] IntIntegrityRemoveRegion failed: 0x%08x\n", status);
1634  return status;
1635  }
1636 
1637  gLoggerCtxState.WmiLoggerIntegrityObject = NULL;
1638  }
1639 
1640  if (NULL != gLoggerCtxState.SiloIntegrityObject)
1641  {
1642  status = IntIntegrityRemoveRegion(gLoggerCtxState.SiloIntegrityObject);
1643  if (!INT_SUCCESS(status))
1644  {
1645  ERROR("[ERROR] IntIntegrityRemoveRegion failed: 0x%08x\n", status);
1646  return status;
1647  }
1648 
1649  gLoggerCtxState.SiloIntegrityObject = NULL;
1650  }
1651 
1652  if (NULL != gLoggerCtxState.WmiLoggerHookObject)
1653  {
1654  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.WmiLoggerHookObject, 0);
1655  if (!INT_SUCCESS(status))
1656  {
1657  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1658  return status;
1659  }
1660  }
1661 
1662  if (NULL != gLoggerCtxState.WmiLoggerHookObjectStats)
1663  {
1664  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.WmiLoggerHookObjectStats, 0);
1665  if (!INT_SUCCESS(status))
1666  {
1667  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1668  return status;
1669  }
1670  }
1671 
1672  if (NULL != gLoggerCtxState.SiloHookObject)
1673  {
1674  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.SiloHookObject, 0);
1675  if (!INT_SUCCESS(status))
1676  {
1677  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1678  return status;
1679  }
1680  }
1681 
1682  if (NULL != gLoggerCtxState.SiloHookObjectStats)
1683  {
1684  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLoggerCtxState.SiloHookObjectStats, 0);
1685  if (!INT_SUCCESS(status))
1686  {
1687  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1688  return status;
1689  }
1690  }
1691 
1692  if (NULL != gLoggerCtxState.FirstSiloWriteHookObject)
1693  {
1694  status = IntHookGvaRemoveHook((HOOK_GVA **)&gLoggerCtxState.FirstSiloWriteHookObject, 0);
1695  if (!INT_SUCCESS(status))
1696  {
1697  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
1698  return status;
1699  }
1700  }
1701 
1702  // Reset the state but keep the FailedToInitialize boolean the same as before.
1703  bFailedToInit = gLoggerCtxState.FailedToInitialize;
1704 
1705  memzero(&gLoggerCtxState, sizeof(gLoggerCtxState));
1706 
1707  gLoggerCtxState.FailedToInitialize = bFailedToInit;
1708 
1709  return INT_STATUS_SUCCESS;
1710 }
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:1263
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:64
#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:37
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:898
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:1199
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
Infinity hook modifications of WMI_LOGGER_CONTEXT.GetCpuClock.
Definition: intro_types.h:264
#define STATS_EXIT(id)
Definition: stats.h:160
Event structure for integrity violations on monitored structures.
Definition: intro_types.h:1572
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
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1198
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
INTRO_OBJECT_TYPE Type
Definition: intro_types.h:1589
int32_t INT32
Definition: intro_types.h:44
BOOLEAN KernelBetaDetections
True if the kernel protection is in beta (log-only) mode.
Definition: guests.h:303
struct _EVENT_INTEGRITY_VIOLATION::@304 Victim
#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:675
INTSTATUS IntPeFindFunctionByPatternInBuffer(BYTE *Buffer, DWORD BufferSize, WIN_UNEXPORTED_FUNCTION_PATTERN *Pattern, BOOLEAN IgnoreSectionHint, DWORD *Rva)
Find a function using a pattern.
Definition: winpe.c:3044
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 Offset
The offset of the modification.
Definition: exceptions.h:803
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:1144
void * WmiLoggerHookObjectStats
Hook object for SPP statistics on WMI_LOGGER_CONTEXT.GetCpuClock.
Describes a kernel-mode originator.
Definition: exceptions.h:943
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:85
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:1217
#define ZONE_INTEGRITY
Used for integrity zone.
Definition: exceptions.h:738
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
EXCEPTION_VICTIM_OBJECT Object
The modified object.
Definition: exceptions.h:895
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:671
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:357
#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:1618
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:749
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:153
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1196
#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:290
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:236
INTRO_MODULE Module
The module that modified the monitored region.
Definition: intro_types.h:1578
QWORD VirtualAddress
The guest virtual address which was modified.
Definition: intro_types.h:1616
#define TRUE
Definition: intro_types.h:30
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1574
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:802
CHAR SectionHint[8]
Optional section name hint.
Definition: winguest.h:90
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:283
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
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:367
INTRO_OBJECT_TYPE Type
The type of the modified object.
Definition: exceptions.h:852
ZONE_TYPE ZoneType
The type of the modified zone.
Definition: exceptions.h:897
INTRO_EXEC_CONTEXT ExecContext
Information about the instruction that triggered the alert.
Definition: intro_types.h:1309
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:674
Describes the modified zone.
Definition: exceptions.h:893
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
struct _EXCEPTION_VICTIM_ZONE::@58::@60 WriteInfo
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:740
WCHAR Name[ALERT_PATH_MAX_LEN]
NULL-terminated string with a human readable description of the modified object.
Definition: intro_types.h:1591
uint32_t DWORD
Definition: intro_types.h:49
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:852
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:965
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
Definition: intro_types.h:1607
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: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
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...
struct _EVENT_INTEGRITY_VIOLATION::@302 Originator
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1194
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:1614
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:63
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:851
KERNEL_DRIVER * KernelDriver
Points to the driver object that describes the kernel image.
Definition: guests.h:385
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:1197
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
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:908
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:1215
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:734
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271
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:485
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
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:751
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...