Bitdefender Hypervisor Memory Introspection
wintoken.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "wintoken.h"
6 #include "winprocess.h"
7 #include "winprocesshp.h"
8 #include "decoder.h"
9 #include "hook.h"
10 #include "drivers.h"
11 #include "alerts.h"
12 #include "winpool.h"
13 #include "gpacache.h"
14 
49 
50 
52 
53 
54 static INTSTATUS
56  _In_opt_ void *Context,
57  _In_ void *Hook,
58  _In_ QWORD Address,
59  _Out_ INTRO_ACTION *Action
60  );
61 
62 static INTSTATUS
64  _In_opt_ void *Context,
65  _In_ QWORD VirtualAddress,
66  _In_ QWORD OldEntry,
67  _In_ QWORD NewEntry,
68  _In_ QWORD OldPageSize,
69  _In_ QWORD NewPageSize
70  );
71 
72 static void
74  _In_ EXCEPTION_KM_ORIGINATOR *Originator,
76  _In_ INTRO_ACTION Action,
78  )
87 {
89  INTSTATUS status;
90 
91  memzero(pEpt, sizeof(*pEpt));
92 
93  pEpt->Header.Action = Action;
94  pEpt->Header.Reason = Reason;
96 
98 
99  IntAlertEptFillFromKmOriginator(Originator, pEpt);
100  IntAlertEptFillFromVictimZone(Victim, pEpt);
101 
103 
104  if (Victim->Object.WinProc->SystemProcess)
105  {
107  }
108 
109  IntAlertFillWinProcessByCr3(Victim->Object.WinProc->Cr3, &pEpt->Header.CurrentProcess);
110 
111  IntAlertFillCodeBlocks(Originator->Original.Rip, gVcpu->Regs.Cr3, FALSE, &pEpt->CodeBlocks);
113 
115 
116  status = IntNotifyIntroEvent(introEventEptViolation, pEpt, sizeof(*pEpt));
117  if (!INT_SUCCESS(status))
118  {
119  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
120  }
121 }
122 
123 
124 static void
126  _In_ EXCEPTION_VICTIM_ZONE *Victim,
127  _In_ INTRO_ACTION Action,
129  )
137 {
138  INTSTATUS status;
139  EVENT_INTEGRITY_VIOLATION *pIntViol;
140 
141  pIntViol = &gAlert.Integrity;
142  memzero(pIntViol, sizeof(*pIntViol));
143 
145 
146  // Force de-activation of ALERT_FLAG_NOT_RING0. We're always in ring0.
147  pIntViol->Header.Flags &= ~ALERT_FLAG_NOT_RING0;
148 
149  pIntViol->Header.Flags |= ALERT_FLAG_ASYNC;
150 
151  pIntViol->Header.Action = Action;
152  pIntViol->Header.Reason = Reason;
153  pIntViol->Header.MitreID = idAccessToken;
154 
155  pIntViol->Victim.Type = Victim->Object.Type;
156 
157  memcpy(pIntViol->Victim.Name, VICTIM_TOKEN_PRIVILEGES, sizeof(VICTIM_TOKEN_PRIVILEGES));
158 
159  IntAlertFillWinProcess(Victim->Object.Process, &pIntViol->Originator.Process);
160  IntAlertFillWinProcess(Victim->Object.Process, &pIntViol->Victim.Process);
161  IntAlertFillWinProcess(Victim->Object.Process, &pIntViol->Header.CurrentProcess);
162 
163  IntAlertFillWriteInfo(Victim, &pIntViol->WriteInfo);
164 
166 
167  // We can't know from what CPU the write was, but we know where the integrity check failed
168  pIntViol->Header.CpuContext.Valid = FALSE;
169 
170  IntAlertFillVersionInfo(&pIntViol->Header);
171 
172  status = IntNotifyIntroEvent(introEventIntegrityViolation, pIntViol, sizeof(*pIntViol));
173  if (!INT_SUCCESS(status))
174  {
175  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
176  }
177 }
178 
179 static BOOLEAN
181  _In_ WIN_PROCESS_OBJECT *Process,
182  _In_ QWORD NewTokenPtr
183  )
203 {
204  void *pPage = NULL;
205  INTSTATUS status;
206  const POOL_HEADER *ph;
207  DWORD blockSize, desiredBlockSize;
208  BOOLEAN ret;
209 
210  // By default protect everything through EPT if we have SPP.
211  if (gGuest.SupportSPP)
212  {
213  return TRUE;
214  }
215 
216  // If the process was static detected there's virtually no chance that the token was allocated
217  // through our hook. Bail out the following checks since they would most likely not pass on static
218  // detected processes.
219  if (Process->StaticDetected)
220  {
221  return FALSE;
222  }
223 
224  // The process is not static detected, now verify that the token is really allocated through our hook handler
225  // and the allocation has been forced to PAGE_SIZE for performance improvement. If the block size is not 0xff
226  // (meaning that the allocation + sizeof(pool_header) = 1 page), it means that the allocation was not forced
227  // and we should not protect through EPT this token, as it would most probably induce a high performance impact.
228  // We have previously observed this behavior on the "registry" process, where the token assigned to that process
229  // is allocated at boot time, long before we initialize our hooks.
230  status = IntVirtMemMap(NewTokenPtr & PAGE_MASK, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pPage);
231  if (!INT_SUCCESS(status))
232  {
233  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", NewTokenPtr, status);
234  return FALSE;
235  }
236 
237  ph = IntWinPoolGetPoolHeaderInPage(pPage, NewTokenPtr & PAGE_OFFSET, WIN_POOL_TAG_TOKE);
238  if (NULL == ph)
239  {
240  // Try with WIN_POOL_TAG_TOKE2, as on windows 7, the most significant bit is set if the allocation is
241  // considered "Protected".
242  ph = IntWinPoolGetPoolHeaderInPage(pPage, NewTokenPtr & PAGE_OFFSET, WIN_POOL_TAG_TOKE2);
243  if (NULL == ph)
244  {
245  ERROR("[ERROR] IntWinPoolGetPoolHeaderInPage did not found a valid pool header!\n");
246  ret = FALSE;
247  goto _exit;
248  }
249  }
250 
251  blockSize = gGuest.Guest64 ? ph->Header64.BlockSize : ph->Header32.BlockSize;
252  desiredBlockSize = gGuest.Guest64 ? 0xFF : 0x1FF;
253 
254  if (blockSize != desiredBlockSize)
255  {
256  // This means that the token didn't go through our allocation hook. That's a pity, but we'll detect it through
257  // integrity if anything goes wrong, or if the token changes to one of the tokens which were forced
258  // to page size.
259  ret = FALSE;
260  goto _exit;
261  }
262 
263  ret = TRUE;
264 
265 _exit:
266  if (NULL != pPage)
267  {
268  IntVirtMemUnmap(&pPage);
269  }
270 
271  return ret;
272 }
273 
274 
275 static INTSTATUS
277  _In_ WIN_PROCESS_OBJECT *Process,
278  _In_ QWORD NewTokenPtr
279  )
290 {
291  INTSTATUS status;
292 
293  if (NULL != Process->TokenHook)
294  {
295  status = IntHookGvaRemoveHook((HOOK_GVA **)&Process->TokenHook, 0);
296  if (!INT_SUCCESS(status))
297  {
298  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
299  return status;
300  }
301  }
302 
303  if (NULL != Process->TokenSwapHook)
304  {
305  status = IntHookGvaRemoveHook((HOOK_GVA **)&Process->TokenSwapHook, 0);
306  if (!INT_SUCCESS(status))
307  {
308  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
309  return status;
310  }
311  }
312 
313  if (IntWinTokenPrivsShouldHook(Process, NewTokenPtr))
314  {
316  NewTokenPtr & PAGE_MASK,
317  PAGE_SIZE,
320  Process,
321  NULL,
322  0,
323  (HOOK_GVA **)&Process->TokenHook);
324  if (!INT_SUCCESS(status))
325  {
326  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
327  return status;
328  }
329 
331  NewTokenPtr & PAGE_MASK,
332  PAGE_SIZE,
335  Process,
336  NULL,
338  (HOOK_GVA **)&Process->TokenSwapHook);
339  if (!INT_SUCCESS(status))
340  {
341  ERROR("[ERROR] IntHookGvaSetHook failed: 0x%08x\n", status);
342  return status;
343  }
344  }
345  else
346  {
347  TRACE("[INFO] Token at 0x%016llx is not allocated through our hook - we'll protect it only with integrity!\n",
348  NewTokenPtr);
349  }
350 
351  return INT_STATUS_SUCCESS;
352 }
353 
354 
355 static INTSTATUS
357  _In_opt_ void *Context,
358  _In_ QWORD VirtualAddress,
359  _In_ QWORD OldEntry,
360  _In_ QWORD NewEntry,
361  _In_ QWORD OldPageSize,
362  _In_ QWORD NewPageSize
363  )
386 {
387  INTSTATUS status;
388  WIN_PROCESS_OBJECT *pProc = Context;
389  QWORD newTokenPtr = 0;
390 
391  if (NULL == pProc)
392  {
393  return INT_STATUS_NOT_FOUND;
394  }
395 
397 
398  status = IntKernVirtMemFetchWordSize(pProc->EprocessAddress + WIN_KM_FIELD(Process, Token), &newTokenPtr);
399  if (!INT_SUCCESS(status))
400  {
401  ERROR("[ERROR] IntKernVirtMemFetchWordSize failed: 0x%08x\n", status);
402 
404 
405  return status;
406  }
407 
408  newTokenPtr = EX_FAST_REF_TO_PTR(gGuest.Guest64, newTokenPtr);
409 
410  if (newTokenPtr != pProc->OriginalTokenPtr)
411  {
412  LOG("[INFO] Token has been changed during translation modification of 0x%016llx [0x%016llx -> 0x%016llx], "
413  "[0x%016llx -> 0x%016llx]: old = 0x%016llx, new = 0x%016llx\n",
414  VirtualAddress, OldEntry, NewEntry, OldPageSize, NewPageSize, pProc->OriginalTokenPtr, newTokenPtr);
415 
416  IntWinTokenProtectPrivsInternal(pProc, newTokenPtr);
417 
418  // Check integrity if token changed.
420  }
421 
423 
424  return INT_STATUS_SUCCESS;
425 }
426 
427 
428 static INTSTATUS
430  _In_opt_ void *Context,
431  _In_ void *Hook,
432  _In_ QWORD Address,
433  _Out_ INTRO_ACTION *Action
434  )
447 {
448  WIN_PROCESS_OBJECT *pProc = Context;
449  QWORD newTokenPtr = 0;
450  INTSTATUS status;
451  INTRO_ACTION_REASON reason;
452  EXCEPTION_KM_ORIGINATOR originator = { 0 };
453  EXCEPTION_VICTIM_ZONE victim = { 0 };
454  BOOLEAN exitAfterInformation = FALSE;
455  DWORD privsOffsetInPage, writeOffset;
456 
458 
459  *Action = introGuestAllowed;
460 
461  if (NULL == pProc)
462  {
463  return INT_STATUS_NOT_FOUND;
464  }
465 
467 
468  status = IntKernVirtMemFetchWordSize(pProc->EprocessAddress + WIN_KM_FIELD(Process, Token), &newTokenPtr);
469  if (!INT_SUCCESS(status))
470  {
471  ERROR("[ERROR] IntKernVirtMemFetchWordSize failed: 0x%08x\n", status);
472 
474 
475  return status;
476  }
477 
478  newTokenPtr = EX_FAST_REF_TO_PTR(gGuest.Guest64, newTokenPtr);
479 
480  if (newTokenPtr != pProc->OriginalTokenPtr)
481  {
482  IntWinTokenProtectPrivsInternal(pProc, newTokenPtr);
483 
484  // Check integrity if token changed.
486 
487  *Action = introGuestAllowed;
489  return INT_STATUS_SUCCESS;
490  }
491 
493 
494  privsOffsetInPage = (pProc->OriginalTokenPtr & PAGE_OFFSET) + WIN_KM_FIELD(Token, Privs);
495  writeOffset = Address & PAGE_OFFSET;
496 
497  // The write [woff, woff + access_size) was outside the ranges [privoff, privoff + 3 * sizeof(QWORD)),
498  // basically the interval [a, b) cannot intersect with [x, y) if either b <= x or a >= y. Since we already verified
499  // if the token has changed, and the write is not over the Privileges field, we will just return in this case.
500  if (writeOffset + gVcpu->AccessSize <= privsOffsetInPage || writeOffset >= privsOffsetInPage + 3 * sizeof(QWORD))
501  {
502  *Action = introGuestAllowed;
503  return INT_STATUS_SUCCESS;
504  }
505 
508 
509  *Action = introGuestNotAllowed;
510  reason = introReasonUnknown;
511 
512  status = IntExceptKernelGetOriginator(&originator, 0);
513  if (status == INT_STATUS_EXCEPTION_BLOCK)
514  {
515  reason = introReasonNoException;
516  exitAfterInformation = TRUE;
517  }
518  else if (!INT_SUCCESS(status))
519  {
520  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
521  reason = introReasonInternalError;
522  exitAfterInformation = TRUE;
523  }
524 
525  status = IntExceptGetVictimEpt(Context,
526  Address,
527  IntHookGetGlaFromGpaHook(Hook, Address),
529  ZONE_WRITE,
530  &victim);
531  if (!INT_SUCCESS(status))
532  {
533  reason = introReasonInternalError;
534  ERROR("[ERROR] Failed getting zone details: 0x%08x\n", status);
535  goto _exit_exceptions;
536  }
537 
538  if (exitAfterInformation)
539  {
540  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
541  }
542  else
543  {
544  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventEptViolation);
545  }
546 
547 _exit_exceptions:
549 
551  {
552  IntWinTokenPrivsSendEptAlert(&originator, &victim, *Action, reason);
553  }
554 
556 
558 
559  return INT_STATUS_SUCCESS;
560 }
561 
562 
563 static INTSTATUS
565  _In_ WIN_PROCESS_OBJECT *Process,
566  _Out_ QWORD *OldValue,
567  _Out_ QWORD *NewValue
568  )
584 {
585  INTSTATUS status;
586  VA_TRANSLATION tr = { 0 };
587  QWORD newValue = 0, oldValue = 0;
588 
589  status = IntTranslateVirtualAddressEx(Process->EprocessAddress + WIN_KM_FIELD(Process, Token),
592  &tr);
593  if (!INT_SUCCESS(status))
594  {
595  ERROR("[ERROR] IntTranslateVirtualAddressEx failed: 0x%08x\n", status);
596  return status;
597  }
598 
599  if (0 == (tr.Flags & PT_P))
600  {
602  }
603 
604  // Read the token.
606  if (!INT_SUCCESS(status))
607  {
608  ERROR("[ERROR] IntGpaCacheFetchAndAdd failed: 0x%08x\n", status);
609  return status;
610  }
611 
612  // Ignore ref count.
613  newValue = EX_FAST_REF_TO_PTR(gGuest.Guest64, newValue);
614  oldValue = EX_FAST_REF_TO_PTR(gGuest.Guest64, Process->OriginalTokenPtr);
615 
616  *OldValue = oldValue;
617  *NewValue = newValue;
618 
619  return INT_STATUS_SUCCESS;
620 }
621 
622 
623 _Success_(return == TRUE)
624 BOOLEAN
626  _In_ WIN_PROCESS_OBJECT *Process,
627  _In_ BOOLEAN Check,
628  _Out_opt_ WIN_PROCESS_OBJECT **FromProcess,
629  _Out_opt_ QWORD *OldValue,
630  _Out_opt_ QWORD *NewValue
631  )
646 {
647  INTSTATUS status = INT_STATUS_SUCCESS;
648  QWORD newValue = 0, oldValue = 0;
649  WIN_PROCESS_OBJECT *pProc = Process;
650 
651  status = IntWinTokenFetchTokenAddress(pProc, &oldValue, &newValue);
652  if (!INT_SUCCESS(status))
653  {
654  ERROR("[ERROR] IntWinTokenFetchTokenAddress failed: 0x%08x\n", status);
655  return FALSE;
656  }
657 
658  pProc->OriginalTokenPtr = newValue;
659 
660  // the pointer should never change
661  if (Check || ((newValue != oldValue) && (0 != newValue)))
662  {
663  WIN_PROCESS_OBJECT* pProc2 = NULL;
664  LIST_ENTRY* list;
665  BOOLEAN bFound;
666 
667  // Check if the new token belongs to another process; if it doesn't, we can bail out, it is most likely
668  // a legitimate action.
669  bFound = FALSE;
670  list = gWinProcesses.Flink;
671  while (list != &gWinProcesses)
672  {
673  pProc2 = CONTAINING_RECORD(list, WIN_PROCESS_OBJECT, Link);
674 
675  list = list->Flink;
676 
677  // Ignore the current process, as it will obviously have this token already.
678  if (pProc2 == pProc)
679  {
680  continue;
681  }
682 
683  if (EX_FAST_REF_TO_PTR(gGuest.Guest64, pProc2->OriginalTokenPtr) == newValue)
684  {
685  bFound = TRUE;
686  break;
687  }
688  }
689 
690  if (!bFound || (NULL == pProc2))
691  {
692  goto _bail_out;
693  }
694 
695  if (NULL != NewValue)
696  {
697  *NewValue = newValue;
698  }
699 
700  if (NULL != OldValue)
701  {
702  *OldValue = oldValue;
703  }
704 
705  if (NULL != FromProcess)
706  {
707  *FromProcess = pProc2;
708  }
709 
710  return TRUE;
711  }
712 
713 _bail_out:
714  return FALSE;
715 }
716 
717 
718 INTSTATUS
720  _In_ WIN_PROCESS_OBJECT *Process
721 )
729 {
730  QWORD newValue;
731  QWORD oldValue;
732  WIN_PROCESS_OBJECT *pProc;
733  WIN_PROCESS_OBJECT *pProc2;
734 
735  if (NULL == Process)
736  {
738  }
739 
740  pProc = Process;
741 
742  if (IntWinTokenPtrIsStolen(pProc, FALSE, &pProc2, &oldValue, &newValue))
743  {
744  EVENT_INTEGRITY_VIOLATION* pIntViolation;
745  INTSTATUS status;
746 
747  IntPauseVcpus();
748 
749  LOG("[INTEGRITY VIOLATION] Token pointer was modified (%llx -> %llx): "
750  "process %llx (%d / %s), token stolen from process %llx (%d / %s)\n",
751  oldValue, newValue, pProc->EprocessAddress, pProc->Pid,
752  pProc->Name, pProc2->EprocessAddress, pProc2->Pid, pProc2->Name);
754  {
755  LOG("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (B) MALWARE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
756  }
757  else
758  {
759  LOG("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ MALWARE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
760  }
761 
762  // We can't change the pointer back to the original value because that would result in a bug check
763  // almost all the time so save the new value in order to avoid alert spamming and to catch future changes.
764 
765  pIntViolation = &gAlert.Integrity;
766  memzero(pIntViolation, sizeof(*pIntViolation));
767 
768  pIntViolation->Header.Action = introGuestAllowed;
769  pIntViolation->Header.Reason = introReasonNoException;
770  pIntViolation->Header.MitreID = idAccessToken;
771 
772  pIntViolation->Header.CpuContext.Valid = FALSE;
773 
774  IntAlertFillWinProcess(pProc, &pIntViolation->Header.CurrentProcess);
775  IntAlertFillWinProcess(pProc, &pIntViolation->Originator.Process);
776  IntAlertFillWinProcess(pProc2, &pIntViolation->Victim.Process);
777 
779  {
780  pIntViolation->Header.Flags |= ALERT_FLAG_BETA;
781  }
782  pIntViolation->Header.Flags |= ALERT_FLAG_ASYNC;
783 
784  pIntViolation->Victim.Type = introObjectTypeTokenPtr;
785  memcpy(pIntViolation->Victim.Name, VICTIM_PROCESS_TOKEN, sizeof(VICTIM_PROCESS_TOKEN));
786 
787  pIntViolation->Size = gGuest.WordSize;
788  pIntViolation->BaseAddress = pProc2->EprocessAddress + WIN_KM_FIELD(Process, Token);
789  pIntViolation->VirtualAddress = pProc->EprocessAddress + WIN_KM_FIELD(Process, Token);
790 
791  pIntViolation->WriteInfo.Size = gGuest.WordSize;
792  pIntViolation->WriteInfo.OldValue[0] = oldValue;
793  pIntViolation->WriteInfo.NewValue[0] = newValue;
794 
795  IntAlertFillVersionInfo(&pIntViolation->Header);
796 
797  status = IntNotifyIntroEvent(introEventIntegrityViolation, pIntViolation, sizeof(*pIntViolation));
798  if (!INT_SUCCESS(status))
799  {
800  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
801  }
802 
803  IntResumeVcpus();
804  }
805 
806  return INT_STATUS_SUCCESS;
807 }
808 
809 
810 INTSTATUS
812  _In_ WIN_PROCESS_OBJECT *Process,
813  _In_ QWORD TokenPtr,
814  _In_ BOOLEAN IntegrityCheck,
815  _Out_ BOOLEAN *PresentIncreased,
816  _Out_ BOOLEAN *EnabledIncreased,
817  _Out_opt_ QWORD *Present,
818  _Out_opt_ QWORD *Enabled
819  )
854 {
855  INTSTATUS status;
856  QWORD privs[2];
857  QWORD present, enabled;
858 
859  if (NULL == Process)
860  {
862  }
863 
864  if (NULL == PresentIncreased)
865  {
867  }
868 
869  if (NULL == EnabledIncreased)
870  {
872  }
873 
874  *PresentIncreased = FALSE;
875  *EnabledIncreased = FALSE;
876 
877  // Always check the new token, as the old token may have been used for something else in the meantime.
878  status = IntVirtMemRead(TokenPtr + WIN_KM_FIELD(Token, Privs), 2 * sizeof(QWORD), gGuest.Mm.SystemCr3, privs, NULL);
879  if (!INT_SUCCESS(status))
880  {
881  // The token is probably not present in memory, nothing we can do for now...
882  return status;
883  }
884 
885  present = privs[0];
886  enabled = privs[1];
887 
888  // We might have not fetched the enabled/present beforehand, so don't bother checking.
889  if (Process->OriginalEnabledPrivs == 0 && Process->OriginalPresentPrivs == 0)
890  {
891  goto _skip_checks;
892  }
893 
894  // Check if there is any bit which is set in present and not set in original present.
895  if ((present & (~Process->OriginalPresentPrivs)) != 0)
896  {
897  *PresentIncreased = TRUE;
898  }
899 
900  // All bits in enabled must also be set in present, if any bit is not set in present => it shouldn't
901  // have that privilege. Note that if we previously didn't give a detection due to the fact that only one bit was
902  // changed, we will give a detection now if there is any bit which is set in enabled but not set in present.
903  if ((enabled != Process->OriginalEnabledPrivs || (Process->PrivsChangeOneBit && IntegrityCheck)) &&
904  (enabled & present) != enabled)
905  {
906  // Some versions of Windows are special and, when a privilege is disabled, it disables it from present and
907  // just after that from enabled. This may lead to a race condition, where we find a bit in Enabled
908  // which is not set in Present. We will check a bit which can give a detection in this case, and skip the
909  // detection if this happens. Note that we will do it only once and only on integrity. We expect that, at
910  // next check the 1-bit difference to not be present anymore, as it would indicate malicious behavior, and
911  // not the presented race condition.
912  if (!Process->PrivsChangeOneBit && IntegrityCheck)
913  {
914  QWORD diffbits = enabled & (~(enabled & present));
915 
916  if (diffbits != 0 && (diffbits & (diffbits - 1)) == 0)
917  {
918  WARNING("[WARNING] Special case on OS version: %d, difference 1 bit! 0x%016llx 0x%016llx\n",
919  gGuest.OSVersion, enabled, present);
920 
921  Process->PrivsChangeOneBit = TRUE;
922 
923  goto _skip_checks;
924  }
925  }
926 
927  // Since we're giving a detection, we'll reset the flag, but only if we are doing an integrity check.
928  if (IntegrityCheck)
929  {
930  Process->PrivsChangeOneBit = FALSE;
931  }
932 
933  *EnabledIncreased = TRUE;
934  }
935  else if (IntegrityCheck)
936  {
937  // Since there seems nothing have changed in a malicious way, we'll reset the flag, but only if we
938  // are doing an integrity check.
939  Process->PrivsChangeOneBit = FALSE;
940  }
941 
942 _skip_checks:
943  if (NULL != Present)
944  {
945  *Present = present;
946  }
947 
948  if (NULL != Enabled)
949  {
950  *Enabled = enabled;
951  }
952 
953  return INT_STATUS_SUCCESS;
954 }
955 
956 
957 INTSTATUS
959  _In_ WIN_PROCESS_OBJECT *Process
960  )
969 {
970  INTSTATUS status;
971  QWORD present, enabled;
972  QWORD oldValue = 0, newValue = 0;
973  BOOLEAN presentIncreased = FALSE, enabledIncreased = FALSE;
974  EXCEPTION_KM_ORIGINATOR originator = { 0 };
975  EXCEPTION_VICTIM_ZONE victim = { 0 };
976  INTRO_ACTION action;
977  INTRO_ACTION_REASON reason;
978 
979  if (NULL == Process)
980  {
982  }
983 
984  status = IntWinTokenFetchTokenAddress(Process, &oldValue, &newValue);
985  if (!INT_SUCCESS(status))
986  {
987  ERROR("[ERROR] IntWinTokenFetchTokenAddress failed: 0x%08x!\n", status);
988  return status;
989  }
990 
991  if (oldValue != newValue)
992  {
993  IntWinTokenProtectPrivsInternal(Process, newValue);
994  }
995 
996  status = IntWinTokenCheckCurrentPrivileges(Process,
997  newValue,
998  TRUE,
999  &presentIncreased,
1000  &enabledIncreased,
1001  &present,
1002  &enabled);
1003  if (!INT_SUCCESS(status))
1004  {
1005  return status;
1006  }
1007 
1008  if (Process->SkipPrivsNextCheck)
1009  {
1010  goto _skip_checks;
1011  }
1012 
1013  if (presentIncreased || enabledIncreased)
1014  {
1016 
1017  // The originator is only dummy. Complete just the elements so that we can go through exceptions.
1018  originator.Original.NameHash = INITIAL_CRC_VALUE;
1019  originator.Return.NameHash = INITIAL_CRC_VALUE;
1020 
1021  // Since we don't have an INTEGRITY_REGION associated, we'll complete the victim in-place.
1022  victim.Object.Process = Process;
1023  victim.Object.NameHash = Process->NameHash;
1025  victim.ZoneFlags |= ZONE_WRITE | ZONE_INTEGRITY;
1027 
1028  victim.WriteInfo.OldValue[0] = Process->OriginalPresentPrivs;
1029  victim.WriteInfo.OldValue[1] = Process->OriginalEnabledPrivs;
1030 
1031  victim.WriteInfo.NewValue[0] = present;
1032  victim.WriteInfo.NewValue[1] = enabled;
1033 
1034  victim.WriteInfo.AccessSize = 2 * sizeof(QWORD);
1035 
1036  IntExcept(&victim, &originator, exceptionTypeKm, &action, &reason, introEventIntegrityViolation);
1037 
1039 
1041  {
1042  // Mark action as allowed, since we didn't really block anything.
1044 
1045  if (presentIncreased)
1046  {
1047  WARNING("[WARNING] Present privileges are higher than the original ones: "
1048  "0x%016llx vs 0x%016llx in process %s:%d\n",
1049  present,
1050  Process->OriginalPresentPrivs,
1051  Process->Name,
1052  Process->Pid);
1053  }
1054  if (enabledIncreased)
1055  {
1056  WARNING("[WARNING] Enabled privileges are higher than the present ones: "
1057  "0x%016llx vs 0x%016llx in process %s:%d\n",
1058  enabled,
1059  present,
1060  Process->Name,
1061  Process->Pid);
1062  }
1063  }
1064 
1065  // Theoretically, we can overwrite the privileges with the old ones if not allowed, but note that we are
1066  // around ~1 sec after the LPE took place, so the exploit could have run enough to do damage
1067  // with the gained privileges, so there's not much to do about it...
1068 
1069  // Note that we should also consider on DPI from now on as a detection every process creation, since
1070  // on DPI we will see only the changed values, as we update them below.
1071  // NOTE: we set the DPI flag only if Present privileges were increased. If Enabled were increased in
1072  // comparison with Present, then the DPI mechanism will issue an alert as is, as the heuristic will
1073  // detect the Enabled extra bits at that point. However, if Present privileges were increased, we won't
1074  // be able to detect it otherwise. Also, after Present privileges increase has taken place, we can't
1075  // consider any process creation as legitimate in DPI from now on, as we can't know in a reliable way
1076  // when Present privileges will be ok in the future, so we won't mark PrivsChangeDetected as FALSE at
1077  // any point from now on.
1078  if (presentIncreased)
1079  {
1080  Process->PrivsChangeDetected = TRUE;
1081  }
1082  }
1083 
1084 _skip_checks:
1085  // Save them so we don't give an alert for the same stuff once every second.
1086  Process->OriginalPresentPrivs = present;
1087  Process->OriginalEnabledPrivs = enabled;
1088 
1089  // On the next tick, give a detection if something else changed.
1090  Process->SkipPrivsNextCheck = FALSE;
1091 
1092  return status;
1093 }
1094 
1095 
1098  void
1099  )
1107 {
1108  LIST_ENTRY *pList = NULL;
1109 
1111  {
1113  }
1114 
1115  if (!gGuest.GuestInitialized)
1116  {
1118  }
1119 
1120  if ((introGuestWindows != gGuest.OSType) ||
1122  {
1124  }
1125 
1126  pList = gWinProcesses.Flink;
1127  while (pList != &gWinProcesses)
1128  {
1129  WIN_PROCESS_OBJECT* pProcess = CONTAINING_RECORD(pList, WIN_PROCESS_OBJECT, Link);
1130  INTSTATUS status;
1131 
1132  pList = pList->Flink;
1133 
1134  // Note: the order is important here, we should do the token stolen integrity checks
1135  // always the last one, since afterwards we won't have anymore the old token, as the
1136  // check will also overwrite the OriginalTokenPtr field.
1138  {
1139  status = IntWinTokenPrivsCheckIntegrityOnProcess(pProcess);
1140  if (!INT_SUCCESS(status) &&
1142  {
1143  ERROR("[ERROR] IntWinTokenPtrCheckIntegrityOnProcess failed: 0x%08x\n", status);
1144  }
1145  }
1146 
1148  {
1149  status = IntWinTokenPtrCheckIntegrityOnProcess(pProcess);
1150  if (!INT_SUCCESS(status) &&
1152  {
1153  ERROR("[ERROR] IntWinTokenPtrCheckIntegrityOnProcess failed: 0x%08x\n", status);
1154  }
1155  }
1156  }
1157 
1158  return INT_STATUS_SUCCESS;
1159 }
1160 
1161 
1162 INTSTATUS
1164  _Inout_ WIN_PROCESS_OBJECT *Process
1165  )
1176 {
1177  QWORD privs[2];
1178  INTSTATUS status;
1179 
1180  if (NULL == Process)
1181  {
1183  }
1184 
1185  if (NULL != Process->TokenHook)
1186  {
1188  }
1189 
1190  status = IntVirtMemRead(Process->OriginalTokenPtr + WIN_KM_FIELD(Token, Privs),
1191  2 * sizeof(QWORD),
1193  privs,
1194  NULL);
1195  if (!INT_SUCCESS(status))
1196  {
1197  // If the page is not present, just skip checks on next integrity and skip ept hooking,
1198  // as we can't properly decide whether to hook the token or not.
1200  {
1201  Process->SkipPrivsNextCheck = TRUE;
1202  return INT_STATUS_SUCCESS;
1203  }
1204 
1205  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
1206  return status;
1207  }
1208 
1209  // Save them even if the flag was not given, so that, if the flag is suddenly activated and we check integrity,
1210  // we won't have any bad surprises.
1211  Process->OriginalPresentPrivs = privs[0];
1212  Process->OriginalEnabledPrivs = privs[1];
1213 
1215  {
1217  }
1218 
1219  return IntWinTokenProtectPrivsInternal(Process, Process->OriginalTokenPtr);
1220 }
1221 
1222 
1223 INTSTATUS
1225  _In_ WIN_PROCESS_OBJECT *Process
1226  )
1227 {
1228  INTSTATUS status = INT_STATUS_SUCCESS;
1229 
1230  if (NULL == Process)
1231  {
1233  }
1234 
1235  if (NULL != Process->TokenHook)
1236  {
1237  status = IntHookGvaRemoveHook((HOOK_GVA **)&Process->TokenHook, 0);
1238  if (!INT_SUCCESS(status))
1239  {
1240  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
1241  }
1242  }
1243 
1244  if (NULL != Process->TokenSwapHook)
1245  {
1246  status = IntHookGvaRemoveHook((HOOK_GVA **)&Process->TokenSwapHook, 0);
1247  if (!INT_SUCCESS(status))
1248  {
1249  ERROR("[ERROR] IntHookGvaRemoveHook failed: 0x%08x\n", status);
1250  }
1251  }
1252 
1253  return status;
1254 }
1255 
1256 
1257 INTSTATUS
1259  void
1260  )
1267 {
1268  LIST_ENTRY *list;
1269  INTSTATUS status;
1270 
1272  {
1274  }
1275 
1276  list = gWinProcesses.Flink;
1277 
1278  while (list != &gWinProcesses)
1279  {
1281 
1282  list = list->Flink;
1283 
1284  status = IntWinTokenPrivsProtectOnProcess(pProc);
1285  if (!INT_SUCCESS(status))
1286  {
1287  WARNING("[WARNING] IntWinTokenPrivsProtectOnProcess failed for %s:%d: 0x%08x\n",
1288  pProc->Name,
1289  pProc->Pid,
1290  status);
1291  }
1292  }
1293 
1294  return INT_STATUS_SUCCESS;
1295 }
1296 
1297 
1298 INTSTATUS
1300  void
1301  )
1308 {
1309  LIST_ENTRY *list;
1310  INTSTATUS status = INT_STATUS_SUCCESS;
1311 
1313  {
1315  }
1316 
1317  list = gWinProcesses.Flink;
1318 
1319  while (list != &gWinProcesses)
1320  {
1322 
1323  list = list->Flink;
1324 
1325  if (NULL != pProc->TokenHook)
1326  {
1327  IntHookGvaRemoveHook((HOOK_GVA **)&pProc->TokenHook, 0);
1328  }
1329 
1330  if (NULL != pProc->TokenSwapHook)
1331  {
1333  }
1334  }
1335 
1336  return status;
1337 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
#define _In_opt_
Definition: intro_sal.h:16
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
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
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
#define WIN_POOL_TAG_TOKE2
Definition: winpool.h:18
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
Exposes the types, constants and functions used to handle Windows processes events (creation...
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
QWORD IntAlertCoreGetFlags(QWORD ProtectionFlag, INTRO_ACTION_REASON Reason)
Returns the flags for an alert.
Definition: alerts.c:366
DWORD Size
The size of the access.
Definition: intro_types.h:982
#define VICTIM_PROCESS_TOKEN
Printable name used for introObjectTypeTokenPtr objects.
Definition: intro_types.h:753
#define EX_FAST_REF_TO_PTR(is64, p)
Converts a _EX_FAST_REF value to a pointer.
Definition: wddefs.h:100
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
QWORD ZoneFlags
The flags of the modified zone.
Definition: exceptions.h:898
static INTSTATUS IntWinTokenFetchTokenAddress(WIN_PROCESS_OBJECT *Process, QWORD *OldValue, QWORD *NewValue)
Fetches the token pointer from inside the EPROCESS and returns the old token pointer and the new toke...
Definition: wintoken.c:564
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
const POOL_HEADER * IntWinPoolGetPoolHeaderInPage(const void *Page, DWORD StartOffset, DWORD Tag)
Search for a pool header with given tag in a buffer.
Definition: winpool.c:160
BOOLEAN IntPolicyCoreTakeAction(QWORD Flag, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason)
Returns the action that should be taken for a core introspection option.
Definition: introcore.c:2693
QWORD NewValue[8]
The written value. Only the first Size bytes are valid.
Definition: intro_types.h:981
#define STATS_EXIT(id)
Definition: stats.h:160
static BOOLEAN IntWinTokenPrivsShouldHook(WIN_PROCESS_OBJECT *Process, QWORD NewTokenPtr)
Decides if the given token address should be hooked through EPT or not.
Definition: wintoken.c:180
void IntAlertFillWinProcess(const WIN_PROCESS_OBJECT *Process, INTRO_PROCESS *EventProcess)
Saves information about a windows process inside an alert.
Definition: alerts.c:689
Event structure for integrity violations on monitored structures.
Definition: intro_types.h:1572
POOL_HEADER32 Header32
Definition: wddefs.h:462
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
#define _Success_(expr)
Definition: intro_sal.h:47
#define INTRO_OPT_PROT_KM_TOKEN_PTR
Enable process token protection (Windows only).
Definition: intro_types.h:416
#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
BOOLEAN ProtectionActivated
Definition: guests.h:297
#define PAGE_OFFSET
Definition: pgtable.h:32
BOOLEAN KernelBetaDetections
True if the kernel protection is in beta (log-only) mode.
Definition: guests.h:303
struct _EVENT_INTEGRITY_VIOLATION::@304 Victim
QWORD IntHookGetGlaFromGpaHook(HOOK_GPA const *Hook, QWORD Address)
Gets the GLA from a GPA hook.
Definition: hook.c:279
DWORD BlockSize
Definition: wddefs.h:440
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
static INTSTATUS IntWinTokenPrivsHandleWrite(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
EPT callback triggered when a write occurs over the Privileges bitfields in a nt!_TOKEN structure pro...
Definition: wintoken.c:429
#define ALERT_FLAG_ASYNC
If set, the alert was generated in an async manner.
Definition: intro_types.h:675
Token writes.
Definition: stats.h:94
INTSTATUS IntKernVirtMemFetchWordSize(QWORD GuestVirtualAddress, void *Data)
Reads a guest pointer from the guest kernel memory.
Definition: introcore.c:847
int INTSTATUS
The status data type.
Definition: introstatus.h:24
DWORD OSVersion
Os version.
Definition: guests.h:281
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
TIMER_FRIENDLY INTSTATUS IntWinTokenCheckIntegrity(void)
This function checks the integrity of the security token for all the processes inside gWinProcesses...
Definition: wintoken.c:1097
Describes a kernel-mode originator.
Definition: exceptions.h:943
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
struct _EXCEPTION_KM_ORIGINATOR::@63 Return
EVENT_EPT_VIOLATION Ept
Definition: alerts.h:16
#define LOG(fmt,...)
Definition: glue.h:61
LIST_HEAD gWinProcesses
The list of all the processes inside the guest.
Definition: winprocesshp.c:11
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
DWORD AccessSize
The size of the memory access. Valid only for EPT exits.
Definition: guests.h:103
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1217
#define ZONE_INTEGRITY
Used for integrity zone.
Definition: exceptions.h:738
#define HOOK_FLG_HIGH_PRIORITY
If flag is set, the callback associated to this hook will have a higher priority than the others...
Definition: hook.h:54
INTSTATUS IntWinTokenCheckCurrentPrivileges(WIN_PROCESS_OBJECT *Process, QWORD TokenPtr, BOOLEAN IntegrityCheck, BOOLEAN *PresentIncreased, BOOLEAN *EnabledIncreased, QWORD *Present, QWORD *Enabled)
Verifies the current token if the current Privileges.Present and Privileges.Enabled fields were not a...
Definition: wintoken.c:811
Access Token Manipulation.
Definition: intro_types.h:1153
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
INTSTATUS IntWinTokenPtrCheckIntegrityOnProcess(WIN_PROCESS_OBJECT *Process)
This function checks if the security token of a given process has been stone from another process...
Definition: wintoken.c:719
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
Token privileges.
Definition: intro_types.h:266
#define ALERT_FLAG_BETA
If set, the alert is a BETA alert. No action was taken.
Definition: intro_types.h:671
static void IntWinTokenPrivsSendIntegrityAlert(EXCEPTION_VICTIM_ZONE *Victim, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Sends an EVENT_INTEGRITY_VIOLATION when checks over the token privileges have failed.
Definition: wintoken.c:125
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 IntWinTokenPrivsUnprotectOnProcess(WIN_PROCESS_OBJECT *Process)
Definition: wintoken.c:1224
QWORD Flags
The entry that maps VirtualAddress to PhysicalAddress, together with all the control bits...
Definition: introcore.h:119
#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 WIN_POOL_TAG_TOKE
Definition: winpool.h:17
#define INITIAL_CRC_VALUE
Definition: introdefs.h:221
#define INT_STATUS_EXCEPTION_BLOCK
Definition: introstatus.h:421
DWORD Size
The size of the modified memory area.
Definition: intro_types.h:1618
void IntAlertEptFillFromVictimZone(const EXCEPTION_VICTIM_ZONE *Victim, EVENT_EPT_VIOLATION *EptViolation)
Fills the victim information inside an EPT alert.
Definition: alerts.c:868
POOL_HEADER64 Header64
Definition: wddefs.h:463
The modified object is inside an integrity hook.
Definition: exceptions.h:749
#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
uint8_t * PBYTE
Definition: intro_types.h:47
DWORD BlockSize
Definition: wddefs.h:411
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
#define memzero(a, s)
Definition: introcrt.h:35
#define PT_P
Definition: pgtable.h:83
INTSTATUS IntGpaCacheFetchAndAdd(PGPA_CACHE Cache, QWORD Gpa, DWORD Size, PBYTE Buffer)
Fetch data from a cached entry, or add it to the cache, of not already present.
Definition: gpacache.c:508
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
CHAR Name[IMAGE_BASE_NAME_LEN]
Process base name.
Definition: winprocess.h:108
QWORD Current
The currently used options.
Definition: guests.h:236
void * GpaCache
The currently used GPA cache.
Definition: guests.h:403
QWORD VirtualAddress
The guest virtual address which was modified.
Definition: intro_types.h:1616
#define TRUE
Definition: intro_types.h:30
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1574
void * TokenSwapHook
Hook object for notifications over the swap-in/swap-out of the current process TOKEN. We need to place this hook in order to verify on translation modifications of the current TOKEN if it is still assigned to the current process. The token might get deallocated in the mean-time and the page can be used, for example, for mapping other physical pages, thus leading to translation violations when the hashes of the contents are checked. For this purpose we will verify on every translation modification event if the current token is still used, and re-establish the hook over the token if it was previously de-allocated.
Definition: winprocess.h:322
Measures the checks to see if the token has been changed when a token swap occurs.
Definition: stats.h:97
BOOLEAN GuestInitialized
True if the OS-specific portion has been initialized.
Definition: guests.h:293
#define TRACE(fmt,...)
Definition: glue.h:58
DWORD NameHash
The hash of the modified object.
Definition: exceptions.h:854
#define TIMER_FRIENDLY
Definition: introdefs.h:83
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
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
INTSTATUS IntTranslateVirtualAddressEx(QWORD Gva, QWORD Cr3, DWORD Flags, VA_TRANSLATION *Translation)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1863
INTRO_EXEC_CONTEXT ExecContext
Information about the instruction that triggered the alert.
Definition: intro_types.h:1309
void * TokenHook
Hook object for the ept hook over nt!_TOKEN Privileges field.
Definition: winprocess.h:314
INTSTATUS IntWinTokenPrivsProtectOnProcess(WIN_PROCESS_OBJECT *Process)
Updates the stored original Privileges bitfields (Present and Enabled) and hooks through EPT the Priv...
Definition: wintoken.c:1163
#define WARNING(fmt,...)
Definition: glue.h:60
DWORD Pid
Process ID (the one used by Windows).
Definition: winprocess.h:100
Measures the checks to see if the token has been changed when a write occurs over the token...
Definition: stats.h:96
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
#define PAGE_SIZE
Definition: common.h:70
Describes the modified zone.
Definition: exceptions.h:893
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define TRFLG_PG_MODE
Obtains the translation mode flag for the currently used paging mode.
Definition: introcore.h:92
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
QWORD OriginalTokenPtr
Original Token pointer inside EPROCESS (should never change).
Definition: winprocess.h:247
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.
QWORD OldValue[8]
The original value. Only the first Size bytes are valid.
Definition: intro_types.h:980
INTRO_WRITE_INFO WriteInfo
Definition: intro_types.h:1607
No access type. This can be used for swap hooks.
Definition: glueiface.h:297
__must_check INTSTATUS IntVirtMemMap(QWORD Gva, DWORD Length, QWORD Cr3, DWORD Flags, void **HostPtr)
Maps a guest virtual memory range inside Introcore virtual address space.
Definition: introcore.c:2134
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
static INTSTATUS IntWinTokenPrivsHandleSwap(void *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize)
Handles a token swap-in or swap-out, re-applying protection if the token is not assigned anymore to a...
Definition: wintoken.c:356
BOOLEAN IntWinTokenPtrIsStolen(WIN_PROCESS_OBJECT *Process, BOOLEAN Check, WIN_PROCESS_OBJECT **FromProcess, QWORD *OldValue, QWORD *NewValue)
This function checks if the security token of a given process has been stone from another process...
Definition: wintoken.c:625
QWORD EprocessAddress
This will be the address of the ActiveProcess field.
Definition: winprocess.h:90
#define ALERT_FLAG_SYSPROC
If set, the alert is on system process.
Definition: intro_types.h:673
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
void IntAlertFillWinProcessByCr3(QWORD ProcessCr3, INTRO_PROCESS *EventProcess)
Saves information about a Windows process inside an alert. The process is searched by its kernel CR3...
Definition: alerts.c:756
INTSTATUS IntVirtMemRead(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer, DWORD *RetLength)
Reads data from a guest virtual memory range.
Definition: introcore.c:627
static void IntWinTokenPrivsSendEptAlert(EXCEPTION_KM_ORIGINATOR *Originator, EXCEPTION_VICTIM_ZONE *Victim, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Sends an EVENT_EPT_VIOLATION for a token privileges violation.
Definition: wintoken.c:73
INTSTATUS IntWinTokenUnprotectPrivs(void)
Unprotects all the currently protected tokens belonging to processes against privileges manipulation...
Definition: wintoken.c:1299
void * Process
The internal structure of the modified process.
Definition: exceptions.h:881
EVENT_INTEGRITY_VIOLATION Integrity
Definition: alerts.h:23
struct _EXCEPTION_KM_ORIGINATOR::@64 Original
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
#define INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
QWORD BaseAddress
The guest virtual address at which the monitored integrity region starts.
Definition: intro_types.h:1614
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
INTRO_PROCESS Process
The module to which the current code return to.
Definition: intro_types.h:1579
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1197
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
#define INTRO_OPT_PROT_KM_TOKEN_PRIVS
Enable protection over Token Privileges bitmaps.
Definition: intro_types.h:505
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
Event structure for EPT violations.
Definition: intro_types.h:1215
INTSTATUS IntWinTokenPrivsCheckIntegrityOnProcess(WIN_PROCESS_OBJECT *Process)
This function checks if the privileges bitfields for the given process have been changed in a malicio...
Definition: wintoken.c:958
static INTSTATUS IntWinTokenProtectPrivsInternal(WIN_PROCESS_OBJECT *Process, QWORD NewTokenPtr)
If needed, this function will establish an EPT hook on the given token pointer for privileges protect...
Definition: wintoken.c:276
Access Token pointer.
Definition: intro_types.h:250
Write-access hook.
Definition: glueiface.h:299
#define PAGE_MASK
Definition: pgtable.h:35
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
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
DWORD NameHash
The namehash of the originator return driver.
Definition: exceptions.h:947
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:83
#define VICTIM_TOKEN_PRIVILEGES
Printable name used for introObjectTypeTokenPrivs objects.
Definition: intro_types.h:755
INTSTATUS IntWinTokenProtectPrivs(void)
Protects all the currently unprotected tokens belonging to processes against privileges manipulation...
Definition: wintoken.c:1258
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68