Bitdefender Hypervisor Memory Introspection
ptfilter.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "ptfilter.h"
6 #include "ptfilter.h"
7 #include "winagent.h"
8 #include "decoder.h"
9 #include "hook.h"
10 #include "icache.h"
11 #include "kernvm.h"
12 #include "loader.h"
13 #include "memcloak.h"
14 #include "memtables.h"
15 #include "winagent_ptdriver_x64.h"
16 #include "winapi.h"
17 #include "winpe.h"
18 #include "winpower.h"
19 
20 
41 // The main components are:
54 
55 
56 #define MAX_ENTRIES_PER_BUCKET 256
57 #define MAX_LUT_PAGES 64
58 #define MAX_HANDLER_SIZE 48ull
59 #define TABLE_BUCKET_MASK 0x3F
60 
61 
65 typedef struct _PTI_CANDIDATE
66 {
70  INSTRUX Instruction;
71  void *CloakHandle;
75 
78 
89 
93 
96 
97 // Relevant section sizes.
101 
102 
103 
107  )
113 {
115 }
116 
117 
120  _In_ RBNODE *Left,
121  _In_ RBNODE *Right
122  )
133 {
134  PPTI_CANDIDATE p1 = CONTAINING_RECORD(Left, PTI_CANDIDATE, Node);
135  PPTI_CANDIDATE p2 = CONTAINING_RECORD(Right, PTI_CANDIDATE, Node);
136 
137  if (p1->Gla < p2->Gla)
138  {
139  return -1;
140  }
141  else if (p1->Gla > p2->Gla)
142  {
143  return 1;
144  }
145  else
146  {
147  return 0;
148  }
149 }
150 
151 
154 
157 
158 
159 
160 static void
162  void
163  )
167 {
169  gPtDriverAddress = 0;
178  gPtDriverSize = 0;
179  gPtMemtableSize = 0;
180  gPtHandlerIndex = 0;
181  memset(gPtTableIndexes, 0, sizeof(gPtTableIndexes));
182 }
183 
184 
185 static void
187  _Inout_ PPTI_CANDIDATE Candidate
188  )
194 {
195  if (NULL != Candidate->CloakHandle)
196  {
197  IntMemClkUncloakRegion(Candidate->CloakHandle, MEMCLOAK_OPT_APPLY_PATCH);
198 
200  }
201 
202  Candidate->CloakHandle = NULL;
203 
204  RemoveEntryList(&Candidate->Link);
205 
206  RbDeleteNode(&gPtiCandidatesTree, &Candidate->Node);
207 
208  HpFreeAndNullWithTag(&Candidate, IC_TAG_ALLOC);
209 }
210 
211 
212 static INTSTATUS
214  void
215  )
234 {
235  INTSTATUS status;
237  INSTRUX instrux;
238  DWORD i, vi, count, seci, pagei;
239  BYTE int3[16] = { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC };
240  CHAR secname[9];
241  PPTI_CANDIDATE pPtc;
242  PBYTE pPage1, pPage2;
243 
244  // NOTE: The VCPUs must already be paused when calling this.
245  pPage1 = pPage2 = NULL;
246 
248 
249  for (seci = 0; ; seci++)
250  {
251  DWORD pageCount;
252 
253  // Determine the .text section size & location.
255  if (!INT_SUCCESS(status))
256  {
257  break;
258  }
259 
260  // We only parse executable, non-paged, non-discardable sections.
261  if (0 == (sec.Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
264  {
265  continue;
266  }
267 
268  if (0 != memcmp(sec.Name, ".text", 5))
269  {
270  // Skip all non-.text sections.
271  continue;
272  }
273 
274  memcpy(secname, sec.Name, 8);
275  secname[8] = 0;
276 
277  TRACE("[PTCORE] Parsing section '%s'...\n", secname);
278 
279  // Will count how many valid, consecutive instructions we have found. We will ignore candidate instructions
280  // if they are not preceded by at least 8 such valid, consecutive instructions.
281  vi = count = i = 0;
282 
283  pageCount = ROUND_UP(sec.Misc.VirtualSize, PAGE_SIZE) / PAGE_SIZE;
284 
285  // Start disassembling the .text section.
286  for (pagei = 0; pagei < pageCount; ++pagei)
287  {
288  DWORD offset = sec.VirtualAddress + pagei * PAGE_SIZE;
289  DWORD sizeToParse = (pagei == (pageCount - 1)) ? sec.Misc.VirtualSize & PAGE_OFFSET : PAGE_SIZE;
290  QWORD target = gGuest.KernelVa + offset;
291 
292  if (NULL == pPage1)
293  // We may have already mapped this inside the while loop
294  {
295  status = IntVirtMemMap(target, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pPage1);
296  if (!INT_SUCCESS(status))
297  {
298  ERROR("ERROR] Failed mapping page %d of section %d: 0x%08x\n", pagei, seci, status);
299  goto cleanup_and_exit;
300  }
301  }
302 
303  pPage2 = NULL;
304 
305  while (i < sizeToParse)
306  {
307  BOOLEAN bCrossPage = FALSE;
308  NDSTATUS ndstatus = NdDecodeEx(&instrux,
309  pPage1 + i,
310  sizeToParse - i,
311  ND_CODE_64,
312  ND_DATA_64);
313  if (ND_STATUS_BUFFER_TOO_SMALL == ndstatus)
314  {
315  BYTE buffer[16] = { 0 };
316 
317  bCrossPage = TRUE;
318 
319  status = IntVirtMemMap(target + PAGE_SIZE, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pPage2);
320  if (!INT_SUCCESS(status))
321  {
322  ERROR("ERROR] Failed mapping page %d of section %d: 0x%08x\n", pagei, seci, status);
323  goto cleanup_and_exit;
324  }
325 
326  memcpy(buffer, pPage1 + i, sizeToParse - i);
327  memcpy(buffer + sizeToParse - i, pPage2, 16 - (sizeToParse - i));
328 
329  status = IntDecDecodeInstructionFromBuffer(buffer, 16, IG_CS_TYPE_64B, &instrux);
330  }
331  else
332  {
333  status = ND_SUCCESS(ndstatus) ? INT_STATUS_SUCCESS : INT_STATUS_DISASM_ERROR;
334  }
335 
336  if (!INT_SUCCESS(status))
337  {
338  i++;
339  vi = 0;
340  }
341  else
342  {
343  // Note: none of these have single byte encodings (with memory access) - this is important, because
344  // e we will replace them with INT 20 (2 bytes), so we won't overwrite two instructions.
345  // Instruction formats accepted:
346  // mov qword [reg{+disp}], 0
347  // mov qword [reg{+disp}], reg
348  // xchg qword [reg{+disp}], reg
349  // lock cmpxchg [reg{+disp}], reg
350  // Constraints:
351  // - base reg cannot be RSP
352  // - disp, if present:
353  // - must be less than 0x20
354  // - must be aligned to 8
355  if (instrux.Instruction == ND_INS_MOV ||
356  instrux.Instruction == ND_INS_XCHG ||
357  (instrux.Instruction == ND_INS_CMPXCHG && instrux.HasLock))
358  {
359  if (instrux.Operands[0].Type == ND_OP_MEM && instrux.Operands[0].Size == 8 &&
360  instrux.Operands[0].Info.Memory.HasBase && !instrux.Operands[0].Info.Memory.HasIndex &&
361  instrux.Operands[0].Info.Memory.Base != NDR_RSP &&
362  (!instrux.HasDisp || ((instrux.Displacement < 0x20) && (instrux.Displacement % 8 == 0))) &&
363  ((instrux.Operands[1].Type == ND_OP_REG) || ((instrux.Operands[1].Type == ND_OP_IMM) &&
364  (instrux.Operands[1].Info.Immediate.Imm == 0))) &&
365  (vi >= 8))
366  {
367  pPtc = HpAllocWithTag(sizeof(*pPtc), IC_TAG_ALLOC);
368  if (NULL == pPtc)
369  {
371  goto cleanup_and_exit;
372  }
373 
374  pPtc->Gla = target + i;
375 
376  memcpy(&pPtc->Instruction, &instrux, sizeof(instrux));
377 
378  // Intercept the instruction.
379  status = IntMemClkCloakRegion(pPtc->Gla,
380  0,
381  pPtc->Instruction.Length,
382  0,
383  pPtc->Instruction.InstructionBytes,
384  int3,
385  NULL,
386  &pPtc->CloakHandle);
387  if (!INT_SUCCESS(status))
388  {
389  ERROR("[ERROR] IntMemClkCloakRegion failed: 0x%08x\n", status);
391  goto cleanup_and_exit;
392  }
393 
394  if (!bCrossPage)
395  {
396  memcpy(pPage1 + i, int3, instrux.Length);
397  }
398  else
399  {
400  memcpy(pPage1 + i, int3, sizeToParse - i);
401  memcpy(pPage2, int3, instrux.Length - (sizeToParse - i));
402  }
403 
404  status = RbInsertNode(&gPtiCandidatesTree, &pPtc->Node);
405  if (!INT_SUCCESS(status))
406  {
407  ERROR("[ERROR] RbInsertNode failed: 0x%08x\n", status);
409  goto cleanup_and_exit;
410  }
411 
412  InsertTailList(&gPtiCandidatesList, &pPtc->Link);
413 
415 
416  pPtc->Monitored = TRUE;
417 
418  count++;
419  }
420  }
421 
422  vi++;
423  i += instrux.Length;
424  }
425  }
426 
427  i -= sizeToParse;
428 
429  IntVirtMemUnmap(&pPage1);
430 
431  pPage1 = pPage2;
432  pPage2 = NULL;
433  }
434 
435  TRACE("[PTCORE] Patched %d instructions in section '%s'!\n", count, secname);
436  }
437 
438  gPtMonitored = TRUE;
439 
440  status = INT_STATUS_SUCCESS;
441 
442 cleanup_and_exit:
443 
444  if (NULL != pPage1)
445  {
446  IntVirtMemUnmap(&pPage1);
447  }
448 
449  if (NULL != pPage2)
450  {
451  IntVirtMemUnmap(&pPage2);
452  }
453 
455 
456  return status;
457 }
458 
459 
460 static INTSTATUS
462  void
463  )
469 {
470  // Search for the matching instruction.
471  for (LIST_ENTRY *list = gPtiCandidatesList.Flink; list != &gPtiCandidatesList; )
472  {
473  PPTI_CANDIDATE pPtc = CONTAINING_RECORD(list, PTI_CANDIDATE, Link);
474 
475  list = list->Flink;
476 
478  }
479 
481 
482  return INT_STATUS_SUCCESS;
483 }
484 
485 
486 static INTSTATUS
488  PPTI_CANDIDATE Candidate
489  )
521 {
522  INTSTATUS status;
523  QWORD addr = *(&gVcpu->Regs.Rax + Candidate->Instruction.Operands[0].Info.Memory.Base) +
524  Candidate->Instruction.Operands[0].Info.Memory.Disp; // Will be 0 if not present.
525  BYTE int20[16] = { 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xCD, 0x14 };
526  BYTE handler[MAX_HANDLER_SIZE], i, len;
527  BOOLEAN relevant;
528  QWORD rip, hnd;
529  DWORD dest;
530  QWORD idx;
531  enum { ptNone, ptSelfMap, ptTranslatesToPt, ptWritesPte } criteria = ptNone;
532 
533  IntPauseVcpus();
534 
535  // Assume the modification is not relevant for now.
536  relevant = FALSE;
537 
538  // The most important and straight forward criteria - a self-map entry is being accessed.
539  if (((addr >> 39) & 0x1FF) == gGuest.Mm.SelfMapIndex)
540  {
541  relevant = TRUE;
542  criteria = ptSelfMap;
543  }
544 
545 #define REG(r) (*(&gVcpu->Regs.Rax + (r)))
546 
547  // Not a self-map entry - see if this translates to a PT anyway.
548  if (!relevant)
549  {
550  PHOOK_EPT_ENTRY pageEntry = NULL;
551  QWORD gpa, gla;
552 
553  // NOTE: We only instrument instructions using register base addressing + optional displacement (max 0x20).
554  gla = REG(Candidate->Instruction.Operands[0].Info.Memory.Base) +
555  Candidate->Instruction.Operands[0].Info.Memory.Disp;
556 
557  // Translate the GLA.
558  status = IntTranslateVirtualAddress(gla, gVcpu->Regs.Cr3, &gpa);
559  if (INT_SUCCESS(status))
560  {
561  pageEntry = IntHookGpaGetExistingEptEntry(gpa);
562  if (NULL != pageEntry)
563  {
564  // Check if this is a page table.
565  relevant = pageEntry->PtCount != 0;
566  criteria = ptTranslatesToPt;
567  }
568  }
569  }
570 
571  // Not a PT accessed at all - maybe an unmonitored PT, without self-map access, so check if the written value
572  // looks like a legit PT entry.
573  if (!relevant)
574  {
575  QWORD newval;
576 
577  if (ND_OP_REG == Candidate->Instruction.Operands[1].Type)
578  {
579  newval = REG(Candidate->Instruction.Operands[1].Info.Register.Reg);
580  }
581  else if (ND_OP_IMM == Candidate->Instruction.Operands[1].Type)
582  {
583  newval = Candidate->Instruction.Operands[1].Info.Immediate.Imm;
584  }
585  else
586  {
587  // This wont match anything.
588  newval = 0;
589  }
590 
591  // We take a look at the low 12 bits, and match them against a few known PT values.
592  // We take a look at the high 12 bit, which must resemble legit PT values.
593  // We take a look at the mapping bits, which must not be 0.
594  if (((newval & 0xFFF) == 0x863 || (newval & 0xFFF) == 0x063 ||
595  (newval & 0xFFF) == 0x8E3 || (newval & 0xFFF) == 0x021) &&
596  ((newval & 0xFFF0000000000000) == 0x0000000000000000 ||
597  (newval & 0xFFF0000000000000) == 0x0A00000000000000 ||
598  (newval & 0xFFF0000000000000) == 0x8A00000000000000) &&
599  ((newval & 0x000FFFFFFFFFF000) != 0))
600  {
601  relevant = TRUE;
602  criteria = ptWritesPte;
603  }
604  }
605 
606  // Check for self-map - we use the PML4 index for that.
607  if (relevant)
608  {
609  // We always use the instruction following the current one, since its easier.
610  rip = Candidate->Gla + Candidate->Instruction.Length;
611 
612  // Make sure we still have space left inside the handlers section.
614  {
615  ERROR("[ERROR] Maximum handler index reached!\n");
617  goto release_and_exit;
618  }
619 
620  // Make sure we still have space left inside the (rip, hnd) table. Max MAX_ENTRIES_PER_BUCKET entries per page.
621  idx = gPtTableIndexes[rip & TABLE_BUCKET_MASK];
622  if (idx >= MAX_ENTRIES_PER_BUCKET)
623  {
624  ERROR("[ERROR] Maximum table index reached for bucket %lld!\n", rip & TABLE_BUCKET_MASK);
626  goto release_and_exit;
627  }
628 
629  len = 0;
630 
631  // Build the "PUSH IF" instruction.
632  handler[len++] = 0x6A;
633  handler[len++] = (gVcpu->Regs.Flags & NDR_RFLAG_IF) ? 1 : 0;
634 
635  // Build the "PUSH disp" instruction.
636  handler[len++] = 0x6A;
637  if (Candidate->Instruction.HasDisp)
638  {
639  // NOTE: We already made sure the displacement is less than 0x20, so it fits in a single byte.
640  handler[len++] = (BYTE)Candidate->Instruction.Displacement;
641  }
642  else
643  {
644  handler[len++] = 0;
645  }
646 
647  // Build the "PUSH gla" instruction.
648  handler[len++] = 0x48 | Candidate->Instruction.Rex.b;
649  handler[len++] = 0x50 | Candidate->Instruction.ModRm.rm;
650 
651  // Build the "CALL check" instruction.
652  dest = (DWORD)(gPtDriverCheckFunction -
654 
655  // Store a call to the check function.
656  handler[len++] = 0xE8;
657  handler[len++] = (dest >> 0x00) & 0xFF;
658  handler[len++] = (dest >> 0x08) & 0xFF;
659  handler[len++] = (dest >> 0x10) & 0xFF;
660  handler[len++] = (dest >> 0x18) & 0xFF;
661 
662  // If the above call returns, we can proceed with the instruction.
663 
664  // Build the "PUSH qword [gla]" instruction.
665  handler[len++] = 0x48 | Candidate->Instruction.Rex.b;
666  handler[len++] = 0xFF;
667  handler[len++] = 0x30 | Candidate->Instruction.ModRm.rm | (Candidate->Instruction.ModRm.mod << 6);
668 
669  if (Candidate->Instruction.HasSib)
670  {
671  handler[len++] = Candidate->Instruction.Sib.Sib;
672  }
673 
674  if (Candidate->Instruction.HasDisp)
675  {
676  for (QWORD k = 0; k < Candidate->Instruction.DispLength; k++)
677  {
678  handler[len++] = (Candidate->Instruction.Displacement >> (k * 8)) & 0xFF;
679  }
680  }
681 
682  // Store the original instruction.
683  for (i = 0; i < Candidate->Instruction.Length; i++)
684  {
685  handler[len++] = Candidate->Instruction.InstructionBytes[i];
686  }
687 
688  // Store the "PUSH qword [gla]" instruction.
689  handler[len++] = 0x48 | Candidate->Instruction.Rex.b;
690  handler[len++] = 0xFF;
691  handler[len++] = 0x30 | Candidate->Instruction.ModRm.rm | (Candidate->Instruction.ModRm.mod << 6);
692 
693  if (Candidate->Instruction.HasSib)
694  {
695  handler[len++] = Candidate->Instruction.Sib.Sib;
696  }
697 
698  if (Candidate->Instruction.HasDisp)
699  {
700  for (QWORD k = 0; k < Candidate->Instruction.DispLength; k++)
701  {
702  handler[len++] = (Candidate->Instruction.Displacement >> (k * 8)) & 0xFF;
703  }
704  }
705 
706  // Right here, the stack layout is:
707  // IF indicator (1 if IF is set when the instruction normally executes, 0 otherwise)
708  // The instruction displacement
709  // The instruction base GLA
710  // Old value
711  // New value
712 
713  // Store the "JMP main handler" instruction.
714  dest = (DWORD)(gPtDriverMainAddress -
715  (gPtDriverHandlersAddress + (gPtHandlerIndex * MAX_HANDLER_SIZE) + len + 5));
716 
717  // Store a jump to the main cache handler.
718  handler[len++] = 0xE9;
719  handler[len++] = (dest >> 0x00) & 0xFF;
720  handler[len++] = (dest >> 0x08) & 0xFF;
721  handler[len++] = (dest >> 0x10) & 0xFF;
722  handler[len++] = (dest >> 0x18) & 0xFF;
723 
724  // Modify the table to hold the new entry.
725 
726  hnd = gPtDriverHandlersAddress + ((QWORD)gPtHandlerIndex * MAX_HANDLER_SIZE);
727 
728  // No need to use IntVirtMemSafeWrite, since we already used it to deliver the agent at this address, so if that
729  // worked, these will work too.
730 
731  status = IntKernVirtMemWrite(gPtDriverTableAddress + ((rip & TABLE_BUCKET_MASK) << 12) + idx * 16, 8, &rip);
732  if (!INT_SUCCESS(status))
733  {
734  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
736  goto release_and_exit;
737  }
738 
739  status = IntKernVirtMemWrite(gPtDriverTableAddress + ((rip & TABLE_BUCKET_MASK) << 12) + idx * 16 + 8, 8, &hnd);
740  if (!INT_SUCCESS(status))
741  {
742  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
744  goto release_and_exit;
745  }
746 
747  // Store the actual handler code.
748  status = IntKernVirtMemWrite(gPtDriverHandlersAddress + gPtHandlerIndex * MAX_HANDLER_SIZE, len, handler);
749  if (!INT_SUCCESS(status))
750  {
751  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
753  goto release_and_exit;
754  }
755 
756  // Store an INT20 there.
757  status = IntMemClkModifyPatchedData(Candidate->CloakHandle, 0, Candidate->Instruction.Length,
758  &int20[16 - Candidate->Instruction.Length]);
759  if (!INT_SUCCESS(status))
760  {
761  ERROR("[ERROR] IntMemClkModifyPatchedData failed: 0x%08x\n", status);
763  goto release_and_exit;
764  }
765 
766  gPtHandlerIndex++;
768 
769  Candidate->PtInstruction = TRUE;
770  Candidate->Monitored = FALSE;
771 
772  // We modified the instruction, so flush it now.
774 
775  TRACE("[PTCORE] Successfully patched instruction at RIP 0x%016llx, HND 0x%016llx, index %d, criteria %d!\n",
776  Candidate->Gla, hnd, gPtHandlerIndex, criteria);
777  }
778  else
779  {
780  // We can delete all instructions which are not viable candidates.
781  IntPtiDeleteInstruction(Candidate);
782  }
783 
784 release_and_exit:
785  IntResumeVcpus();
786 
787  return INT_STATUS_SUCCESS;
788 }
789 
790 
791 INTSTATUS
793  _In_ QWORD Rip
794  )
803 {
804  LIST_ENTRY *list;
805 
806  if (!gPtMonitored)
807  {
808  return INT_STATUS_NOT_FOUND;
809  }
810 
811  list = gPtiCandidatesList.Flink;
812  while (list != &gPtiCandidatesList)
813  {
815  QWORD oldiret = 0;
816  INTSTATUS status;
817 
818  list = list->Flink;
819 
820  // We store the address as saved by INT 20 in R9, so the address immediately after the instruction.
821  if (pPtc->Gla + pPtc->Instruction.Length == Rip)
822  {
823  LOG("[PTCORE] Found instruction to remove, RIP 0x%016llx\n", pPtc->Gla);
824 
825  IntDumpGva(gVcpu->Regs.Rsp, 0x80, gVcpu->Regs.Cr3);
826 
827  // Patch the return address of the IRETQ instruction so it points to the old instruction.
828  status = IntKernVirtMemRead(gVcpu->Regs.Rsp + 0x50, sizeof(oldiret), &oldiret, NULL);
829  if (!INT_SUCCESS(status))
830  {
831  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
833  }
834 
835  oldiret -= pPtc->Instruction.Length;
836 
838  gVcpu->Regs.Rsp + 0x50,
839  sizeof(oldiret),
840  &oldiret,
841  IG_CS_RING_0);
842  if (!INT_SUCCESS(status))
843  {
844  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
846  }
847 
849 
850  break;
851  }
852  }
853 
854  return INT_STATUS_SUCCESS;
855 }
856 
857 
858 INTSTATUS
860  void
861  )
871 {
872  INTSTATUS status;
873  PTI_CANDIDATE target;
874  RBNODE *result;
875  PPTI_CANDIDATE pPtc;
876 
877  if (!gPtMonitored)
878  {
879  return INT_STATUS_NOT_FOUND;
880  }
881 
883 
884  target.Gla = gVcpu->Regs.Rip;
885 
886  status = RbLookupNode(&gPtiCandidatesTree, &target.Node, &result);
887  if (!INT_SUCCESS(status))
888  {
890  return INT_STATUS_NOT_FOUND;
891  }
892 
893  pPtc = CONTAINING_RECORD(result, PTI_CANDIDATE, Node);
894 
895  if (pPtc->Monitored)
896  {
898  }
899 
901 
903 }
904 
905 
906 void
908  void
909  )
913 {
914  DWORD total, monitored, inpt, relevant;
915 
916  total = monitored = inpt = relevant = 0;
917 
918  if (!gPtMonitored)
919  {
920  return;
921  }
922 
923  // Search for the matching instruction.
924  for (LIST_ENTRY *list = gPtiCandidatesList.Flink; list != &gPtiCandidatesList; )
925  {
926  PPTI_CANDIDATE pPtc = CONTAINING_RECORD(list, PTI_CANDIDATE, Link);
927  CHAR text[ND_MIN_BUF_SIZE];
928 
929  list = list->Flink;
930 
931  total++;
932 
933  if (pPtc->Monitored)
934  {
935  monitored++;
936  }
937 
938  NdToText(&pPtc->Instruction, pPtc->Gla, ND_MIN_BUF_SIZE, text);
939 
940  LOG("%04d ---- RIP 0x%016llx, instruction %s\n", total, pPtc->Gla, text);
941  }
942 
943  LOG("We have %d total instructions modified, %d remaining to inspect, %d in page-tables, %d relevant\n",
944  total, monitored, inpt, relevant);
945 }
946 
947 
948 static INTSTATUS
950  _In_opt_ void *Context,
951  _In_ void *Hook,
952  _In_ QWORD Address,
953  _Out_ INTRO_ACTION *Action
954  )
965 {
966  UNREFERENCED_PARAMETER(Context);
968  UNREFERENCED_PARAMETER(Address);
969 
970  LOG("[PTCORE] Write took place inside the PT agent at GLA %llx, from RIP %llx!\n",
971  gVcpu->Gla,
972  gVcpu->Regs.Rip);
973 
975 
976  *Action = introGuestNotAllowed;
977 
978  return INT_STATUS_SUCCESS;
979 }
980 
981 
982 static INTSTATUS
984  _In_opt_ void *Context,
985  _In_ void *Hook,
986  _In_ QWORD Address,
987  _Out_ INTRO_ACTION *Action
988  )
1000 {
1001  UNREFERENCED_PARAMETER(Context);
1002  UNREFERENCED_PARAMETER(Hook);
1003  UNREFERENCED_PARAMETER(Address);
1004 
1005  LOG("[PTCORE] Exec took place inside the PT agent at GLA %llx, from RIP %llx!\n",
1006  gVcpu->Gla,
1007  gVcpu->Regs.Rip);
1008 
1009  IntEnterDebugger();
1010 
1011  *Action = introGuestNotAllowed;
1012 
1013  return INT_STATUS_SUCCESS;
1014 }
1015 
1016 
1017 static INTSTATUS
1019  void
1020  )
1030 {
1031  INTSTATUS status;
1032  DWORD i;
1033  PIMAGE_SECTION_HEADER pSec;
1034  DWORD sectionRva = 0;
1035  DWORD sectionCount = 0;
1036 
1037  if (NULL == gPtDriverImage)
1038  {
1040  }
1041 
1042  status = IntPeListSectionsHeaders(0, gPtDriverImage, gPtDriverSize, &sectionRva, &sectionCount);
1043  if (!INT_SUCCESS(status))
1044  {
1045  ERROR("[ERROR] IntPeIterateSections failed with status: 0x%08x\n", status);
1046  return status;
1047  }
1048 
1049  pSec = (IMAGE_SECTION_HEADER *)(gPtDriverImage + sectionRva);
1050  for (i = 0; i < sectionCount; i++, pSec++)
1051  {
1052  TRACE("[PTCORE] Hooking section %d (%s) with characteristics 0x%08x against writes\n",
1053  i, pSec->Name, pSec->Characteristics);
1054 
1055  if (0 == (pSec->Characteristics & IMAGE_SCN_MEM_WRITE))
1056  {
1063  NULL, 0, NULL);
1064  if (!INT_SUCCESS(status))
1065  {
1066  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
1067  }
1068  }
1069 
1070  if (0 == (pSec->Characteristics & IMAGE_SCN_MEM_EXECUTE))
1071  {
1072  TRACE("[PTCORE] Hooking section %d (%s) with characteristics 0x%08x against execute\n",
1073  i, pSec->Name, pSec->Characteristics);
1074 
1081  NULL, 0, NULL);
1082  if (!INT_SUCCESS(status))
1083  {
1084  ERROR("[ERROR] IntHookObjectHookRegion failed: 0x%08x\n", status);
1085  }
1086  }
1087  }
1088 
1089  return INT_STATUS_SUCCESS;
1090 }
1091 
1092 
1093 static INTSTATUS
1095  void
1096  )
1104 {
1105  INTSTATUS status;
1106 
1107  IntPauseVcpus();
1108 
1109  // Modify all the candidate instructions.
1111  if (!INT_SUCCESS(status))
1112  {
1113  ERROR("[ERROR] IntPtiMonitorAllPtWriteCandidates failed: 0x%08x\n", status);
1114  goto cleanup_and_exit;
1115  }
1116 
1117  // Inside the GPA hook system, remove the EPT hook on all page-tables and make sure we don't place any new ones.
1118  status = IntHookGpaEnablePtCache();
1119  if (!INT_SUCCESS(status))
1120  {
1121  ERROR("[ERROR] IntHookGpaEnablePtCache failed: 0x%08x\n", status);
1122  goto cleanup_and_exit;
1123  }
1124 
1125  LOG("[PTCORE] PT filtering enabled!\n");
1126 
1128 
1129  status = INT_STATUS_SUCCESS;
1130 
1131 cleanup_and_exit:
1132  if (!INT_SUCCESS(status))
1133  {
1135 
1137  }
1138 
1139  IntResumeVcpus();
1140 
1141  return status;
1142 }
1143 
1144 
1145 static INTSTATUS
1147  void
1148  )
1159 {
1160  INTSTATUS status;
1161 
1162  IntPauseVcpus();
1163 
1164  // Restore all the candidate instructions.
1166  if (!INT_SUCCESS(status))
1167  {
1168  ERROR("[ERROR] IntPtiRestoreAllPtWriteCandidates failed: 0x%08x\n", status);
1169  }
1170 
1171  // Re-enable EPT hook on the page tables.
1172  status = IntHookGpaDisablePtCache();
1173  if (!INT_SUCCESS(status))
1174  {
1175  ERROR("[ERROR] IntHookGpaDisablePtCache failed: 0x%08x\n", status);
1176  }
1177 
1178  // Remove mem-tables entries that were allocated inside the agent.
1179  status = IntMtblRemoveAgentEntries();
1180  if (!INT_SUCCESS(status))
1181  {
1182  ERROR("[ERROR] IntMtblRemoveAgentEntries failed: 0x%08x\n", status);
1183  }
1184 
1185  LOG("[PTCORE] PT filtering disabled!\n");
1186 
1188 
1189  IntResumeVcpus();
1190 
1191  return INT_STATUS_SUCCESS;
1192 }
1193 
1194 
1195 static INTSTATUS
1197  _In_ QWORD GuestVirtualAddress,
1198  _In_ DWORD AgentTag,
1199  _In_opt_ void *Context
1200  )
1210 {
1211  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1212  UNREFERENCED_PARAMETER(AgentTag);
1213  UNREFERENCED_PARAMETER(Context);
1214 
1215  LOG("[PTCORE] PT filter loader deployed!\n");
1216 
1217  return INT_STATUS_SUCCESS;
1218 }
1219 
1220 
1221 static QWORD
1223  _In_ QWORD GuestVirtualAddress,
1224  _In_ DWORD MaxSize,
1225  _In_opt_ void *Context
1226  )
1257 {
1258  INTSTATUS status;
1260  QWORD res;
1261 
1262  UNREFERENCED_PARAMETER(Context);
1263  UNREFERENCED_PARAMETER(MaxSize);
1264 
1265  res = 1;
1266 
1268  if (NULL == gPtDriverImage)
1269  {
1270  return res;
1271  }
1272 
1273  gPtDriverAddress = GuestVirtualAddress;
1274 
1275  LOG("[PTCORE] Delivering the PT filter at GVA %llx, size 0x%x...\n", gPtDriverAddress, gPtDriverSize);
1276 
1277  IntPauseVcpus();
1278 
1279  // Load the image.
1280  status = IntLdrLoadPEImage(gPtDriverx64, sizeof(gPtDriverx64), gPtDriverAddress, gPtDriverImage, gPtDriverSize,
1282  if (!INT_SUCCESS(status))
1283  {
1284  ERROR("[ERROR] IntLdrLoadPEImage failed: 0x%08x\n", status);
1285  goto cleanup_and_exit;
1286  }
1287 
1288  // Deploy the PT driver inside the guest.
1290  if (!INT_SUCCESS(status))
1291  {
1292  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1293  goto cleanup_and_exit;
1294  }
1295 
1296  // Get the cache & table sections.
1297  status = IntPeGetSectionHeaderByName(gPtDriverAddress, NULL, ".cache", gGuest.Mm.SystemCr3, &sec);
1298  if (!INT_SUCCESS(status))
1299  {
1300  ERROR("[ERROR] .cache section not found: 0x%08x!\n", status);
1301  goto cleanup_and_exit;
1302  }
1303 
1305 
1306  TRACE("[PTCORE] Cache at 0x%016llx...\n", gPtDriverCacheAddress);
1307 
1308 
1309  status = IntPeGetSectionHeaderByName(gPtDriverAddress, NULL, ".table", gGuest.Mm.SystemCr3, &sec);
1310  if (!INT_SUCCESS(status))
1311  {
1312  ERROR("[ERROR] .table section not found: 0x%08x!\n", status);
1313  goto cleanup_and_exit;
1314  }
1315 
1317 
1318  TRACE("[PTCORE] Table at 0x%016llx...\n", gPtDriverTableAddress);
1319 
1320 
1321  status = IntPeGetSectionHeaderByName(gPtDriverAddress, NULL, ".handler", gGuest.Mm.SystemCr3, &sec);
1322  if (!INT_SUCCESS(status))
1323  {
1324  ERROR("[ERROR] .handler section not found: 0x%08x!\n", status);
1325  goto cleanup_and_exit;
1326  }
1327 
1330 
1331  TRACE("[PTCORE] Handlers at 0x%016llx...\n", gPtDriverHandlersAddress);
1332 
1333 
1334  status = IntPeGetSectionHeaderByName(gPtDriverAddress, NULL, ".main", gGuest.Mm.SystemCr3, &sec);
1335  if (!INT_SUCCESS(status))
1336  {
1337  ERROR("[ERROR] .main section not found: 0x%08x!\n", status);
1338  goto cleanup_and_exit;
1339  }
1340 
1342 
1343  TRACE("[PTCORE] Main at 0x%016llx...\n", gPtDriverMainAddress);
1344 
1345 
1346  // Fixup all the variables.
1347  status = IntPeGetSectionHeaderByName(gPtDriverAddress, NULL, ".rdata", gGuest.Mm.SystemCr3, &sec);
1348  if (!INT_SUCCESS(status))
1349  {
1350  ERROR("[ERROR] .main section not found: 0x%08x!\n", status);
1351  goto cleanup_and_exit;
1352  }
1353 
1355 
1356  TRACE("[PTCORE] .rdata at 0x%016llx...\n", gPtDriverRdataAddress);
1357 
1358 
1359  // Mem-tables area.
1360  status = IntPeGetSectionHeaderByName(gPtDriverAddress, NULL, ".memtbl", gGuest.Mm.SystemCr3, &sec);
1361  if (!INT_SUCCESS(status))
1362  {
1363  ERROR("[ERROR] .memtbl section not found: 0x%08x!\n", status);
1364  goto cleanup_and_exit;
1365  }
1366 
1369 
1370  TRACE("[PTCORE] .memtbl at 0x%016llx...\n", gPtDriverMemtableAddress);
1371 
1372 
1373  // GLA check function.
1374  status = IntPeGetSectionHeaderByName(gPtDriverAddress, NULL, ".lock", gGuest.Mm.SystemCr3, &sec);
1375  if (!INT_SUCCESS(status))
1376  {
1377  ERROR("[ERROR] .lock section not found: 0x%08x!\n", status);
1378  goto cleanup_and_exit;
1379  }
1380 
1382 
1383  TRACE("[PTCORE] .lock at 0x%016llx...\n", gPtDriverCheckFunction);
1384 
1385 
1386  // No need to use IntVirtMemSafeWrite here, as it was already used to deploy the agent, so if that worked,
1387  // these will work too.
1388 
1389  // Store the self-map index.
1391  // Store the relevant bits.
1393  // Store the attached process offset in KTHREAD.
1394  IntKernVirtMemPatchQword(gPtDriverRdataAddress + 16, WIN_KM_FIELD(Thread, AttachedProcess));
1395  // Store the process offset in KTHREAD.
1397  // Store the spare offset in EPROCESS.
1399 
1400 
1401  // Create a hook object for the PT filtering driver.
1403  if (!INT_SUCCESS(status))
1404  {
1405  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
1406  goto cleanup_and_exit;
1407  }
1408 
1409  // Hook the ptfilter driver inside the guest - for now, we only hook the non-writable sections, as during the
1410  // initialization, the ptfilter driver will write those sections.
1411  status = IntPtiHookPtDriver();
1412  if (!INT_SUCCESS(status))
1413  {
1414  ERROR("[ERROR] IntPtiHookPtDriver failed: 0x%08x\n", status);
1415  goto cleanup_and_exit;
1416  }
1417 
1418  // Hook the ptfilter handler and make it point inside our driver.
1420  if (!INT_SUCCESS(status))
1421  {
1422  ERROR("[ERROR] IntVeHookVirtualizationExceptionHandler failed: 0x%08x\n", status);
1423  goto cleanup_and_exit;
1424  }
1425 
1427 
1428  res = 0;
1429 
1430 cleanup_and_exit:
1431  IntResumeVcpus();
1432 
1433  return res;
1434 }
1435 
1436 
1437 static INTSTATUS
1439  _In_ QWORD GuestVirtualAddress,
1440  _In_ DWORD ErrorCode,
1441  _In_ DWORD AgentTag,
1442  _In_opt_ void *Context
1443  )
1457 {
1458  UNREFERENCED_PARAMETER(Context);
1459  UNREFERENCED_PARAMETER(AgentTag);
1460  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1461 
1462  if (ErrorCode != 0)
1463  {
1464  ERROR("[ERROR] PT filter injection failed with error 0x%08x, will bail out.\n", ErrorCode);
1465  IntPtiResetState();
1466  }
1467  else
1468  {
1470  gPtDeployed = TRUE;
1471 
1472  if (gPtEnableMonitor)
1473  {
1474  INTSTATUS status = IntPtiEnableFiltering();
1475  if (!INT_SUCCESS(status))
1476  {
1477  ERROR("[ERROR] IntPtiEnableFiltering failed: 0x%08x\n", status);
1478  }
1479  else
1480  {
1481  LOG("[PTCORE] PT filter loaded successfully!\n");
1482  }
1483  }
1484  else
1485  {
1486  if (!gGuest.UninitPrepared)
1487  {
1488  ERROR("[ERROR] PT filter deliver failed, cannot enable monitoring!\n");
1489  }
1490  else
1491  {
1492  WARNING("[WARNING] PT filter deliver failed, cannot enable monitoring!\n");
1493  }
1494  }
1495  }
1496 
1497  return INT_STATUS_SUCCESS;
1498 }
1499 
1500 
1501 static INTSTATUS
1503  _In_ QWORD GuestVirtualAddress,
1504  _In_ DWORD AgentTag,
1505  _In_opt_ void *Context
1506  )
1516 {
1517  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1518  UNREFERENCED_PARAMETER(AgentTag);
1519  UNREFERENCED_PARAMETER(Context);
1520 
1522 
1523  LOG("[PTCORE] PT filter unloader deployed!\n");
1524 
1525  return INT_STATUS_SUCCESS;
1526 }
1527 
1528 
1529 static INTSTATUS
1531  void
1532  )
1542 {
1543  INTSTATUS status = INT_STATUS_SUCCESS;
1544 
1545  // Restore the int 20 hook.
1546  if (NULL != gPtDriverCloak)
1547  {
1549  if (!INT_SUCCESS(status))
1550  {
1551  ERROR("[ERROR] IntMemClkUncloakRegion failed: 0x%08x\n", status);
1552  }
1553 
1554  gPtDriverCloak = NULL;
1555  }
1556 
1557  // Remove the PT driver agent hooks.
1558  if (NULL != gPtDriverHook)
1559  {
1561  if (!INT_SUCCESS(status))
1562  {
1563  ERROR("[ERROR] IntHookObjectDestroy failed: 0x%08x\n", status);
1564  }
1565  }
1566 
1567  if (NULL != gPtDriverImage)
1568  {
1570  }
1571 
1572  return status;
1573 }
1574 
1575 
1576 static QWORD
1578  _In_ QWORD GuestVirtualAddress,
1579  _In_ DWORD MaxSize,
1580  _In_opt_ void *Context
1581  )
1599 {
1600  INTSTATUS status;
1601  BOOLEAN postpone = FALSE;
1602 
1603  UNREFERENCED_PARAMETER(Context);
1604  UNREFERENCED_PARAMETER(MaxSize);
1605  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1606 
1607  // Make sure there are no RIPs inside the agent.
1608  IntPauseVcpus();
1609 
1611  if (INT_STATUS_CANNOT_UNLOAD == status)
1612  {
1613  LOG("[WARNING] Cannot unload yet, RIPs still point inside the filter!\n");
1614  postpone = TRUE;
1615  }
1616 
1617  IntResumeVcpus();
1618 
1619  if (postpone)
1620  {
1621  return 1;
1622  }
1623 
1624  status = IntPtiUnhookPtFilter();
1625  if (!INT_SUCCESS(status))
1626  {
1627  ERROR("[ERROR] IntPtiUnhookPtFilter failed: 0x%08x\n", status);
1628  }
1629  else
1630  {
1631  LOG("[PTCORE] PT filter unhooked successfully!\n");
1632  }
1633 
1634  return 0;
1635 }
1636 
1637 
1638 static INTSTATUS
1640  _In_ QWORD GuestVirtualAddress,
1641  _In_ DWORD ErrorCode,
1642  _In_ DWORD AgentTag,
1643  _In_opt_ void *Context
1644  )
1653 {
1654  INTSTATUS status;
1655 
1656  UNREFERENCED_PARAMETER(Context);
1657  UNREFERENCED_PARAMETER(AgentTag);
1658  UNREFERENCED_PARAMETER(ErrorCode);
1659  UNREFERENCED_PARAMETER(GuestVirtualAddress);
1660 
1661  IntPtiResetState();
1662 
1663  LOG("[PTCORE] PT filter unloaded successfully!\n");
1664 
1665  status = IntWinPowDisableSpinWait();
1666  if (!INT_SUCCESS(status))
1667  {
1668  ERROR("[ERROR] IntWinPowDisableSpinWait failed: 0x%08x\n", status);
1669  }
1670 
1671  return INT_STATUS_SUCCESS;
1672 }
1673 
1674 
1675 INTSTATUS
1677  void
1678  )
1691 {
1692  INTSTATUS status;
1693 
1694  if (gGuest.UninitPrepared)
1695  {
1696  ERROR("[ERROR] Uninit in progress, cannot deploy the PT filtering agent now!\n");
1698  }
1699 
1700  // Normally, we support filtering any x64 Windows. However, we intend to use it only on RS4 and newer.
1702  {
1704  }
1705 
1706  // If the agent has already been deployed, bail out.
1708  {
1710  }
1711 
1712  // Get the ptfilter driver info.
1713  status = IntLdrGetImageSizeAndEntryPoint(gPtDriverx64, sizeof(gPtDriverx64), &gPtDriverSize, &gPtDriverEntryPoint);
1714  if (!INT_SUCCESS(status))
1715  {
1716  ERROR("[ERROR] IntLdrGetImageSizeAndEntryPoint failed: 0x%08x\n", status);
1717  return status;
1718  }
1719 
1721 
1724  NULL, 0, NULL, 0, NULL);
1725  if (!INT_SUCCESS(status))
1726  {
1727  ERROR("[ERROR] IntAgentInject failed: 0x%08x\n", status);
1729  }
1730 
1731  return status;
1732 }
1733 
1734 
1735 INTSTATUS
1737  _In_ DWORD AgOpts
1738  )
1752 {
1753  INTSTATUS status;
1754 
1755  // Agent not deployed - nothing to do here.
1757  {
1759  }
1760 
1762  {
1763  status = IntPtiDisableFiltering();
1764  if (!INT_SUCCESS(status))
1765  {
1766  ERROR("[ERROR] IntPtiDisableFiltering failed: 0x%08x\n", status);
1767  }
1768 
1769  status = IntPtiUnhookPtFilter();
1770  if (!INT_SUCCESS(status))
1771  {
1772  ERROR("[ERROR] IntPtiUnhookPtFilter failed: 0x%08x\n", status);
1773  }
1774 
1775  // Overwrite the whole driver with zeros.
1776  // This SHOULD be safe. We will only get here if `BugCheckInProgress` is set.
1777  // `BugCheckInProgress` will only be set if the guest reaches our hook inside
1778  // `KiDisplayBlueScreen` and by that time we *assume* that there's only one
1779  // VCPU running (with the rip inside our hook). Thus, there *shouldn't* be any
1780  // other VCPUs inside the PT Filter.
1781  for (DWORD page = 0; page < gPtDriverSize; page += PAGE_SIZE)
1782  {
1783  PBYTE pMap;
1784 
1785  status = IntVirtMemMap(gPtDriverAddress + page, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pMap);
1786  if (!INT_SUCCESS(status))
1787  {
1788  continue;
1789  }
1790 
1791  memzero(pMap, PAGE_SIZE);
1792 
1793  IntVirtMemUnmap(&pMap);
1794  }
1795 
1796  IntPtiResetState();
1797 
1798  LOG("[PTCORE] PT filter unhooked successfully!\n");
1799 
1800  return status;
1801  }
1802 
1804 
1806  NULL, gPtDriverx64, sizeof(gPtDriverx64), TRUE,
1808  NULL, AgOpts, NULL, 0, NULL);
1809 }
1810 
1811 
1812 BOOLEAN
1814  _In_ QWORD Ptr,
1815  _In_ THS_PTR_TYPE Type
1816  )
1825 {
1826  if (!gPtDeployed || (0 == gPtDriverAddress))
1827  {
1828  return FALSE;
1829  }
1830  else
1831  {
1832  if (ptrLiveRip == Type)
1833  {
1834  return Ptr >= gPtDriverAddress && Ptr < gPtDriverAddress + gPtDriverSize;
1835  }
1836  // >, because the base of our agent may still be on some stacks due to the allocation.
1837  else if (Ptr > gPtDriverAddress && Ptr < gPtDriverAddress + gPtDriverSize)
1838  {
1839  IMAGE_SECTION_HEADER sec = { 0 };
1840  INTSTATUS status;
1841 
1842  status = IntPeGetSectionHeaderByRva(gPtDriverAddress, NULL, (DWORD)(Ptr - gPtDriverAddress), &sec);
1843  if (!INT_SUCCESS(status))
1844  {
1845  ERROR("[ERROR] IntPeGetSectionHeaderByRva failed for address %llx, agent at 0x%016llx, size %d\n",
1847  return FALSE;
1848  }
1849 
1850  // If the detected pointer is inside an executable section, we will bail out. Otherwise, simply ignore
1851  // pointers which don't lead to code, as they are most likely stray pointers.
1852  return (!!(sec.Characteristics & IMAGE_SCN_MEM_EXECUTE));
1853  }
1854  else
1855  {
1856  return FALSE;
1857  }
1858  }
1859 }
1860 
1861 
1862 INTSTATUS
1864  _In_ QWORD Gpa
1865  )
1877 {
1878  INTSTATUS status;
1879  QWORD cacheIndex = Gpa & 0x1FF000;
1880  QWORD cacheGva = gPtDriverCacheAddress + cacheIndex;
1881  PQWORD pEntries = NULL;
1882 
1883  if (0 == gPtDriverCacheAddress || 0 == cacheGva || 0 == gPtDriverAddress)
1884  {
1886  }
1887 
1888  Gpa &= PHYS_PAGE_MASK;
1889 
1890  status = IntVirtMemMap(cacheGva, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pEntries);
1891  if (!INT_SUCCESS(status))
1892  {
1893  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", cacheGva, status);
1894  return status;
1895  }
1896 
1897  for (DWORD i = 0; i < 512; i++)
1898  {
1899  if (pEntries[i] == Gpa)
1900  {
1901  pEntries[i] = 0;
1902  }
1903  }
1904 
1905  IntVirtMemUnmap(&pEntries);
1906 
1907  return INT_STATUS_SUCCESS;
1908 }
1909 
1910 
1911 INTSTATUS
1913  _In_ QWORD Gpa
1914  )
1927 {
1928  INTSTATUS status;
1929  DWORD i;
1930  QWORD cacheIndex = Gpa & 0x1FF000;
1931  QWORD cacheGva = gPtDriverCacheAddress + cacheIndex;
1932  PQWORD pEntries = NULL;
1933 
1934  if (0 == gPtDriverCacheAddress || 0 == cacheGva || 0 == gPtDriverAddress)
1935  {
1937  }
1938 
1939  Gpa &= PHYS_PAGE_MASK;
1940 
1941  status = IntVirtMemMap(cacheGva, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &pEntries);
1942  if (!INT_SUCCESS(status))
1943  {
1944  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", cacheGva, status);
1945  return status;
1946  }
1947 
1948  for (i = 0; i < 512; i++)
1949  {
1950  if (pEntries[i] == Gpa)
1951  {
1952  break;
1953  }
1954 
1955  if (pEntries[i] == 0)
1956  {
1957  pEntries[i] = Gpa;
1958  break;
1959  }
1960  }
1961 
1962  if (i == 512)
1963  {
1964  pEntries[__rdtsc() % 512] = Gpa;
1965  }
1966 
1967  IntVirtMemUnmap(&pEntries);
1968 
1969  return INT_STATUS_SUCCESS;
1970 }
1971 
1972 
1973 QWORD
1975  void
1976  )
1982 {
1983  return gPtDriverAddress;
1984 }
1985 
1986 
1987 QWORD
1989  _In_ QWORD Rip,
1990  _In_ DWORD Size
1991  )
2007 {
2008  INTSTATUS status;
2009  QWORD ptr;
2010  QWORD idx;
2011 
2013  {
2014  return 0;
2015  }
2016 
2018  {
2019  ERROR("[ERROR] Could not allocate space in agent: requested %d bytes, used %d bytes, total %d bytes\n",
2021  return 0;
2022  }
2023 
2025 
2026  idx = gPtTableIndexes[Rip & TABLE_BUCKET_MASK];
2027  if (idx >= MAX_ENTRIES_PER_BUCKET)
2028  {
2029  ERROR("[ERROR] Maximum table index reached for bucket %lld!\n", Rip & TABLE_BUCKET_MASK);
2030  IntEnterDebugger();
2031  return 0;
2032  }
2033 
2034  // Store the handler RIP.
2035  status = IntKernVirtMemWrite(gPtDriverTableAddress + ((Rip & TABLE_BUCKET_MASK) << 12) + idx * 16,
2036  sizeof(Rip), &Rip);
2037  if (!INT_SUCCESS(status))
2038  {
2039  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
2040  IntEnterDebugger();
2041  return 0;
2042  }
2043 
2044  status = IntKernVirtMemWrite(gPtDriverTableAddress + ((Rip & TABLE_BUCKET_MASK) << 12) + idx * 16 + 8,
2045  sizeof(ptr), &ptr);
2046  if (!INT_SUCCESS(status))
2047  {
2048  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
2049  IntEnterDebugger();
2050  return 0;
2051  }
2052 
2053  TRACE("[PTFILTER] Allocated memtable space for RIP 0x%016llx at 0x%016llx, size %d bytes\n", Rip, ptr, Size);
2054 
2056 
2057  gPtMemtableSize += Size;
2058 
2059  return ptr;
2060 }
2061 
2062 
2063 void
2065  void
2066  )
2070 {
2072 }
#define IMAGE_SCN_MEM_EXECUTE
Definition: winpe.h:472
static INTSTATUS IntPtiCompleteLoader(QWORD GuestVirtualAddress, DWORD ErrorCode, DWORD AgentTag, void *Context)
Called once the PT loader finished execution.
Definition: ptfilter.c:1438
#define _In_opt_
Definition: intro_sal.h:16
BOOLEAN gPtDeployed
Definition: ptfilter.c:77
BOOLEAN Monitored
TRUE if the instruction is being monitored. FALSE if it has been restored.
Definition: ptfilter.c:72
INTSTATUS IntPeGetSectionHeaderByRva(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD GuestRva, IMAGE_SECTION_HEADER *SectionHeader)
Given a relative virtual address, return the section header which describes the section the RVA lies ...
Definition: winpe.c:707
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
#define ROUND_UP(what, to)
Definition: introdefs.h:158
static INTSTATUS IntPtiDisableFiltering(void)
Disable PT candidate instructions monitoring.
Definition: ptfilter.c:1146
INTSTATUS IntPtiCacheAdd(QWORD Gpa)
Add a guest-physical address to the PT filter cache of entries for which an exit is not required...
Definition: ptfilter.c:1912
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
LIST_HEAD gPtiCandidatesList
We store each candidate in a RB tree, ordered by the location. We also store them in a linked-list...
Definition: ptfilter.c:153
QWORD gPtDriverMainAddress
Main function inside the PT filter.
Definition: ptfilter.c:85
INTSTATUS IntPtiRemovePtFilter(DWORD AgOpts)
Removes the PT filter.
Definition: ptfilter.c:1736
BOOLEAN gPtPendingRemove
Definition: ptfilter.c:77
#define THS_CHECK_PTFILTER
Will check if any RIP is inside the PT filter agent.
uint8_t BYTE
Definition: intro_types.h:47
DWORD gPtTableIndexes[MAX_LUT_PAGES]
Lookup table of indexes.
Definition: ptfilter.c:95
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:37
static INTSTATUS IntPtCompleteUnloader(QWORD GuestVirtualAddress, DWORD ErrorCode, DWORD AgentTag, void *Context)
Called once the agent unloader has finished executing.
Definition: ptfilter.c:1639
INTSTATUS IntKernVirtMemWrite(QWORD KernelGva, DWORD Length, void *Buffer)
Writes data to a guest kernel virtual memory range.
Definition: introcore.c:699
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
QWORD gPtDriverHandlersAddress
Main handler inside the PT filter.
Definition: ptfilter.c:84
INTSTATUS IntKernVirtMemPatchDword(QWORD GuestVirtualAddress, DWORD Data)
Writes 4 bytes in the guest kernel memory.
Definition: introcore.c:950
#define LDR_FLAG_FIX_RELOCATIONS
If flag is set, the relocations will be applied.
Definition: loader.h:11
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
#define INT_STATUS_DISASM_ERROR
Indicates a decoder error.
Definition: introstatus.h:442
#define STATS_EXIT(id)
Definition: stats.h:160
INTSTATUS IntMemClkModifyPatchedData(void *CloakHandle, DWORD Offset, DWORD Size, const void *Data)
Modifies the patched data inside the guest memory.
Definition: memcloak.c:795
#define IntEnterDebugger()
Definition: introcore.h:373
INTSTATUS IntHookGpaDisablePtCache(void)
Disable PT filtering.
Definition: hook_gpa.c:1442
BOOLEAN gPtEnableMonitor
Definition: ptfilter.c:77
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
#define IC_ANY_VAS
Definition: icache.h:86
Measures the instruction search done for the page table filtering agent.
Definition: stats.h:62
QWORD Gla
Linear address where the candidate was found.
Definition: ptfilter.c:69
QWORD gPtDriverAddress
Guest virtual address where the PT filter was injected.
Definition: ptfilter.c:79
void FUNC_RbTreeNodeFree(RBNODE *Node)
Definition: rbtree.h:47
INTSTATUS IntPeGetSectionHeaderByIndex(QWORD ImageBase, BYTE *ImageBaseBuffer, DWORD Index, IMAGE_SECTION_HEADER *SectionHeader)
Return the section header located on position Index (0 based).
Definition: winpe.c:838
#define IMAGE_SCN_MEM_WRITE
Definition: winpe.h:474
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD gPtDriverCheckFunction
The check function inside the PT filter.
Definition: ptfilter.c:88
BOOLEAN gPtMonitored
Indicate the PT filter state.
Definition: ptfilter.c:77
QWORD gPtDriverMemtableAddress
Section used to store mem-tables (checkout memtables.c).
Definition: ptfilter.c:87
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
Definition: rbtree.h:34
#define PAGE_OFFSET
Definition: pgtable.h:32
RBTREE gPtiCandidatesTree
PT candidate instruction RB tree root.
Definition: ptfilter.c:156
INTSTATUS RbLookupNode(RBTREE *Tree, RBNODE *NodeToSearch, RBNODE **NodeFound)
Definition: rbtree.c:517
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
void * CloakHandle
Cloak handle used to hide the INT3/INT 20.
Definition: ptfilter.c:71
struct _PTI_CANDIDATE * PPTI_CANDIDATE
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
#define PHYS_PAGE_MASK
Definition: pgtable.h:38
int INTSTATUS
The status data type.
Definition: introstatus.h:24
static void IntPtiRbTreeNodeFree(RBNODE *Node)
Called on RB tree node free.
Definition: ptfilter.c:105
DWORD OSVersion
Os version.
Definition: guests.h:281
void IntPtiDumpStats(void)
Dump PT filtering statistics.
Definition: ptfilter.c:907
int FUNC_RbTreeNodeCompare(RBNODE *Left, RBNODE *Right)
Definition: rbtree.h:59
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
The PT filter loader.
Definition: aghcall.h:54
INSTRUX Instruction
The decoded instruction.
Definition: ptfilter.c:70
struct _PTI_CANDIDATE PTI_CANDIDATE
static INTSTATUS IntPtiMonitorAllPtWriteCandidates(void)
Scan the .text section of the NT image for PT candidates.
Definition: ptfilter.c:213
QWORD Flags
Definition: glueiface.h:49
static INTSTATUS IntPtiHandleWrite(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
This callback handles writes that take place inside the in-guest PT filter. All attempts will be bloc...
Definition: ptfilter.c:949
INTSTATUS IntPtiHandleInt3(void)
This function is the main INT3 handler.
Definition: ptfilter.c:859
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
static void IntPtiDeleteInstruction(PPTI_CANDIDATE Candidate)
Delete a PT candidate instruction.
Definition: ptfilter.c:186
#define IC_TAG_PTI_DRV
PTI driver image.
Definition: memtags.h:114
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
RBNODE Node
RB node for this entry.
Definition: ptfilter.c:68
INTSTATUS IntThrSafeCheckThreads(QWORD Options)
Checks if any of the guest threads have their RIP or have any stack pointers pointing to regions of c...
Will write the contents of the patched data inside the guest.
Definition: memcloak.h:54
void IntPtiHandleGuestResumeFromSleep(void)
Sets PtFilterWaiting to true if PT filtering was enabled, or to false otherwise.
Definition: ptfilter.c:2064
static int IntPtiRbTreeNodeCompareRip(RBNODE *Left, RBNODE *Right)
RB tree node compare function.
Definition: ptfilter.c:119
#define LOG(fmt,...)
Definition: glue.h:61
QWORD IntPtiAllocMemtableSpace(QWORD Rip, DWORD Size)
Allocate space for a mem-table.
Definition: ptfilter.c:1988
#define TABLE_BUCKET_MASK
Definition: ptfilter.c:59
BOOLEAN PtFilterWaiting
True if the in-guest PT filter was not yet injected, but it should be.
Definition: guests.h:345
static INTSTATUS IntPtiHookPtDriver(void)
Protect the in-guest PT filter against unauthorized access.
Definition: ptfilter.c:1018
BOOLEAN IntPtiIsPtrInAgent(QWORD Ptr, THS_PTR_TYPE Type)
Check if an address points inside the PT filter. Ignore non-executable sections when doing so...
Definition: ptfilter.c:1813
Measures the INT3 exits generated by the page table filtering mechanism.
Definition: stats.h:60
#define _Inout_
Definition: intro_sal.h:20
#define INT_STATUS_CANNOT_UNLOAD
Indicates that Introcore can not unload in a safely manner.
Definition: introstatus.h:450
INTSTATUS IntPtiInjectPtFilter(void)
Inject the PT filter inside the guest.
Definition: ptfilter.c:1676
BOOLEAN PtFilterEnabled
If True, the in-guest PT filter is enabled and deployed.
Definition: guests.h:338
PHOOK_EPT_ENTRY IntHookGpaGetExistingEptEntry(QWORD GpaPage)
Get the EPT entry associated with the provided guest physical page.
Definition: hook_gpa.c:161
static INTSTATUS IntPtiUnhookPtFilter(void)
Remove protection from the PT filter.
Definition: ptfilter.c:1530
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
INTSTATUS IntHookGpaEnablePtCache(void)
Enable PT filtering.
Definition: hook_gpa.c:1418
DWORD gPtMemtableSize
This indicates how much mem-table space we have allocated.
Definition: ptfilter.c:98
#define STATS_ENTER(id)
Definition: stats.h:153
uint8_t * PBYTE
Definition: intro_types.h:47
INTSTATUS IntPtiRemoveInstruction(QWORD Rip)
Remove the hook on a monitored instruction.
Definition: ptfilter.c:792
static INTSTATUS IntPtiHandleExecute(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
This callback handles instruction fetches that take place inside the in-guest PT filter. All attempts will be blocked.
Definition: ptfilter.c:983
#define MAX_ENTRIES_PER_BUCKET
Definition: ptfilter.c:56
#define REG(r)
DWORD gPtDriverSize
Size of the PT filter.
Definition: ptfilter.c:80
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
static void IntPtiResetState(void)
Reset the PT filter state (used after removing it from guest memory).
Definition: ptfilter.c:161
static INTSTATUS IntPtiInspectInstruction(PPTI_CANDIDATE Candidate)
Inspect a candidate instruction for page-table modifications.
Definition: ptfilter.c:487
#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
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:236
INTSTATUS IntPeListSectionsHeaders(QWORD ImageBase, BYTE *ImageBuffer, DWORD ImageBufferSize, DWORD *FirstSectionOffset, DWORD *SectionCount)
Will get the offset to the first section header and the number of sections from the given module...
Definition: winpe.c:473
LIST_ENTRY Link
List entry element.
Definition: ptfilter.c:67
INTSTATUS IntTranslateVirtualAddress(QWORD Gva, QWORD Cr3, QWORD *PhysicalAddress)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1999
#define TRUE
Definition: intro_types.h:30
UINT32 VirtualAddress
Definition: winpe.h:85
INTSTATUS IntDecDecodeInstructionFromBuffer(PBYTE Buffer, size_t BufferSize, IG_CS_TYPE CsType, void *Instrux)
Decode an instruction from the provided buffer.
Definition: decoder.c:308
INTSTATUS IntMtblRemoveAgentEntries(void)
Removes only the mem-table entries that were relocated inside the PT filter.
Definition: memtables.c:707
static QWORD IntPtiDeliverDriverForLoad(QWORD GuestVirtualAddress, DWORD MaxSize, void *Context)
Called by the driver loader, in order to initialize the in-guest PT-filter.
Definition: ptfilter.c:1222
#define TRACE(fmt,...)
Definition: glue.h:58
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
The PT filter unloader.
Definition: aghcall.h:55
INTSTATUS IntMemClkCloakRegion(QWORD VirtualAddress, QWORD Cr3, DWORD Size, DWORD Options, PBYTE OriginalData, PBYTE PatchedData, PFUNC_IntMemCloakWriteHandle WriteHandler, void **CloakHandle)
Hides a memory zone from the guest.
Definition: memcloak.c:548
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
INTSTATUS IntIcFlushAddress(PINS_CACHE Cache, QWORD Gva, QWORD Cr3)
Flush entries cached from a given address.
Definition: icache.c:597
union _IMAGE_SECTION_HEADER::@214 Misc
The RIP of a thread.
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
#define INT_STATUS_NO_DETOUR_EMU
Signals that no emulation is needed for this event.
Definition: introstatus.h:378
#define IntPeGetSectionHeaderByName(Base, Buff, Name, Cr3, Sec)
Definition: winpe.h:765
#define INT_STATUS_ALREADY_INITIALIZED_HINT
Definition: introstatus.h:323
BOOLEAN UninitPrepared
Definition: guests.h:320
#define WARNING(fmt,...)
Definition: glue.h:60
DWORD SelfMapIndex
The self map index.
Definition: guests.h:220
INTSTATUS IntWinAgentInject(PFUNC_AgentInjection InjectionCallback, PFUNC_AgentCompletion CompletionCallback, PFUNC_AgentDeliver DeliverCallback, void *Context, PBYTE AgentContent, DWORD AgentSize, BOOLEAN AgentInternal, DWORD AgentTag, AGENT_TYPE AgentType, const CHAR *Name, DWORD Options, const CHAR *Args, DWORD Pid, PWIN_AGENT *Agent)
Schedule an agent injection inside the guest.
Definition: winagent.c:2608
INTSTATUS IntVirtMemSafeWrite(QWORD Cr3, QWORD VirtualAddress, DWORD Size, void *Buffer, DWORD Ring)
Safely modify guest memory.
Definition: kernvm.c:498
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
void * InstructionCache
The currently used instructions cache.
Definition: guests.h:404
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:740
uint32_t DWORD
Definition: intro_types.h:49
UINT32 VirtualSize
Definition: winpe.h:83
UINT32 Characteristics
Definition: winpe.h:92
static QWORD IntPtiDeliverDriverForUnload(QWORD GuestVirtualAddress, DWORD MaxSize, void *Context)
This function is called when the boot driver is ready to unload & free the PT filter.
Definition: ptfilter.c:1577
static INTSTATUS IntPtiEnableFiltering(void)
Enable PT candidate instruction monitoring.
Definition: ptfilter.c:1094
#define MAX_HANDLER_SIZE
Definition: ptfilter.c:58
enum _INTRO_ACTION INTRO_ACTION
Event actions.
static uint64_t __rdtsc(void)
Definition: intrinsics.h:306
DWORD PtCount
Number of PT hooks.
Definition: hook_gpa.h:80
INTSTATUS IntPtiCacheRemove(QWORD Gpa)
Remove a guest physical page from the PT filter cache.
Definition: ptfilter.c:1863
Definition: rbtree.h:84
static INTSTATUS IntPtiDeployUnloader(QWORD GuestVirtualAddress, DWORD AgentTag, void *Context)
Called as soon as the PT filter unloader has been successfully injected. Disables the PT filtering...
Definition: ptfilter.c:1502
__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
INTSTATUS IntLdrLoadPEImage(PBYTE RawPe, DWORD RawPeSize, QWORD GuestVirtualAddress, PBYTE LoadedPe, DWORD VirtualPeSize, DWORD Flags)
Load the provided PE image at the provided guest virtual address, and return it in LoadedPe...
Definition: loader.c:670
DWORD gPtHandlersTotalSize
Total handlers size.
Definition: ptfilter.c:100
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
INTSTATUS IntLdrGetImageSizeAndEntryPoint(PBYTE RawPe, DWORD RawSize, DWORD *VirtualSize, DWORD *EntryPoint)
Returns the entry point and the virtual size for the provided module.
Definition: loader.c:11
#define _Function_class_(expr)
Definition: intro_sal.h:40
THS_PTR_TYPE
The type of pointer to be checked.
PBYTE gPtDriverImage
Pointer to the PT filter image.
Definition: ptfilter.c:90
QWORD gPtDriverCacheAddress
GVA of the PT filter cache - entries we do not need to generate an exit for.
Definition: ptfilter.c:82
INTSTATUS IntWinPowDisableSpinWait(void)
This function is called in order to disable spin waiting after everything we needed to be unloaded wa...
Definition: winpower.c:170
#define IC_TAG_ALLOC
Memory allocation.
Definition: memtags.h:28
#define IMAGE_SCN_MEM_DISCARDABLE
Definition: winpe.h:468
TIMER_FRIENDLY void IntDumpGva(QWORD Gva, DWORD Length, QWORD Cr3)
This function is a wrapper over IntDumpGvaEx (it uses RowLength = 16, ElementLength = 1...
Definition: dumper.c:273
void RbDeleteNode(RBTREE *Tree, RBNODE *Node)
Definition: rbtree.c:710
void * gPtDriverCloak
PT filter cloak handle.
Definition: ptfilter.c:92
#define THS_CHECK_ONLY
Will check for safeness, without moving any RIP or stack value.
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
static INTSTATUS IntPtiRestoreAllPtWriteCandidates(void)
Restore all PT candidates, by removing the INT3/INT 20 hook established on them.
Definition: ptfilter.c:461
#define HOOK_PTS_MONITORED_BITS
Definition: hook_pts.h:19
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
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 IntKernVirtMemPatchQword(QWORD GuestVirtualAddress, QWORD Data)
Writes 8 bytes in the guest kernel memory.
Definition: introcore.c:932
INTSTATUS RbInsertNode(RBTREE *Tree, RBNODE *Node)
Definition: rbtree.c:606
QWORD gPtDriverRdataAddress
Address of the rdata section inside the PT filter.
Definition: ptfilter.c:86
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:851
INTSTATUS IntMemClkUncloakRegion(void *CloakHandle, DWORD Options)
Removes a cloak region, making the original memory contents available again to the guest...
Definition: memcloak.c:970
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
void * gPtDriverHook
PT filter hook handle.
Definition: ptfilter.c:91
UINT8 Name[IMAGE_SIZEOF_SHORT_NAME]
Definition: winpe.h:79
#define MAX_LUT_PAGES
Definition: ptfilter.c:57
64-bit selector.
Definition: glueiface.h:188
static INTSTATUS IntPtiDeployLoader(QWORD GuestVirtualAddress, DWORD AgentTag, void *Context)
Called on initial loader deployment.
Definition: ptfilter.c:1196
DWORD gPtMemtableTotalSize
Total mem-table size.
Definition: ptfilter.c:99
BOOLEAN BugCheckInProgress
Definition: guests.h:333
BOOLEAN gPtPendingInject
Definition: ptfilter.c:77
Execute-access hook.
Definition: glueiface.h:300
#define RB_TREE_INIT(Name, Free, Compare)
Initializes a RBTREE structure.
Definition: introcore.h:39
INTSTATUS IntWinApiHookVeHandler(QWORD NewHandler, void **Cloak, QWORD *OldHandler, DWORD *ReplacedCodeLen, BYTE *ReplacedCode)
Hooks the #VE handler.
Definition: winapi.c:367
char CHAR
Definition: intro_types.h:56
unsigned long long * PQWORD
Definition: intro_types.h:53
#define INTRO_OPT_IN_GUEST_PT_FILTER
Enable in-guest page-table filtering (64-bit Windows only).
Definition: intro_types.h:461
QWORD IntPtiGetAgentAddress(void)
Get the guest virtual address where the PT filter resides.
Definition: ptfilter.c:1974
Write-access hook.
Definition: glueiface.h:299
QWORD gPtDriverTableAddress
Table of handlers inside the PT filter.
Definition: ptfilter.c:83
#define IMAGE_SCN_MEM_NOT_PAGED
Definition: winpe.h:470
The page table filtering agent.
Definition: glueiface.h:356
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271
QWORD Gla
The accessed guest virtual address. Valid only for EPT exits.
Definition: guests.h:102
INTSTATUS IntHookObjectCreate(DWORD ObjectType, QWORD Cr3, void **Object)
Create a new hook object.
Definition: hook_object.c:81
BOOLEAN PtInstruction
TRUE if the instruction modified e PT entry.
Definition: ptfilter.c:73
DWORD gPtHandlerIndex
Current handler index.
Definition: ptfilter.c:94
DWORD gPtDriverEntryPoint
Entry point of the PT filter.
Definition: ptfilter.c:81
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281