Bitdefender Hypervisor Memory Introspection
detours.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
10 
11 #include "detours.h"
12 #include "decoder.h"
13 #include "guests.h"
14 #include "introcpu.h"
15 #include "memcloak.h"
16 #include "slack.h"
17 #include "lixmodule.h"
18 #include "kernvm.h"
19 #include "alerts.h"
20 #include "introcrt.h"
21 
22 
24 #define DETOUR_MAX_FUNCTION_SIZE 24
25 
29 typedef struct _DETOURS_STATE
30 {
32  union
33  {
36  };
38 
41 {
42 
44 };
45 
54 #define for_each_detour(_var_name) list_for_each (gDetours.DetoursList, DETOUR, _var_name)
55 
56 
57 static INTSTATUS
59  _Inout_ DETOUR *Detour
60  )
65 //
73 {
75 
76  if (!Detour->LixFnDetour)
77  {
79  }
80 
81  TRACE("[DETOUR] Disabling detour - Function name : %s, Hijack function name: %s\n", Detour->LixFnDetour->FunctionName,
82  Detour->LixFnDetour->HijackFunctionName != NULL ? Detour->LixFnDetour->HijackFunctionName : "none");
83 
84  Detour->Disabled = TRUE;
85 
86  status = IntKernVirtMemPatchQword(Detour->LixGuestDetour + OFFSET_OF(LIX_GUEST_DETOUR, EnableOptions), 0);
87  if (!INT_SUCCESS(status))
88  {
89  ERROR("[ERROR] IntKernVirtMemWrite failed for GVA 0x%016llx with status: 0x%08x\n", Detour->LixGuestDetour, status);
90  return status;
91  }
92 
93  return INT_STATUS_SUCCESS;
94 }
95 
96 
97 static INTSTATUS
99  _Inout_ DETOUR *Detour
100  )
116 {
117  BYTE nop = 0x90;
118  BYTE atomic_nop_3[3] = {0x66, 0x66, 0x90};
119 
120  if (NULL == Detour->HandlerCloakHandle)
121  {
123  }
124 
125  Detour->Disabled = TRUE;
126 
127  if (Detour->HypercallOffset == DETOUR_INVALID_HYPERCALL)
128  {
130  }
131 
132  switch (Detour->HypercallType)
133  {
134  case hypercallTypeInt3:
135  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
136  Detour->HypercallOffset,
137  sizeof(nop),
138  &nop);
139 
140  case hypercallTypeVmcall:
141  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
142  Detour->HypercallOffset,
143  sizeof(atomic_nop_3),
144  atomic_nop_3);
145 
146  case hypercallTypeNone:
148  }
149 
151 }
152 
153 
154 static INTSTATUS
156  _Inout_ DETOUR *Detour
157  )
173 {
174  BYTE int3 = 0xCC;
175  BYTE vmcall[3] = {0x0F, 0x01, 0xC1};
176 
177  if (NULL == Detour->HandlerCloakHandle)
178  {
180  }
181 
182  Detour->Disabled = FALSE;
183 
184  if (Detour->HypercallOffset == DETOUR_INVALID_HYPERCALL)
185  {
187  }
188 
189  switch (Detour->HypercallType)
190  {
191  case hypercallTypeInt3:
192  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
193  Detour->HypercallOffset,
194  sizeof(int3),
195  &int3);
196 
197  case hypercallTypeVmcall:
198  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
199  Detour->HypercallOffset,
200  sizeof(vmcall),
201  vmcall);
202 
203  case hypercallTypeNone:
205  }
206 
208 }
209 
210 
211 static void
213  _Inout_ DETOUR *Detour
214  )
225 {
226  if (NULL == Detour->FunctionCloakHandle)
227  {
228  return;
229  }
230 
231  IntMemClkUncloakRegion(Detour->FunctionCloakHandle, MEMCLOAK_OPT_APPLY_PATCH);
232 
233  Detour->FunctionCloakHandle = NULL;
234 }
235 
236 
237 static void
239  _Inout_ DETOUR *Detour
240  )
253 {
254  if (NULL != Detour->HandlerCloakHandle)
255  {
256  IntMemClkUncloakRegion(Detour->HandlerCloakHandle, MEMCLOAK_OPT_APPLY_PATCH);
257  }
258 
259  Detour->HandlerCloakHandle = NULL;
260 
261  if (0 != Detour->HandlerAddress)
262  {
263  IntSlackFree(Detour->HandlerAddress);
264  }
265 
266  Detour->HandlerAddress = 0;
267 }
268 
269 
270 static void
272  _Inout_ DETOUR *Detour
273  )
286 {
287  IntDetRemoveBranch(Detour);
288 
289  IntDetRemoveHandler(Detour);
290 
292 }
293 
294 
295 static void
297  _Inout_ DETOUR *Detour
298  )
309 {
310  IntDetRemoveBranch(Detour);
311 
313  {
315  }
316  else if (gGuest.OSType == introGuestWindows)
317  {
319  }
320 }
321 
322 
323 INTSTATUS
325  _In_ DETOUR_TAG Tag
326  )
340 {
342  {
344  }
345 
346  if (Tag >= detTagMax)
347  {
349  }
350 
351  if (NULL == gDetours.DetoursTable[Tag])
352  {
354  }
355 
356  return IntDetDisableWinHypercall(gDetours.DetoursTable[Tag]);
357 }
358 
359 
360 INTSTATUS
362  _In_ DETOUR_TAG Tag
363  )
375 {
376  DETOUR *pDetour;
377 
378  if (Tag >= detTagMax)
379  {
381  }
382 
383  pDetour = gDetours.DetoursTable[Tag];
384  if (NULL == pDetour)
385  {
387  }
388 
389  return IntDetEnableHypercall(pDetour);
390 }
391 
392 
393 static INTSTATUS
395  _In_ QWORD FunctionAddress,
396  _In_ const LIX_FN_DETOUR *FnDetour,
397  _Out_ LIX_GUEST_DETOUR *DetourStruct,
398  _Out_ DETOUR **Detour
399  )
411 {
412  INTSTATUS status = INT_STATUS_SUCCESS;
413  DETOUR *pDetour = NULL;
414  QWORD detourStructGva = gLixGuest->MmAlloc.Detour.Data.Address + OFFSET_OF(LIX_HYPERCALL_PAGE,
415  Detours) + sizeof(LIX_GUEST_DETOUR) * FnDetour->Id;
416 
417  // This reads a LIX_GUEST_DETOUR structure from the guest memory, but the region that contains the structure,
418  // gLixGuest->MmAlloc.Detour.Data.Address, is protected against writes (and writes are blocked
419  // even if beta is enabled).
420  status = IntKernVirtMemRead(detourStructGva,
421  sizeof(LIX_GUEST_DETOUR),
422  DetourStruct,
423  NULL);
424  if (!INT_SUCCESS(status))
425  {
426  ERROR("[ERROR] IntKernVirtMemRead failed with status: 0x%08x\n", status);
427  return status;
428  }
429 
430  pDetour = HpAllocWithTag(sizeof(*pDetour), IC_TAG_DETG);
431  if (NULL == pDetour)
432  {
434  }
435 
436  pDetour->Callback = FnDetour->Callback;
437  pDetour->FunctionAddress = FunctionAddress;
439  pDetour->HandlerAddress = gLixGuest->MmAlloc.Detour.Data.Address + DetourStruct->Address;
440  pDetour->RelocatedCodeOffset = (BYTE)(DetourStruct->RelocatedCode - DetourStruct->Address);
441  pDetour->RelocatedCodeLength = 0;
442  pDetour->LixGuestDetour = detourStructGva;
443  pDetour->LixFnDetour = FnDetour;
444 
445  *Detour = pDetour;
446 
447  return INT_STATUS_SUCCESS;
448 }
449 
450 
451 static INTSTATUS
453  _In_ DETOUR *Detour,
454  _In_ BYTE *FunctionCode,
455  _In_ DWORD FunctionSize,
456  _Out_opt_ DWORD *InstrCount
457  )
475 {
476  INTSTATUS status;
477  INSTRUX instrux;
478  BYTE handler[DETOUR_MAX_HANDLER_SIZE] = {0};
479  BYTE prologueSize = 0;
480 
481  QWORD handlerAddress = Detour->HandlerAddress;
482  DWORD handlerSize = DETOUR_MAX_HANDLER_SIZE;
483 
484  if (InstrCount)
485  {
486  *InstrCount = 0;
487  }
488 
489  status = IntKernVirtMemRead(handlerAddress, handlerSize, handler, NULL);
490  if (!INT_SUCCESS(status))
491  {
492  ERROR("[ERROR] IntKernVirtMemRead failed with status: 0x%08x\n", status);
493  return status;
494  }
495 
496  while (prologueSize < 5)
497  {
498  BOOLEAN relInstr = FALSE;
499 
500  status = IntDecDecodeInstructionFromBuffer(FunctionCode + prologueSize,
501  FunctionSize - prologueSize,
503  &instrux);
504  if (!INT_SUCCESS(status))
505  {
506  return status;
507  }
508 
509  if (InstrCount)
510  {
511  *InstrCount += 1;
512  }
513 
514  relInstr = NdIsInstruxRipRelative(&instrux);
515 
516  if (relInstr)
517  {
518  if ((instrux.PrimaryOpCode != 0xe8) && // CALL
519  (instrux.PrimaryOpCode != 0xe9) && // JMP
520  (instrux.PrimaryOpCode != 0x89) && // MOV
521  (instrux.PrimaryOpCode != 0x8b) && // MOV
522  (instrux.PrimaryOpCode != 0x80) && // CMP
523  (instrux.Instruction != ND_INS_Jcc || instrux.Length != 6))
524  {
525  WARNING("[WARNING] RIP relative found at GVA 0x%016llx, can't continue!\n",
526  Detour->FunctionAddress + prologueSize);
527  IntDumpInstruction(&instrux, Detour->FunctionAddress + prologueSize);
528 
530  }
531  }
532 
533  if (instrux.PrimaryOpCode == 0xe8 || instrux.PrimaryOpCode == 0xe9)
534  {
535  QWORD calledFunc;
536  DWORD patched;
537 
538  calledFunc = Detour->FunctionAddress + prologueSize +
539  SIGN_EX(instrux.RelOffsLength, instrux.RelativeOffset);
540 
541  patched = (DWORD)(calledFunc - (handlerAddress + Detour->RelocatedCodeOffset + prologueSize));
542  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.PrimaryOpCode;
543 
544  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 1, &patched, 4);
545  }
546  else if (relInstr && (instrux.PrimaryOpCode == 0x8b || instrux.PrimaryOpCode == 0x89))
547  {
548  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
549 
550  QWORD movGva = Detour->FunctionAddress + prologueSize +
551  SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
552 
553  DWORD patched = (DWORD)(movGva - (handlerAddress + Detour->RelocatedCodeOffset + prologueSize));
554 
555  if (instrux.Rex.Rex != 0)
556  {
557  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
558  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
559  handler[Detour->RelocatedCodeOffset + prologueSize + 2] = instrux.InstructionBytes[2];
560 
561  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 3, &patched, 4);
562  }
563  else
564  {
565  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
566  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
567 
568  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
569  }
570  }
571  else if (relInstr && instrux.PrimaryOpCode == 0x80)
572  {
573  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
574 
575  QWORD cmpGva = Detour->FunctionAddress + prologueSize +
576  SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
577 
578  DWORD patched = (DWORD)(cmpGva - (handlerAddress + Detour->RelocatedCodeOffset + prologueSize));
579 
580  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
581  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
582 
583  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
584 
585  handler[Detour->RelocatedCodeOffset + prologueSize + 6] = instrux.InstructionBytes[6];
586  }
587  else if (relInstr && instrux.Instruction == ND_INS_Jcc)
588  {
589  QWORD target = Detour->FunctionAddress + instrux.Operands[0].Info.RelativeOffset.Rel;
590  DWORD newRel = (DWORD)(target - (handlerAddress + Detour->RelocatedCodeOffset));
591 
592  // Translate the old JMP into the new JMP. We make it long and we modify the relative offset so that it
593  // points to the same address. Important note: we must make sure that the target is NOT inside the
594  // relocated code, otherwise we need to do some more black magic.
595  handler[Detour->RelocatedCodeOffset + prologueSize] = 0x0F;
596  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = 0x80 | instrux.Predicate;
597 
598  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 2, &newRel, 4);
599  }
600  else
601  {
602  // Copy the current instruction inside the detour handler
603  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize, FunctionCode + prologueSize, instrux.Length);
604  }
605 
606  prologueSize += instrux.Length;
607  }
608 
609  status = IntVirtMemWrite(handlerAddress, handlerSize, gGuest.Mm.SystemCr3, handler);
610  if (!INT_SUCCESS(status))
611  {
612  ERROR("[ERROR] IntVirtMemWrite failed with status: 0x%x.", status);
613  return status;
614  }
615 
616  Detour->RelocatedCodeLength = prologueSize;
617 
618  return INT_STATUS_SUCCESS;
619 }
620 
621 
622 static DETOUR *
624  _In_ DETOUR_TAG Tag
625  )
633 {
634  if (Tag >= detTagMax)
635  {
636  return NULL;
637  }
638 
639  return gDetours.DetoursTable[Tag];
640 }
641 
642 
643 static INTSTATUS
645  _In_ void *Hook,
646  _In_ QWORD PhysicalAddress,
647  _In_ QWORD RegionVirtualAddress,
648  _In_ void *CloakHandle,
649  _Out_ INTRO_ACTION *Action
650  )
669 {
670  INTSTATUS status = INT_STATUS_SUCCESS;
671  DWORD regionOffset;
672  BOOLEAN relocateAfter;
674  BYTE *memcloakData = NULL;
675  DWORD memcloakDataSize;
676  LIX_ACTIVE_PATCH *pActivePatch = NULL;
677 
678  if (NULL == Hook)
679  {
681  }
682 
683  if (NULL == CloakHandle)
684  {
686  }
687 
688  if (NULL == Action)
689  {
691  }
692 
693  // We will handle and emulate the write instead of allowing it. Even if we fail, the guest still shouldn't
694  // be allowed to perform any writes inside our detours.
695  *Action = introGuestNotAllowed;
696 
698  {
699  ERROR("[ERROR] Detour writes are not being handled on this OS!");
701  }
702 
703  for (DWORD index = 0; index < ARRAYSIZE(gLixGuest->ActivePatch); index++)
704  {
705  status = IntLixDrvIsLegitimateTextPoke(Hook, PhysicalAddress, &gLixGuest->ActivePatch[index], &action);
706  if (INT_SUCCESS(status))
707  {
708  pActivePatch = &gLixGuest->ActivePatch[index];
709  break;
710  }
711  }
712 
713  if (!INT_SUCCESS(status))
714  {
715  WARNING("[WARNING] IntLixDrvIsLegitimateTextPoke failed for address 0x%llx. Status: 0x%08x\n",
716  PhysicalAddress, status);
717  return status;
718  }
719 
720  if (action != introGuestAllowed)
721  {
722  ERROR("[ERROR] The guest attempted to perform a write inside detour %d with a non-legitimate "
723  "text poke. Will deny!", pActivePatch->DetourTag);
724  return INT_STATUS_SUCCESS;
725  }
726 
727  if (!pActivePatch->IsDetour)
728  {
729  ERROR("[ERROR] Detour write handler called, but the ActivePatch is not inside a detour! 0x%llx (+%d)\n",
730  pActivePatch->Gva, pActivePatch->Length);
732  }
733 
734  // At this point the patch is legitimate and we should handle it.
735 
736  status = IntMemClkGetOriginalData(CloakHandle, &memcloakData, &memcloakDataSize);
737  if (!INT_SUCCESS(status))
738  {
739  ERROR("[ERROR] IntMemClkGetOriginalData failed: 0x%08x\n", status);
740  return status;
741  }
742 
743  regionOffset = (DWORD)(pActivePatch->Gva - RegionVirtualAddress);
744 
745  WARNING("[WARNING] The guest is applying a patch with size %d at 0x%llx (+ %d).\n",
746  pActivePatch->Length, RegionVirtualAddress, regionOffset);
747 
748  // Basically, we are checking if this write tries to overwrite the 0xCC, which means that
749  // the poke is completed and we should attempt to update the relocated code inside our detour handler.
750  relocateAfter = (pActivePatch->Length == 1);
751  relocateAfter = relocateAfter && (RegionVirtualAddress == pActivePatch->Gva);
752  relocateAfter = relocateAfter && (memcloakData[regionOffset] == 0xCC);
753 
754  // This is just a safety measure in case this function will be called twice for the same write.
755  // 99% this is happening on KVM or if the write is at a page boundary.
756  relocateAfter = relocateAfter && (pActivePatch->Data[0] != 0xCC);
757 
758  // This way we are tricking the guest into thinking that the write was actually performed.
759  status = IntMemClkModifyOriginalData(CloakHandle,
760  regionOffset,
761  pActivePatch->Length,
762  pActivePatch->Data);
763  if (!INT_SUCCESS(status))
764  {
765  ERROR("[ERROR] IntMemClkModifyPatchedData failed with status 0x%08x\n", status);
766  return status;
767  }
768 
769  TRACE("[INFO] Updated memcloak original buffer at 0x%llx. Size: %d\n. ",
770  pActivePatch->Gva, pActivePatch->Length);
771 
772  if (relocateAfter)
773  {
774  DETOUR *pDet = IntDetFindByTag(pActivePatch->DetourTag);
775 
776  // Here we should call this again because the memcloak buffer may have been changed since the last call
777  status = IntMemClkGetOriginalData(CloakHandle, &memcloakData, &memcloakDataSize);
778  if (!INT_SUCCESS(status))
779  {
780  ERROR("[ERROR] IntMemClkGetOriginalData failed: 0x%08x\n", status);
781  return INT_STATUS_SUCCESS;
782  }
783 
784  if (memcloakDataSize < pDet->RelocatedCodeLength)
785  {
786  // This should never ever happen.
787  BUG_ON(TRUE);
788 
789  ERROR("[ERROR] Cloaked region size for detour %d is smaller than the relocated code length (%d vs %d)!\n",
790  pDet->Tag, memcloakDataSize, pDet->RelocatedCodeLength);
791 
792  return INT_STATUS_SUCCESS;
793  }
794 
795  IntPauseVcpus();
796 
797  status = IntDetRelocate(pDet, memcloakData, pDet->RelocatedCodeLength, NULL);
798  if (!INT_SUCCESS(status))
799  {
800  // Not that big of a problem, the relocated code inside our detour handler will not be updated.
801  ERROR("[ERROR] Failed to relocate patch for detour %d. Status: 0x%08x\n\n",
802  pActivePatch->DetourTag, status);
803  }
804 
805  IntResumeVcpus();
806 
808  IntDisasmBuffer(memcloakData, pDet->RelocatedCodeLength, pDet->FunctionAddress);
809  }
810 
811  return INT_STATUS_SUCCESS;
812 }
813 
814 
815 static INTSTATUS
817  _In_ DETOUR *Detour,
818  _In_ BYTE *FunctionCode,
819  _In_ DWORD FunctionSize
820  )
837 {
838  INTSTATUS status = INT_STATUS_SUCCESS;
839  BYTE functionDetour[DETOUR_MAX_FUNCTION_SIZE] = {0};
840  QWORD handlerAddress = 0;
841 
842  UNREFERENCED_PARAMETER(FunctionSize);
843 
844  handlerAddress = Detour->HandlerAddress;
845 
846  functionDetour[0] = 0xE9;
847  *(DWORD *)(functionDetour + 1) = (DWORD)(handlerAddress - (Detour->FunctionAddress + 5));
848 
849  for (DWORD i = 5; i < Detour->RelocatedCodeLength; i++)
850  {
851  functionDetour[i] = 0x90;
852  }
853 
854  status = IntMemClkCloakRegion(Detour->FunctionAddress,
855  0,
856  Detour->RelocatedCodeLength,
858  FunctionCode,
859  functionDetour,
861  &Detour->FunctionCloakHandle);
862  if (!INT_SUCCESS(status))
863  {
864  ERROR("[ERROR] IntMemClkCloackRegion failed: 0x%08x\n", status);
865  goto _exit;
866  }
867 
868  return INT_STATUS_SUCCESS;
869 
870 _exit:
871  return status;
872 }
873 
874 
875 static INTSTATUS
877  _In_ char *FunctionName,
878  _In_ QWORD FunctionAddress,
879  _In_ QWORD DetourAddress
880  )
890 {
891  INTSTATUS status = INT_STATUS_SUCCESS;
893  KERNEL_DRIVER *pDriver = NULL;
894 
895  memzero(pEvent, sizeof(*pEvent));
896 
897  pEvent->Header.Action = introGuestAllowed;
898  pEvent->Header.Reason = introReasonAllowed;
899  pEvent->Header.MitreID = idHooking;
900 
901  pEvent->Header.Flags &= ~ALERT_FLAG_NOT_RING0;
902 
905 
906  pDriver = IntDriverFindByAddress(DetourAddress);
907  if (pDriver)
908  {
910  {
911  IntAlertFillWinKmModule(pDriver, &pEvent->Originator.Module);
912  }
913  else if (gGuest.OSType == introGuestLinux)
914  {
915  IntAlertFillLixKmModule(pDriver, &pEvent->Originator.Module);
916  }
917  }
918 
919  pEvent->Header.CpuContext.Valid = FALSE;
921 
922  utf8toutf16(pEvent->Victim.Name, FunctionName, MIN(sizeof(pEvent->Victim.Name), (DWORD)strlen(FunctionName)));
923 
924  pEvent->BaseAddress = FunctionAddress;
925  pEvent->VirtualAddress = FunctionAddress;
926  pEvent->Size = gGuest.WordSize;
927 
928  pEvent->WriteInfo.Size = gGuest.WordSize;
929  pEvent->WriteInfo.NewValue[0] = DetourAddress;
930 
931  status = IntNotifyIntroEvent(introEventIntegrityViolation, pEvent, sizeof(*pEvent));
932  if (!INT_SUCCESS(status))
933  {
934  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
935  }
936 
937  return INT_STATUS_SUCCESS;
938 }
939 
940 
941 INTSTATUS
943  _In_ QWORD FunctionAddress,
944  _In_ const LIX_FN_DETOUR *FnDetour,
945  _Out_ BOOLEAN *MultipleInstructions
946  )
970 {
971  INTSTATUS status = INT_STATUS_SUCCESS;
972  DETOUR *pDetour = NULL;
973  LIX_GUEST_DETOUR detourStruct = { 0 };
974  DWORD relInstrCount = 0;
975  BYTE functionCode[DETOUR_MAX_FUNCTION_SIZE] = {0};
976 
977  if (FunctionAddress == 0)
978  {
980  }
981 
982  if (FnDetour == NULL)
983  {
985  }
986 
987  status = IntKernVirtMemRead(FunctionAddress, sizeof(functionCode), functionCode, NULL);
988  if (!INT_SUCCESS(status))
989  {
990  ERROR("[ERROR] IntKernVirtMemRead failed with status: 0x%08x\n", status);
991  return status;
992  }
993 
994  if (functionCode[0] == 0xE9 || functionCode[0] == 0xE8)
995  {
996  QWORD addr = FunctionAddress + SIGN_EX_32(*(DWORD *)&functionCode[1]) + 5;
997 
998  IntDetSendIntegrityAlert(FnDetour->FunctionName, FunctionAddress, addr);
999 
1000  WARNING("[WARNING] API function already detoured (0x%016llx) by kprobe/live-patch!\n", addr);
1001  }
1002 
1003  status = IntDetCreateObjectLix(FunctionAddress, FnDetour, &detourStruct, &pDetour);
1004  if (!INT_SUCCESS(status))
1005  {
1006  ERROR("[ERROR] IntDetCreateObjectLix failed with status: 0x%08x.", status);
1007  return status;
1008  }
1009 
1010  status = IntDetRelocate(pDetour, functionCode, sizeof(functionCode), &relInstrCount);
1011  if (!INT_SUCCESS(status))
1012  {
1013  ERROR("[WARNING] IntDetRelocate failed with status: 0x%08x.", status);
1014  goto _exit;
1015  }
1016 
1017  *MultipleInstructions = FALSE;
1018  if (relInstrCount > 1)
1019  {
1020  *MultipleInstructions = TRUE;
1021  }
1022 
1023  status = IntDetPatchFunction(pDetour, functionCode, sizeof(functionCode));
1024  if (!INT_SUCCESS(status))
1025  {
1026  ERROR("[ERROR] IntDetPatchFunction failed with status: 0x%08x.", status);
1027  goto _exit;
1028  }
1029 
1030  detourStruct.EnableOptions = FnDetour->EnableFlags;
1031  detourStruct.JumpBack = FunctionAddress + pDetour->RelocatedCodeLength;
1032 
1033  // The written area is protected, so no need to use IntVirtMemSafeWrite.
1034  status = IntKernVirtMemWrite(pDetour->LixGuestDetour, sizeof(detourStruct), &detourStruct);
1035  if (!INT_SUCCESS(status))
1036  {
1037  ERROR("[ERROR] IntKernVirtMemWrite failed with status: 0x%08x\n", status);
1038  goto _exit;
1039  }
1040 
1041  InsertTailList(&gDetours.DetoursList, &pDetour->Link);
1042 
1043  gDetours.LixDetourTable[FnDetour->Id] = pDetour;
1044 
1045  LOG("[DETOUR] %llx: handler @ %llx (%s)\n",
1046  pDetour->FunctionAddress, pDetour->HandlerAddress, FnDetour->FunctionName);
1047 
1048  status = INT_STATUS_SUCCESS;
1049 
1050 _exit:
1051  if (!INT_SUCCESS(status) && (NULL != pDetour))
1052  {
1053  IntDetRemoveDetour(pDetour);
1054  }
1055 
1056  return status;
1057 }
1058 
1059 
1060 INTSTATUS
1062  _In_ QWORD FunctionAddress,
1063  _In_ QWORD ModuleBase,
1064  _Inout_ API_HOOK_DESCRIPTOR *Descriptor,
1065  _Inout_ API_HOOK_HANDLER *Handler
1066  )
1116 {
1117  INTSTATUS status;
1118  DETOUR *pDetour;
1119  INSTRUX instrux;
1122  DWORD prologueSize, newJumpBackOffset, totalHandlerSize, i;
1123 
1124  prologueSize = 0;
1125 
1126  if (NULL == Descriptor)
1127  {
1129  }
1130 
1131  if (Descriptor->Tag >= detTagMax)
1132  {
1134  }
1135 
1136  if (NULL == Handler)
1137  {
1139  }
1140 
1141  if (0 == ModuleBase)
1142  {
1143  ModuleBase = gGuest.KernelVa;
1144  }
1145 
1146  if (FunctionAddress)
1147  {
1148  //
1149  // Get the original function code (from the kernel buffer if we have that)
1150  //
1151  if ((gGuest.OSType == introGuestWindows) && (ModuleBase == gGuest.KernelVa))
1152  {
1153  DWORD funOffset = (DWORD)(FunctionAddress - ModuleBase);
1154 
1155  if (funOffset + sizeof(origFn) > gWinGuest->KernelBufferSize)
1156  {
1157  ERROR("[ERROR] Function not inside kernel buffer. Offset: 0x%08x, size: 0x%08zx, buffer size: 0x%08x\n",
1158  funOffset, sizeof(origFn), gWinGuest->KernelBufferSize);
1160  }
1161 
1162  memcpy(origFn, gWinGuest->KernelBuffer + funOffset, sizeof(origFn));
1163  }
1164  else
1165  {
1166  status = IntKernVirtMemRead(FunctionAddress, sizeof(origFn), origFn, NULL);
1167  if (!INT_SUCCESS(status))
1168  {
1169  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1170  return status;
1171  }
1172  }
1173 
1174  // If the function is already hooked, leave.
1175  if (origFn[0] == 0xE9)
1176  {
1177  QWORD addr = FunctionAddress + SIGN_EX_32(*(DWORD *)&origFn[1]) + 5;
1178 
1179  IntDetSendIntegrityAlert(Descriptor->FunctionName, FunctionAddress, addr);
1180 
1181  WARNING("[INFO] API function already detoured (0x%016llx), don't know by who! Aborting!\n", addr);
1182  return INT_STATUS_NOT_SUPPORTED;
1183  }
1184 
1185  // Compute the size of the prologue. We need this before actually parsing the prologue in order to know how
1186  // much slack to allocate.
1187  while (prologueSize < 5)
1188  {
1189  status = IntDecDecodeInstructionFromBuffer(origFn + prologueSize,
1190  sizeof(origFn) - prologueSize,
1192  &instrux);
1193  if (!INT_SUCCESS(status))
1194  {
1195  return status;
1196  }
1197 
1198  prologueSize += instrux.Length;
1199  }
1200  }
1201 
1202  totalHandlerSize = Handler->CodeLength + prologueSize;
1203 
1204  if (sizeof(newHnd) < totalHandlerSize)
1205  {
1206  ERROR("[ERROR] The size of the handler exceeds %zu bytes: %d!\n", sizeof(newHnd), totalHandlerSize);
1207  return INT_STATUS_NOT_SUPPORTED;
1208  }
1209 
1210  pDetour = HpAllocWithTag(sizeof(*pDetour), IC_TAG_DETG);
1211  if (NULL == pDetour)
1212  {
1214  }
1215 
1216  status = IntSlackAlloc(ModuleBase, FALSE, totalHandlerSize, &pDetour->HandlerAddress, 0);
1217  if (!INT_SUCCESS(status))
1218  {
1219  ERROR("[ERROR] IntSlackAlloc failed: 0x%08x\n", status);
1220  goto cleanup_and_exit;
1221  }
1222 
1223  if (NULL != Descriptor->PreCallback)
1224  {
1225  status = Descriptor->PreCallback(FunctionAddress, Handler, Descriptor);
1226  if (!INT_SUCCESS(status))
1227  {
1228  ERROR("[ERROR] PreCallback for hook %d (`%s!%s`) failed: 0x%08x\n",
1229  Descriptor->Tag, utf16_for_log(Descriptor->ModuleName), Descriptor->FunctionName, status);
1230  goto cleanup_and_exit;
1231  }
1232  else if (INT_STATUS_NOT_NEEDED_HINT == status)
1233  {
1234  LOG("[INFO] PreCallback for hook %d (`%s!%s`) returned NOT NEEDED. Will skip the hook (critical: %d)\n",
1235  Descriptor->Tag,
1236  utf16_for_log(Descriptor->ModuleName),
1237  Descriptor->FunctionName,
1238  !Descriptor->NotCritical);
1239 
1240  goto cleanup_and_exit;
1241  }
1242  }
1243 
1244  status = IntKernVirtMemRead(pDetour->HandlerAddress, totalHandlerSize, origHnd, NULL);
1245  if (!INT_SUCCESS(status))
1246  {
1247  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1248  goto cleanup_and_exit;
1249  }
1250 
1251  memcpy(newHnd, Handler->Code, totalHandlerSize - prologueSize);
1252 
1253  if (0 == FunctionAddress)
1254  {
1255  goto _no_function;
1256  }
1257 
1258  // Clone the modified bytes inside the handler code. We need to relocate minimum 5 bytes
1259  prologueSize = 0;
1260  while (prologueSize < 5)
1261  {
1262  BOOLEAN relInstr = FALSE;
1263 
1264  status = IntDecDecodeInstructionFromBuffer(origFn + prologueSize,
1265  sizeof(origFn) - prologueSize,
1267  &instrux);
1268  if (!INT_SUCCESS(status))
1269  {
1270  goto cleanup_and_exit;
1271  }
1272 
1273  relInstr = NdIsInstruxRipRelative(&instrux);
1274 
1275  if (relInstr)
1276  {
1277  if ((instrux.PrimaryOpCode != 0xe8) && // CALL
1278  (instrux.PrimaryOpCode != 0x89) && // MOV
1279  (instrux.PrimaryOpCode != 0x8b) && // MOV
1280  (instrux.PrimaryOpCode != 0x80) && // CMP
1281  (instrux.PrimaryOpCode != 0x63) && // MOVSXD
1282  (instrux.Instruction != ND_INS_Jcc || instrux.Length != 6))
1283  {
1284  CHAR insText[ND_MIN_BUF_SIZE] = {0};
1285 
1286  NdToText(&instrux, FunctionAddress + prologueSize, sizeof(insText), insText);
1287 
1288  WARNING("[WARNING] RIP relative instruction '%s' found at GVA 0x%016llx, can't continue!\n",
1289  insText, FunctionAddress + prologueSize);
1290  IntDumpInstruction(&instrux, FunctionAddress + prologueSize);
1291 
1292  status = INT_STATUS_NOT_SUPPORTED;
1293  goto cleanup_and_exit;
1294  }
1295  }
1296 
1297  if (instrux.PrimaryOpCode == 0xe8)
1298  {
1299  QWORD calledFunc = FunctionAddress + prologueSize + SIGN_EX(instrux.RelOffsLength, instrux.RelativeOffset);
1300 
1301  DWORD patched = (DWORD)(calledFunc -
1302  (pDetour->HandlerAddress + Handler->RelocatedCodeOffset + prologueSize));
1303 
1304  newHnd[Handler->RelocatedCodeOffset + prologueSize] = 0xe8;
1305  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 1, &patched, 4);
1306  }
1307  else if (relInstr && (instrux.PrimaryOpCode == 0x8b || instrux.PrimaryOpCode == 0x89))
1308  {
1309  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
1310 
1311  QWORD movGva = FunctionAddress + prologueSize + SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
1312  DWORD patched = (DWORD)(movGva - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset + prologueSize));
1313 
1314  if (instrux.Rex.Rex != 0)
1315  {
1316  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1317  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1318  newHnd[Handler->RelocatedCodeOffset + prologueSize + 2] = instrux.InstructionBytes[2];
1319 
1320  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 3, &patched, 4);
1321  }
1322  else
1323  {
1324  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1325  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1326 
1327  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
1328  }
1329  }
1330  else if (relInstr && instrux.PrimaryOpCode == 0x80)
1331  {
1332  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
1333 
1334  QWORD cmpGva = FunctionAddress + prologueSize + SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
1335  DWORD patched = (DWORD)(cmpGva - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset + prologueSize));
1336 
1337  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1338  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1339 
1340  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
1341 
1342  newHnd[Handler->RelocatedCodeOffset + prologueSize + 6] = instrux.InstructionBytes[6];
1343  }
1344  else if (relInstr && instrux.Instruction == ND_INS_Jcc)
1345  {
1346  QWORD target = FunctionAddress + instrux.Operands[0].Info.RelativeOffset.Rel;
1347  DWORD newRel = (DWORD)(target - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset));
1348 
1349  // Translate the old JMP into the new JMP. We make it long and we modify the relative offset so that it
1350  // points to the same address. Important note: we must make sure that the target is NOT inside the
1351  // relocated code, otherwise we need to do some more black magic.
1352  newHnd[Handler->RelocatedCodeOffset + prologueSize] = 0x0F;
1353  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = 0x80 | instrux.Predicate;
1354 
1355  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &newRel, 4);
1356  }
1357  else if (relInstr && instrux.PrimaryOpCode == 0x63)
1358  {
1359  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
1360  QWORD crtInstruxGva = FunctionAddress + prologueSize + SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
1361  DWORD crtInstruxPatch = (DWORD)(crtInstruxGva - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset +
1362  prologueSize));
1363 
1364  if (instrux.Rex.Rex != 0)
1365  {
1366  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1367  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1368  newHnd[Handler->RelocatedCodeOffset + prologueSize + 2] = instrux.InstructionBytes[2];
1369 
1370 
1371  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 3, &crtInstruxPatch, 4);
1372  }
1373  else
1374  {
1375  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1376  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1377 
1378  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &crtInstruxPatch, 4);
1379  }
1380  }
1381  else
1382  {
1383  // Copy the current instruction inside the detour handler
1384  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize, origFn + prologueSize, instrux.Length);
1385  }
1386 
1387  prologueSize += instrux.Length;
1388  }
1389 
1390  // Patch the jump from the handler code. Note that the jump address inside the handler code is
1391  // variable. The fact that the MAX_PROLOGUE_SIZE is defined to 8 bytes is just a template. The
1392  // actual layout of the handler code once injected inside the guest is variable. The JMP back
1393  // will be located immediately after the prologue, be it 5 or 20 bytes in length.
1394  newJumpBackOffset = Handler->RelocatedCodeOffset + prologueSize;
1395 
1396  // Set the JMP opcode.
1397  *(newHnd + newJumpBackOffset) = 0xE9;
1398 
1399  *(DWORD *)(newHnd + newJumpBackOffset + 1) = (DWORD)((FunctionAddress + 5) - (pDetour->HandlerAddress +
1400  newJumpBackOffset + 5));
1401 
1402  // Now re-patch the jump from handler code to jump after the whole prologue (and don't execute the NOP)
1403  *(DWORD *)(newHnd + newJumpBackOffset + 1) += prologueSize - 5;
1404 
1405  // Set a jmp handler
1406  newFn[0] = 0xE9;
1407  *(DWORD *)(newFn + 1) = (DWORD)(pDetour->HandlerAddress - (FunctionAddress + 5));
1408 
1409  for (i = 5; i < prologueSize; i++)
1410  {
1411  newFn[i] = 0x90;
1412  }
1413 
1414 _no_function:
1415 
1416  pDetour->Tag = Descriptor->Tag;
1417  // This can be NULL! We can have dummy handlers/handlers that don't issue hypercalls.
1418  pDetour->Callback = Descriptor->Callback;
1419  pDetour->HandlerSize = totalHandlerSize;
1420  pDetour->FunctionAddress = FunctionAddress;
1421  // The hypercall can be missing from the handler, in which case the HypercallAddress will be 0.
1422  pDetour->HypercallAddress = (Handler->HypercallOffset == DETOUR_INVALID_HYPERCALL) ? 0 : pDetour->HandlerAddress +
1423  Handler->HypercallOffset;
1424  pDetour->HypercallOffset = Handler->HypercallOffset;
1425  pDetour->ModuleBase = ModuleBase;
1426  pDetour->HypercallType = Handler->HypercallType;
1427  pDetour->JumpBackOffset = Handler->RelocatedCodeOffset + (BYTE)prologueSize;
1428  pDetour->RelocatedCodeOffset = Handler->RelocatedCodeOffset;
1429  pDetour->RelocatedCodeLength = (BYTE)prologueSize;
1430 
1431  memcpy(pDetour->PublicDataOffsets, Handler->PublicDataOffsets,
1432  sizeof(API_HOOK_PUBLIC_DATA) * Handler->NrPublicDataOffsets);
1433  pDetour->NrPublicDataOffsets = Handler->NrPublicDataOffsets;
1434 
1435  pDetour->Descriptor = Descriptor;
1436 
1437  status = IntMemClkCloakRegion(pDetour->HandlerAddress,
1438  0,
1439  pDetour->HandlerSize,
1441  origHnd,
1442  newHnd,
1443  NULL,
1444  &pDetour->HandlerCloakHandle);
1445  if (!INT_SUCCESS(status))
1446  {
1447  ERROR("[ERROR] IntMemClkCloackRegion failed: 0x%08x\n", status);
1448  goto cleanup_and_exit;
1449  }
1450 
1451  if (FunctionAddress)
1452  {
1453  status = IntMemClkCloakRegion(pDetour->FunctionAddress,
1454  0,
1455  pDetour->RelocatedCodeLength,
1457  origFn,
1458  newFn,
1459  NULL,
1460  &pDetour->FunctionCloakHandle);
1461  if (!INT_SUCCESS(status))
1462  {
1463  ERROR("[ERROR] IntMemClkCloackRegion failed: 0x%08x\n", status);
1464  goto cleanup_and_exit;
1465  }
1466  }
1467 
1468  // Add thew new detour to the list.
1469  InsertTailList(&gDetours.DetoursList, &pDetour->Link);
1470 
1471  gDetours.DetoursTable[pDetour->Tag] = pDetour;
1472 
1473  TRACE("[DETOUR] Hooked function @ 0x%016llx, handler @ 0x%016llx, hypercall @ 0x%016llx\n",
1474  pDetour->FunctionAddress, pDetour->HandlerAddress, pDetour->HypercallAddress);
1475 
1476  // This just checks if the current detour overlaps with the SSDT somehow...
1478  {
1479  QWORD start = gWinGuest->Ssdt & PAGE_MASK;
1480  // Multiply the number of services by 4 because on 32-bit kernels the SSDT contains pointers, while on
1481  // 64-bit kernels it contains offsets inside the kernel image.
1482  QWORD end = start + gWinGuest->NumberOfServices * 4ull;
1483 
1484  end = ALIGN_UP(end, PAGE_SIZE);
1485 
1486  TRACE("[INFO] Will ensure that SSDT isn't in our detours!\n");
1487 
1488  if (IN_RANGE(pDetour->FunctionAddress, start, end) ||
1489  IN_RANGE(pDetour->FunctionAddress + pDetour->RelocatedCodeLength, start, end))
1490  {
1491  WARNING("[WARNING] SSDT 0x%016llx (%d services) is in the same page (function) as detour %d \n",
1493  }
1494 
1495  if (IN_RANGE(pDetour->HandlerAddress, start, end) ||
1496  IN_RANGE(pDetour->HandlerAddress + pDetour->HandlerSize, start, end))
1497  {
1498  WARNING("[WARNING] SSDT 0x%016llx (%d services) is in the same page (handler) as detour %d \n",
1500  }
1501  }
1502 
1503 cleanup_and_exit:
1504  if (!INT_SUCCESS(status) && (NULL != pDetour))
1505  {
1506  IntDetRemoveDetour(pDetour);
1507  }
1508  else
1509  {
1510  if (NULL != Descriptor->PostCallback)
1511  {
1512  status = Descriptor->PostCallback(Handler);
1513  if (!INT_SUCCESS(status))
1514  {
1515  WARNING("[ERROR] PostCallback for hook %d (`%s!%s`) failed: 0x%08x\n",
1516  Descriptor->Tag, utf16_for_log(Descriptor->ModuleName), Descriptor->FunctionName, status);
1517  }
1518 
1519  status = INT_STATUS_SUCCESS;
1520  }
1521  }
1522 
1523  return status;
1524 }
1525 
1526 
1527 INTSTATUS
1529  _In_ DETOUR const *Detour,
1530  _Inout_opt_ IG_ARCH_REGS *Registers,
1531  _In_ QWORD ReturnValue
1532  )
1551 {
1552  INTSTATUS status;
1553  IG_ARCH_REGS regs;
1554 
1555  if (NULL == Registers)
1556  {
1557  status = IntGetGprs(gVcpu->Index, &regs);
1558  if (!INT_SUCCESS(status))
1559  {
1560  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1561  return status;
1562  }
1563 
1564  Registers = &regs;
1565  }
1566 
1567  switch (Detour->HypercallType)
1568  {
1569  case hypercallTypeInt3:
1570  Registers->Rax = ReturnValue;
1571  break;
1572 
1573  case hypercallTypeVmcall:
1574  Registers->Rsi = ReturnValue;
1575  break;
1576 
1577  default:
1578  {
1579  ERROR("[DETOUR] Invalid hypercall type %d ...", Detour->HypercallType);
1580  return INT_STATUS_NOT_SUPPORTED;
1581  }
1582  }
1583 
1584  status = IntSetGprs(gVcpu->Index, Registers);
1585  if (!INT_SUCCESS(status))
1586  {
1587  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1588  return status;
1589  }
1590 
1591  return INT_STATUS_SUCCESS;
1592 }
1593 
1594 
1595 INTSTATUS
1597  void
1598  )
1622 {
1624  DETOUR *pDetour = NULL;
1625  BOOLEAN bCalled = FALSE;
1626 
1627  if (gGuest.OSType == introGuestLinux &&
1628  IN_RANGE_LEN(gVcpu->Regs.Rip, gLixGuest->MmAlloc.Detour.Code.Address, gLixGuest->MmAlloc.Detour.Code.Length))
1629  {
1630  DETOUR *pDet = NULL;
1631  if (gVcpu->Regs.Rbx >= det_max_id)
1632  {
1633  return INT_STATUS_NOT_FOUND;
1634  }
1635 
1636  pDet = gDetours.LixDetourTable[gVcpu->Regs.Rbx];
1637 
1638  if (!pDet->Disabled)
1639  {
1640  pDet->HitCount++;
1641  status = gLixHookHandlersx64[gVcpu->Regs.Rbx].Callback(pDet);
1642  }
1643  else
1644  {
1645  status = INT_STATUS_SUCCESS;
1646  }
1647 
1648  pDetour = pDet;
1649  }
1650  else
1651  {
1652  for_each_detour(pDet)
1653  {
1654  // We match a detour using the RIP that generated the VMEXIT. Also, ignore detours with missing handlers.
1655  if (pDet->HypercallAddress != gVcpu->Regs.Rip ||
1656  pDet->HypercallAddress == 0 ||
1657  pDet->HypercallOffset == DETOUR_INVALID_HYPERCALL ||
1658  pDet->Callback == NULL)
1659  {
1660  continue;
1661  }
1662 
1663  if (!pDet->Disabled)
1664  {
1665  // Note: detours can be safely added from other detour handlers.
1666  // However, we can only remove the current detour, as removing other detours will possibly
1667  // corrupt the detours list.
1668  status = pDet->Callback(pDet);
1669 
1670  bCalled = TRUE;
1671 
1672  pDet->HitCount++;
1673  }
1674  else
1675  {
1676  status = INT_STATUS_SUCCESS;
1677  }
1678 
1679  pDetour = pDet;
1680 
1681  break;
1682  }
1683  }
1684 
1685  if (INT_STATUS_DISABLE_DETOUR_ON_RET == status)
1686  {
1687  LOG("[DETOUR] Removing detour with tag %d\n", pDetour->Tag);
1689  }
1690  else if (INT_STATUS_REMOVE_DETOUR_AND_SET_RIP == status)
1691  {
1692  if (pDetour->HypercallType == hypercallTypeVmcall)
1693  {
1695  {
1696  WARNING("[WARNING] The detour uses vmcall as a hypercall. Cannot remove and set RIP ...");
1697  return INT_STATUS_SUCCESS;
1698  }
1699  else if (gGuest.OSType == introGuestLinux)
1700  {
1701  QWORD addrRegs = gLixGuest->MmAlloc.PerCpuData.PerCpuAddress + (gVcpu->Index * PAGE_SIZE);
1702 
1703  LOG("[DETOUR] Removing detour with tag %d and setting RIP[%d] to 0x%016llx\n",
1704  pDetour->Tag, gVcpu->Index, pDetour->FunctionAddress);
1705 
1707 
1708  status = IntKernVirtMemRead(addrRegs, 16 * 8, &gVcpu->Regs, NULL);
1709  if (!INT_SUCCESS(status))
1710  {
1711  ERROR("[ERROR] IntKernVirtMemRead failed for GVA 0x%016llx with status: 0x%08x\n",
1712  addrRegs, status);
1713  return status;
1714  }
1715 
1716  // The intergator adds the length of VMCALL instruction (3 bytes) to RIP
1717  gVcpu->Regs.Rip = pDetour->FunctionAddress - 3;
1718 
1720 
1721  return INT_STATUS_NO_DETOUR_EMU;
1722  }
1723  }
1724 
1725  if (pDetour->HypercallType == hypercallTypeInt3)
1726  {
1727  LOG("[DETOUR] Removing detour with tag %d and setting RIP[%d] to 0x%016llx\n",
1728  pDetour->Tag, gVcpu->Index, pDetour->FunctionAddress);
1729 
1731 
1732  gVcpu->Regs.Rip = pDetour->FunctionAddress;
1733 
1735 
1736  return INT_STATUS_NO_DETOUR_EMU;
1737  }
1738  }
1739  else if (bCalled)
1740  {
1741  // Force success if any callback was called, in case that some unknown statuses are returned, such as
1742  // INT_STATUS_NOT_FOUND, which can generate guest crashes by re-injecting the int3.
1743  return INT_STATUS_SUCCESS;
1744  }
1745 
1746  return status;
1747 }
1748 
1749 
1750 INTSTATUS
1752  _In_ DETOUR_TAG Tag,
1753  _In_ void const *Data,
1754  _In_ DWORD DataSize,
1755  _In_ char const *PublicDataName
1756  )
1779 {
1780  INTSTATUS status;
1781  DETOUR *pDet;
1782  API_HOOK_PUBLIC_DATA *pPublicData = NULL;
1783 
1784  if (Tag >= detTagMax)
1785  {
1787  }
1788 
1789  if (NULL == Data)
1790  {
1792  }
1793 
1794  if (0 == DataSize)
1795  {
1797  }
1798 
1799  if (NULL == PublicDataName)
1800  {
1802  }
1803 
1804  pDet = gDetours.DetoursTable[Tag];
1805  if (NULL == pDet)
1806  {
1808  }
1809 
1810  if (NULL == pDet->HandlerCloakHandle)
1811  {
1813  }
1814 
1815  for (DWORD i = 0; i < pDet->NrPublicDataOffsets; i++)
1816  {
1817  if (!strcmp(PublicDataName, pDet->PublicDataOffsets[i].PublicDataName))
1818  {
1819  pPublicData = &pDet->PublicDataOffsets[i];
1820  break;
1821  }
1822  }
1823 
1824  if (NULL == pPublicData)
1825  {
1826  return INT_STATUS_NOT_FOUND;
1827  }
1828 
1829  if (DataSize > pPublicData->PublicDataSize)
1830  {
1832  }
1833 
1834  LOG("[DETOUR] Will modify patched data for public data %s at offset %d with size %u\n",
1835  pPublicData->PublicDataName, pPublicData->PublicDataOffset, DataSize);
1836 
1837  status = IntMemClkModifyPatchedData(pDet->HandlerCloakHandle, pPublicData->PublicDataOffset, DataSize, Data);
1838  if (!INT_SUCCESS(status))
1839  {
1840  ERROR("[ERROR] IntMemClkModifyPatchedData failed: 0x%08x\n", status);
1841  }
1842 
1843  return status;
1844 }
1845 
1846 
1847 void
1849  void
1850  )
1859 {
1860  for_each_detour(pDet)
1861  {
1863  }
1864 }
1865 
1866 
1867 void
1869  void
1870  )
1879 {
1880  for_each_detour(pDet)
1881  {
1882  RemoveEntryList(&pDet->Link);
1883 
1884  gDetours.DetoursTable[pDet->Tag] = NULL;
1885 
1886  IntDetRemoveDetour(pDet);
1887  }
1888 
1889  memzero(&gDetours, sizeof(gDetours));
1890  InitializeListHead(&gDetours.DetoursList);
1891 }
1892 
1893 
1894 void
1896  void
1897  )
1901 {
1902  LOG("[DBGINTRO] Introspection detours:\n");
1903 
1904  for_each_detour(pDetour)
1905  {
1906  char *pFnName = NULL;
1907  if (gGuest.OSType == introGuestLinux)
1908  {
1909  if (pDetour->LixFnDetour != NULL)
1910  {
1911  pFnName = pDetour->LixFnDetour->FunctionName;
1912  }
1913  else if (pDetour->Descriptor != NULL)
1914  {
1915  pFnName = pDetour->Descriptor->FunctionName;
1916  }
1917  }
1918  else if (gGuest.OSType == introGuestWindows)
1919  {
1920  pFnName = pDetour->Descriptor != NULL ? pDetour->Descriptor->FunctionName : NULL;
1921  }
1922 
1923  LOG(" ## %-32s Hits: %12llu, RIP: %llx, Tag: %02d, Handler RIP: %llx (+ %02x), Hooked RIP: %llx\n",
1924  pFnName != NULL ? pFnName : "unknown",
1925  pDetour->HitCount,
1926  pDetour->HypercallAddress,
1927  pDetour->Tag,
1928  pDetour->HandlerAddress,
1929  pDetour->HandlerSize,
1930  pDetour->FunctionAddress);
1931 
1932  if (gGuest.OSType == introGuestWindows && pDetour->ModuleBase != gGuest.KernelVa)
1933  {
1934  LOG(" Module: %llx\n",
1935  pDetour->ModuleBase);
1936  }
1937  }
1938 }
1939 
1940 
1941 BOOLEAN
1943  _In_ QWORD Ptr,
1944  _Out_opt_ DETOUR_TAG *Tag
1945  )
1955 {
1956  for_each_detour(pDet)
1957  {
1958  if (Ptr >= pDet->FunctionAddress &&
1959  Ptr < pDet->FunctionAddress + pDet->RelocatedCodeLength)
1960  {
1961  if (NULL != Tag)
1962  {
1963  *Tag = pDet->Tag;
1964  }
1965 
1966  return TRUE;
1967  }
1968  }
1969 
1970  if (NULL != Tag)
1971  {
1972  *Tag = detTagMax;
1973  }
1974 
1975  return FALSE;
1976 }
1977 
1978 
1979 BOOLEAN
1981  _In_ QWORD Ptr,
1982  _In_ THS_PTR_TYPE Type,
1983  _Out_opt_ DETOUR_TAG *Tag
1984  )
1994 {
1995 
1996  if (gGuest.OSType == introGuestLinux)
1997  {
1998  if (Tag)
1999  {
2000  *Tag = 0;
2001  }
2002 
2003  if (IN_RANGE_LEN(Ptr, gLixGuest->MmAlloc.Detour.Code.Address, gLixGuest->MmAlloc.Detour.Code.Length))
2004  {
2005  return TRUE;
2006  }
2007  }
2008 
2009  for_each_detour(pDet)
2010  {
2011  if ((Ptr >= pDet->HandlerAddress) && (Ptr < pDet->HandlerAddress + pDet->HandlerSize))
2012  {
2013  WARNING("[WARNING] Found %s ptr 0x%016llx in detours: handler RIP 0x%016llx, hook RIP 0x%016llx\n",
2014  Type == ptrLiveRip ? "live RIP" : "stack value", Ptr, pDet->HandlerAddress, pDet->FunctionAddress);
2015 
2016  // This is ugly, but we want to avoid modifying the RtlpVirtualUnwind handler while a RIP
2017  // is pointing inside of it, since we can't atomically modify 2 instructions if the first
2018  // one has already executed...
2019  if (pDet->Tag >= detTagRtlVirtualUnwind1 && pDet->Tag < detTagRtlVirtualUnwindMax)
2020  {
2022 
2023  if (Ptr < pDet->HandlerAddress + pDet->RelocatedCodeOffset)
2024  {
2025  gRipInsideRtlpVirtualUnwindReloc = TRUE;
2026  }
2027  }
2028 
2029  if (NULL != Tag)
2030  {
2031  *Tag = pDet->Tag;
2032  }
2033 
2034  return TRUE;
2035  }
2036  }
2037 
2038  if (NULL != Tag)
2039  {
2040  *Tag = detTagMax;
2041  }
2042 
2043  return FALSE;
2044 }
2045 
2046 
2047 QWORD
2049  _In_ QWORD Ptr
2050  )
2058 {
2059  for_each_detour(pDetour)
2060  {
2061  if ((Ptr > pDetour->FunctionAddress) &&
2062  (Ptr < pDetour->FunctionAddress + pDetour->RelocatedCodeLength))
2063  {
2064  return pDetour->HandlerAddress + pDetour->RelocatedCodeOffset + (Ptr - pDetour->FunctionAddress);
2065  }
2066  }
2067 
2068  // No modification, return it as it is.
2069  return Ptr;
2070 }
2071 
2072 
2073 INTSTATUS
2075  _In_ QWORD Ptr,
2076  _Out_ QWORD *Address,
2077  _Out_ DWORD *Size,
2078  _Out_ DETOUR_TAG *Tag
2079  )
2091 {
2092  for_each_detour(pDetour)
2093  {
2094  if ((Ptr > pDetour->HandlerAddress) &&
2095  (Ptr < pDetour->HandlerAddress + pDetour->HandlerSize))
2096  {
2097  *Address = pDetour->HandlerAddress;
2098  *Size = pDetour->HandlerSize;
2099  *Tag = pDetour->Tag;
2100 
2101  return INT_STATUS_SUCCESS;
2102  }
2103  }
2104 
2105  return INT_STATUS_NOT_FOUND;
2106 }
2107 
2108 
2109 INTSTATUS
2111  _In_ DETOUR_TAG Tag,
2112  _Out_ QWORD *Address,
2113  _Out_opt_ DWORD *Size
2114  )
2125 {
2126  DETOUR *pDet = IntDetFindByTag(Tag);
2127  if (pDet)
2128  {
2129  *Address = pDet->HandlerAddress;
2130 
2131  if (NULL != Size)
2132  {
2133  *Size = pDet->HandlerSize;
2134  }
2135 
2136  return INT_STATUS_SUCCESS;
2137  }
2138 
2139  return INT_STATUS_NOT_FOUND;
2140 }
2141 
2142 
2143 INTSTATUS
2145  _In_ DETOUR_TAG Tag,
2146  _Out_ QWORD *FunctionAddress
2147  )
2157 {
2158  DETOUR *pDet = IntDetFindByTag(Tag);
2159  if (pDet)
2160  {
2161  *FunctionAddress = pDet->FunctionAddress;
2162  return INT_STATUS_SUCCESS;
2163  }
2164 
2165  return INT_STATUS_NOT_FOUND;
2166 }
2167 
2168 
2169 static INTSTATUS
2171  _In_ DWORD Arg,
2172  _In_opt_ BYTE const *StackBuffer,
2173  _In_ DWORD StackBufferSize,
2174  _Out_ QWORD *Value
2175  )
2189 {
2190  IG_ARCH_REGS const *pRegs = &gVcpu->Regs;
2191  BOOLEAN haveStackBuffer = StackBuffer && StackBufferSize;
2192 
2193  *Value = 0;
2194 
2195  if (DET_ARG_ON_STACK(Arg)) // Read argument from the stack
2196  {
2197  DWORD stackOffset = DET_ARG_STACK_OFFSET(Arg);
2198 
2199  if (haveStackBuffer && stackOffset + gGuest.WordSize <= StackBufferSize)
2200  {
2201  if (gGuest.WordSize == 4)
2202  {
2203  *Value = *(DWORD const *)(StackBuffer + stackOffset);
2204  }
2205  else
2206  {
2207  *Value = *(QWORD const *)(StackBuffer + stackOffset);
2208  }
2209  }
2210  else
2211  {
2212  INTSTATUS status;
2213 
2214  status = IntKernVirtMemRead(pRegs->Rsp + stackOffset, gGuest.WordSize, Value, NULL);
2215  if (!INT_SUCCESS(status))
2216  {
2217  ERROR("[ERROR] IntKernVirtMemRead failed for %llx: 0x%08x\n", pRegs->Rsp + stackOffset, status);
2218  return status;
2219  }
2220  }
2221  }
2222  else if (DET_ARG_REGS(Arg)) // Simply get from registers
2223  {
2224  *Value = ((QWORD const *)pRegs)[Arg];
2225  }
2226  else
2227  {
2228  ERROR("[ERROR] Invalid argument type: 0x%08x\n", Arg);
2230  }
2231 
2232  return INT_STATUS_SUCCESS;
2233 }
2234 
2235 
2236 INTSTATUS
2238  _In_ void const *Detour,
2239  _In_ DWORD Index,
2240  _In_opt_ BYTE const *StackBuffer,
2241  _In_ DWORD StackBufferSize,
2242  _Out_ QWORD *Value
2243  )
2263 {
2264  API_HOOK_DESCRIPTOR const *pApi;
2265  DETOUR const *pDetour;
2266  DWORD const *pArgs;
2267 
2268  if (NULL == Detour)
2269  {
2271  }
2272 
2273  if (Index >= DET_ARGS_MAX)
2274  {
2276  }
2277 
2278  if (NULL == Value)
2279  {
2281  }
2282 
2283  pDetour = Detour;
2284  pApi = pDetour->Descriptor;
2285  if (NULL == pApi)
2286  {
2288  }
2289 
2290  pArgs = pApi->Arguments.Argv;
2291  return IntDetGetArgumentInternal(pArgs[Index], StackBuffer, StackBufferSize, Value);
2292 }
2293 
2294 
2295 INTSTATUS
2297  _In_ void const *Detour,
2298  _In_ DWORD Argc,
2299  _Out_writes_(Argc) QWORD *Argv
2300  )
2321 {
2322  BYTE stackBuffer[256];
2323  DETOUR const *detour;
2324  API_HOOK_DESCRIPTOR const *api;
2325  DWORD const *args;
2326  DWORD stackBufferSize = 0;
2327  BYTE const *pStackBuffer = NULL;
2328  DWORD argc;
2329 
2330  if (NULL == Detour)
2331  {
2333  }
2334 
2335  if (0 == Argc || DET_ARGS_MAX <= Argc)
2336  {
2338  }
2339 
2340  if (NULL == Argv)
2341  {
2343  }
2344 
2345  detour = Detour;
2346  api = detour->Descriptor;
2347  if (NULL == api)
2348  {
2350  }
2351 
2352  args = api->Arguments.Argv;
2353  argc = Argc;
2354  if (argc > api->Arguments.Argc)
2355  {
2356  ERROR("[ERROR] Requested to read %u arguments for for detour %d, but only %u exist.\n",
2357  argc, detour->Tag, api->Arguments.Argc);
2359  }
2360 
2361  for (DWORD i = 0; i < argc; i++)
2362  {
2363  if (DET_ARG_ON_STACK(args[i]))
2364  {
2365  stackBufferSize = MAX(stackBufferSize, DET_ARG_STACK_OFFSET(args[i]));
2366  }
2367  }
2368 
2369  if (stackBufferSize)
2370  {
2371  INTSTATUS status;
2372  QWORD rsp = gVcpu->Regs.Rsp;
2373 
2374  // Add the size of the last parameter
2375  stackBufferSize += gGuest.WordSize;
2376  // Just in case we have something so far up the stack
2377  stackBufferSize = MIN(stackBufferSize, sizeof(stackBuffer));
2378 
2379  status = IntKernVirtMemRead(rsp, stackBufferSize, &stackBuffer, NULL);
2380  if (!INT_SUCCESS(status))
2381  {
2382  WARNING("[WARNING] IntKernVirtMemRead failed for [0x%016llx, 0x%016llx]: 0x%08x\n",
2383  rsp, rsp + stackBufferSize, status);
2384  stackBufferSize = 0;
2385  // We could still try to read them one at a time
2386  }
2387  else
2388  {
2389  pStackBuffer = stackBuffer;
2390  }
2391  }
2392 
2393  for (DWORD i = 0; i < argc; i++)
2394  {
2395  // pStackBuffer will be NULL if we get here and we could not read the stack
2396  // or there are no arguments on the stack; IntDetGetArgumentInternal will handle that
2397  INTSTATUS status = IntDetGetArgumentInternal(args[i], pStackBuffer, stackBufferSize, &Argv[i]);
2398  if (!INT_SUCCESS(status))
2399  {
2400  ERROR("[ERROR] IntDetGetArgument failed for %u: 0x%08x\n", i, status);
2401  return status;
2402  }
2403  }
2404 
2405  return INT_STATUS_SUCCESS;
2406 }
2407 
2408 
2409 INTSTATUS
2411  _In_ void const *Detour,
2412  _In_ DWORD Index,
2413  _In_ QWORD Value
2414  )
2428 {
2429  INTSTATUS status;
2430  API_HOOK_DESCRIPTOR const *pApi;
2431  DETOUR const *pDetour;
2432  PIG_ARCH_REGS pRegs;
2433  QWORD arg;
2434 
2435  if (NULL == Detour)
2436  {
2438  }
2439 
2440  pDetour = Detour;
2441  pApi = pDetour->Descriptor;
2442  if (NULL == pApi)
2443  {
2445  }
2446 
2447  if (Index >= pApi->Arguments.Argc)
2448  {
2450  }
2451 
2452  pRegs = &gVcpu->Regs;
2453  arg = pApi->Arguments.Argv[Index];
2454 
2455  if (DET_ARG_ON_STACK(arg))
2456  {
2458  pRegs->Rsp + DET_ARG_STACK_OFFSET(arg),
2459  gGuest.WordSize,
2460  &Value,
2461  IG_CS_RING_0);
2462  if (!INT_SUCCESS(status))
2463  {
2464  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
2465  return status;
2466  }
2467  }
2468  else if (DET_ARG_REGS(arg))
2469  {
2470  ((QWORD *)pRegs)[arg] = Value;
2471 
2472  status = IntSetGprs(gVcpu->Index, pRegs);
2473  if (!INT_SUCCESS(status))
2474  {
2475  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
2476  return status;
2477  }
2478  }
2479  else
2480  {
2481  ERROR("[ERROR] Invalid argument type: 0x%016llx\n", arg);
2483  }
2484 
2485  return INT_STATUS_SUCCESS;
2486 }
static DETOUR * IntDetFindByTag(DETOUR_TAG Tag)
Searches a detour by its tag.
Definition: detours.c:623
INTSTATUS IntDetSetLixHook(QWORD FunctionAddress, const LIX_FN_DETOUR *FnDetour, BOOLEAN *MultipleInstructions)
Detours a function from guest.
Definition: detours.c:942
QWORD LixGuestDetour
The address of the linux-detour header.
Definition: detours.h:463
#define _In_opt_
Definition: intro_sal.h:16
API_HOOK_PUBLIC_DATA PublicDataOffsets[PUBLIC_DATA_MAX_DESCRIPTORS]
Public data that can be used to modify the detour handler.
Definition: detours.h:482
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
QWORD HandlerAddress
The guest virtual address of the detour handler.
Definition: detours.h:454
static INTSTATUS IntDetEnableHypercall(DETOUR *Detour)
Enables a detour hypercall.
Definition: detours.c:155
Describes the information about a Linux active-patch.
Definition: lixguest.h:461
static void IntDetRemoveBranch(DETOUR *Detour)
Restores the original instructions of a hooked function.
Definition: detours.c:212
#define ALIGN_UP(x, a)
Definition: introdefs.h:164
PDETOUR DetoursTable[detTagMax]
Table of detours, indexed by DETOUR_TAG.
Definition: detours.c:34
struct _LINUX_GUEST::@128 MmAlloc
static INTSTATUS IntDetSendIntegrityAlert(char *FunctionName, QWORD FunctionAddress, QWORD DetourAddress)
Sends an integrity alert if the provieded function is already hooked.
Definition: detours.c:876
DWORD Size
The size of the access.
Definition: intro_types.h:982
BYTE NrPublicDataOffsets
The number of valid entries inside the PublicDataOffsets array.
Definition: detours.h:480
DWORD Argc
The number of valid entries inside the Argv array.
Definition: detours.h:110
uint8_t BYTE
Definition: intro_types.h:47
struct _LINUX_GUEST::@128::@133 PerCpuData
#define OFFSET_OF(Type, Member)
Definition: introlists.h:33
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:37
INTSTATUS IntKernVirtMemWrite(QWORD KernelGva, DWORD Length, void *Buffer)
Writes data to a guest kernel virtual memory range.
Definition: introcore.c:699
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
DWORD Index
The VCPU number.
Definition: guests.h:172
LIX_ACTIVE_PATCH ActivePatch[lixActivePatchCount]
An array that contains information about the active-patches.
Definition: lixguest.h:527
#define _In_
Definition: intro_sal.h:21
struct _LIX_GUEST_DETOUR LIX_GUEST_DETOUR
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
#define INT_STATUS_REMOVE_DETOUR_AND_SET_RIP
Definition: introstatus.h:426
QWORD NewValue[8]
The written value. Only the first Size bytes are valid.
Definition: intro_types.h:981
Holds information about the currently set detours.
Definition: detours.c:29
INTSTATUS IntGetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Get the current guest GPR state.
Definition: introcpu.c:827
INTSTATUS IntMemClkModifyPatchedData(void *CloakHandle, DWORD Offset, DWORD Size, const void *Data)
Modifies the patched data inside the guest memory.
Definition: memcloak.c:795
Event structure for integrity violations on monitored structures.
Definition: intro_types.h:1572
Described a detour handler.
Definition: detours.h:283
QWORD HitCount
The number of times this detour issued a hypercall.
Definition: detours.h:498
Describes a detour set inside the guest memory.
Definition: detours.h:436
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define BUG_ON(cond)
Definition: introdefs.h:236
QWORD HypercallAddress
The guest virtual address at which the hypercall is placed.
Definition: detours.h:449
BOOLEAN IsDetour
Definition: lixguest.h:467
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
INTSTATUS IntDetGetArgument(void const *Detour, DWORD Index, BYTE const *StackBuffer, DWORD StackBufferSize, QWORD *Value)
Reads the specified argument for a detour.
Definition: detours.c:2237
The action was not allowed because there was no reason to allow it.
Definition: intro_types.h:183
#define ARRAYSIZE(A)
Definition: introdefs.h:101
INTSTATUS IntVirtMemWrite(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer)
Writes data to a guest virtual memory range.
Definition: introcore.c:652
struct _EVENT_INTEGRITY_VIOLATION::@304 Victim
WORD Length
The patch length.
Definition: lixguest.h:464
struct _DETOURS_STATE * PDETOURS_STATE
static DETOURS_STATE gDetours
The global detour state.
Definition: detours.c:40
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
#define DET_ARG_STACK_OFFSET(Arg)
Gets the stack offset at which a stack argument is found.
Definition: detours.h:66
INTSTATUS IntDetModifyPublicData(DETOUR_TAG Tag, void const *Data, DWORD DataSize, char const *PublicDataName)
Modifies public parts of a detour handler.
Definition: detours.c:1751
#define SIGN_EX(sz, x)
Definition: introdefs.h:206
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
The function is already hooked.
Definition: intro_types.h:269
int INTSTATUS
The status data type.
Definition: introstatus.h:24
INTSTATUS IntDetEnableDetour(DETOUR_TAG Tag)
Enables a detour based on its tag.
Definition: detours.c:361
#define INT_STATUS_DISABLE_DETOUR_ON_RET
Can be used by detour callbacks to signal that the detour should be disabled.
Definition: introstatus.h:357
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
DWORD NumberOfServices
The number of entries in the SSDT.
Definition: winguest.h:819
unsigned long long EnableOptions
Definition: handlers.h:106
INTSTATUS IntDetGetFunctionAddressByTag(DETOUR_TAG Tag, QWORD *FunctionAddress)
Get a detour function address by its tag.
Definition: detours.c:2144
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
static INTSTATUS IntDetDisableLixHypercall(DETOUR *Detour)
Disables a Linux detour hypercall.
Definition: detours.c:58
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
#define INT_STATUS_BUFFER_OVERFLOW
Definition: introstatus.h:200
#define _Out_writes_(expr)
Definition: intro_sal.h:28
void IntDetDisableAllHooks(void)
Removes all detours from the guest.
Definition: detours.c:1848
#define MIN(a, b)
Definition: introdefs.h:146
Will write the contents of the patched data inside the guest.
Definition: memcloak.h:54
DETOUR_TAG DetourTag
If the guest attempts to patch the jump label for our detour, contains the tag of the detour...
Definition: lixguest.h:469
The detour will use a INT3 instruction in order to notify introcore about an event.
Definition: detours.h:189
INTSTATUS IntSlackAlloc(QWORD ModuleBase, BOOLEAN Pageable, DWORD Size, QWORD *Buffer, QWORD SecHint)
Allocate slack inside the guest.
Definition: slack.c:437
PFUNC_DetourCallback Callback
Callback to be invoked when the detour issues a hypercall. May be NULL.
Definition: detours.h:441
INTSTATUS IntDetGetByTag(DETOUR_TAG Tag, QWORD *Address, DWORD *Size)
Get a detour handler address and size by its tag.
Definition: detours.c:2110
void * FunctionCloakHandle
The memory cloak handle used to hide the modified function start. See Memory cloaking.
Definition: detours.h:494
#define LOG(fmt,...)
Definition: glue.h:61
32-bit selector.
Definition: glueiface.h:187
No hypercall. This detour does not generate events.
Definition: detours.h:187
Describes a kernel driver.
Definition: drivers.h:30
static __default_fn_attr size_t vmcall(DETOUR_ID id)
Definition: handlers.c:121
void IntAlertFillVersionInfo(INTRO_VIOLATION_HEADER *Header)
Fills version information for an alert.
Definition: alerts.c:327
CHAR PublicDataName[PUBLIC_DATA_MAX_NAME_SIZE]
Name used to identify the data.
Definition: detours.h:267
The guest detour API.
static INTSTATUS IntDetDisableWinHypercall(DETOUR *Detour)
Disables a Windows detour hypercall.
Definition: detours.c:98
#define IC_TAG_DETG
Guest detour state.
Definition: memtags.h:16
INTSTATUS IntDetCallCallback(void)
Calls the appropriate detour handler for hypercall.
Definition: detours.c:1596
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
static INTSTATUS IntDetGetArgumentInternal(DWORD Arg, BYTE const *StackBuffer, DWORD StackBufferSize, QWORD *Value)
Reads the value of an argument passed from a detour.
Definition: detours.c:2170
INTSTATUS IntDetSetReturnValue(DETOUR const *Detour, IG_ARCH_REGS *Registers, QWORD ReturnValue)
Sets the return value for a hooked guest function.
Definition: detours.c:1528
struct _LINUX_GUEST::@128::@131 Detour
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
#define _Inout_opt_
Definition: intro_sal.h:31
BYTE HypercallOffset
Offset, relative to HandlerAddress, where the hypercall instruction is found.
Definition: detours.h:472
INTSTATUS IntDetPatchArgument(void const *Detour, DWORD Index, QWORD Value)
Modifies the value of a detour argument.
Definition: detours.c:2410
#define _Inout_
Definition: intro_sal.h:20
void IntAlertFillLixKmModule(const KERNEL_DRIVER *Driver, INTRO_MODULE *EventModule)
Saves information about a kernel module inside an alert.
Definition: alerts.c:1235
TIMER_FRIENDLY void IntDumpInstruction(INSTRUX *Instruction, QWORD Rip)
This function dumps a given instruction (textual disassembly).
Definition: dumper.c:583
INTSTATUS IntDetDisableDetour(DETOUR_TAG Tag)
Disables a detour based on its tag.
Definition: detours.c:324
DETOUR_ARGS Arguments
Encoding of the arguments needed by introcore from the hooked function.
Definition: detours.h:379
#define _Out_opt_
Definition: intro_sal.h:30
DWORD Size
The size of the modified memory area.
Definition: intro_types.h:1618
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1196
DWORD Argv[DET_ARGS_MAX]
Argument encoding. See DET_ARG_REGS and DET_ARG_ON_STACK.
Definition: detours.h:111
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
Public data which allows for external modification to a in-guest hook handler.
Definition: detours.h:264
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
#define IN_RANGE(x, start, end)
Definition: introdefs.h:167
#define memzero(a, s)
Definition: introcrt.h:35
INTSTATUS IntMemClkGetOriginalData(void *CloakHandle, BYTE **OriginalData, DWORD *Length)
Returns the original data of a cloaked region.
Definition: memcloak.c:1121
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
#define for_each_detour(_var_name)
Iterates the linked list in gDetours.
Definition: detours.c:54
unsigned long long QWORD
Definition: intro_types.h:53
PAPI_HOOK_DESCRIPTOR Descriptor
The hook descriptor for which this hook was set.
Definition: detours.h:501
static INTSTATUS IntDetCreateObjectLix(QWORD FunctionAddress, const LIX_FN_DETOUR *FnDetour, LIX_GUEST_DETOUR *DetourStruct, DETOUR **Detour)
Create a DETOUR structure using the information from LIX_FN_DETOUR.
Definition: detours.c:394
HYPERCALL_TYPE HypercallType
The type of the hypercall that this detour uses.
Definition: detours.h:466
INTRO_MODULE Module
The module that modified the monitored region.
Definition: intro_types.h:1578
#define IN_RANGE_LEN(x, start, len)
Definition: introdefs.h:175
Must always be the last one.
Definition: detours.h:178
struct _DETOURS_STATE DETOURS_STATE
Holds information about the currently set detours.
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
QWORD Ssdt
Guest virtual address of the SSDT structure inside the kernel.
Definition: winguest.h:818
INTSTATUS IntDecDecodeInstructionFromBuffer(PBYTE Buffer, size_t BufferSize, IG_CS_TYPE CsType, void *Instrux)
Decode an instruction from the provided buffer.
Definition: decoder.c:308
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1574
INTSTATUS IntMemClkModifyOriginalData(void *CloakHandle, DWORD Offset, DWORD Size, void *Data)
Modifies the internal copy of the original data buffer held by a cloak region.
Definition: memcloak.c:734
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define TRACE(fmt,...)
Definition: glue.h:58
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
#define DETOUR_MAX_HANDLER_SIZE
The maximum size of a in-guest detour handler.
Definition: detours.h:194
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
const LIX_FN_DETOUR gLixHookHandlersx64[]
An array that contains the descriptors about the function that will be hooked (see lixapi...
Definition: lixapi.c:69
void IntDetUninit(void)
Uninitializes the detour module.
Definition: detours.c:1868
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:367
The RIP of a thread.
BOOLEAN gRipInsideRtlpVirtualUnwindReloc
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
BOOLEAN IntDetIsPtrInRelocatedCode(QWORD Ptr, DETOUR_TAG *Tag)
Checks if a guest pointer is inside the modified prologue of a function.
Definition: detours.c:1942
QWORD FunctionAddress
The guest virtual address of the hooked function.
Definition: detours.h:451
#define WARNING(fmt,...)
Definition: glue.h:60
#define ALERT_FLAG_NOT_RING0
If set, the alert was triggered in ring 1, 2 or 3.
Definition: intro_types.h:674
Hooking.
Definition: intro_types.h:1154
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
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
static void IntDetRemoveHandler(DETOUR *Detour)
Removes a detour handler from the guest.
Definition: detours.c:238
QWORD IntDetRelocatePtrIfNeeded(QWORD Ptr)
Returns the new value Ptr should have if it is currently pointing inside a relocated prologue...
Definition: detours.c:2048
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
INTSTATUS IntSlackFree(QWORD Buffer)
Free slack space.
Definition: slack.c:499
DWORD HandlerSize
The size of the detour handler.
Definition: detours.h:460
WCHAR Name[ALERT_PATH_MAX_LEN]
NULL-terminated string with a human readable description of the modified object.
Definition: intro_types.h:1591
#define DETOUR_MAX_FUNCTION_SIZE
The maximum size of the original function code we will replace.
Definition: detours.c:24
uint32_t DWORD
Definition: intro_types.h:49
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:852
PDETOUR LixDetourTable[det_max_id]
Table of detours, indexed by DETOUR_TAG (linux).
Definition: detours.c:35
LIST_ENTRY Link
The link inside the DETOURS_STATE.DetoursList list.
Definition: detours.h:439
BOOLEAN Valid
Set to True if the information in the structure is valid, False otherwise.
Definition: intro_types.h:965
#define INT_STATUS_INVALID_PARAMETER_6
Definition: introstatus.h:77
BYTE JumpBackOffset
Offset, relative to HandlerAddress, where the jump that returns control to the hooked function is fou...
Definition: detours.h:470
enum _INTRO_ACTION INTRO_ACTION
Event actions.
INTSTATUS IntDetGetAddrAndTag(QWORD Ptr, QWORD *Address, DWORD *Size, DETOUR_TAG *Tag)
Checks if Ptr is inside a detour handler and returns the detour&#39;s handler address, size and tag.
Definition: detours.c:2074
INTRO_WRITE_INFO WriteInfo
Definition: intro_types.h:1607
INTSTATUS IntSetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Sets the values of the guest GPRs.
Definition: introcpu.c:905
#define SIGN_EX_32(x)
Definition: introdefs.h:202
BYTE Data[32]
The replacement data which follows to be written.
Definition: lixguest.h:465
#define MAX(a, b)
Definition: introdefs.h:151
INTSTATUS IntLixDrvIsLegitimateTextPoke(void *Hook, QWORD Address, LIX_ACTIVE_PATCH *ActivePatch, INTRO_ACTION *Action)
This function checks if the modified zone by the current instruction is a &#39;text_poke&#39;.
Definition: lixmodule.c:980
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
THS_PTR_TYPE
The type of pointer to be checked.
LIST_HEAD DetoursList
List of detours. Each entry is a DETOUR structure.
Definition: detours.c:31
QWORD ModuleBase
The guest virtual address of the base of the kernel module that owns the hooked function.
Definition: detours.h:491
BYTE PublicDataOffset
The offset at which the data is available inside the detour handler.
Definition: detours.h:269
#define DETOUR_INVALID_HYPERCALL
Used to specify that no hypercall is present in the detour handler so the HypercallOffset field insid...
Definition: detours.h:321
EVENT_INTEGRITY_VIOLATION Integrity
Definition: alerts.h:23
BYTE RelocatedCodeLength
The size of the relocated code.
Definition: detours.h:477
void * HandlerCloakHandle
The memory cloak handle used to hide the detour handler. See Memory cloaking.
Definition: detours.h:496
struct _EVENT_INTEGRITY_VIOLATION::@302 Originator
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1194
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
KERNEL_DRIVER * IntDriverFindByAddress(QWORD Gva)
Returns the driver in which Gva resides.
Definition: drivers.c:164
static void IntDetRemoveDetour(DETOUR *Detour)
Removes and frees a detour.
Definition: detours.c:271
const LIX_FN_DETOUR * LixFnDetour
Definition: detours.h:502
QWORD BaseAddress
The guest virtual address at which the monitored integrity region starts.
Definition: intro_types.h:1614
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
INTSTATUS IntKernVirtMemPatchQword(QWORD GuestVirtualAddress, QWORD Data)
Writes 8 bytes in the guest kernel memory.
Definition: introcore.c:932
static void IntDetPermanentlyDisableDetour(DETOUR *Detour)
Removes a detour from the guest.
Definition: detours.c:296
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:851
char * utf16_for_log(const WCHAR *WString)
Converts a UTF-16 to a UTF-8 string to be used inside logging macros.
Definition: introcore.c:2845
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
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
PFUNC_LixDetourCallback Callback
Callback to be invoked when the detour issues a hypercall.
Definition: detours.h:422
Sent for integrity violation alerts. See EVENT_INTEGRITY_VIOLATION.
Definition: intro_types.h:92
64-bit selector.
Definition: glueiface.h:188
static INTSTATUS IntDetHandleWrite(void *Hook, QWORD PhysicalAddress, QWORD RegionVirtualAddress, void *CloakHandle, INTRO_ACTION *Action)
Handle the writes over bytes modified from detoured function.
Definition: detours.c:644
BYTE PublicDataSize
The size of the data.
Definition: detours.h:271
Holds register state.
Definition: glueiface.h:30
void IntDisasmGva(QWORD Gva, DWORD Length)
This function disassembles a code buffer (given its GVA) and then dumps the instructions (textual dis...
Definition: dumper.c:432
INTSTATUS IntDetGetArguments(void const *Detour, DWORD Argc, QWORD *Argv)
Reads multiple arguments from a detour.
Definition: detours.c:2296
DETOUR_TAG
Unique tag used to identify a detour.
Definition: detours.h:119
QWORD Gva
The start of the region which follows to be patched.
Definition: lixguest.h:463
static INTSTATUS IntDetPatchFunction(DETOUR *Detour, BYTE *FunctionCode, DWORD FunctionSize)
Patch the first instruction of the function with a &#39;JMP&#39; to our handler.
Definition: detours.c:816
#define DET_ARG_ON_STACK(Arg)
Checks if the argument should be taken from the guest stack.
Definition: detours.h:57
void IntAlertFillWinKmModule(const KERNEL_DRIVER *Driver, INTRO_MODULE *EventModule)
Saves kernel module information inside an alert.
Definition: alerts.c:617
BOOLEAN Disabled
True if this detour has been disabled.
Definition: detours.h:489
char CHAR
Definition: intro_types.h:56
void IntDetDumpDetours(void)
Prints all the detours in the gDetours list of detours.
Definition: detours.c:1895
unsigned long long JumpBack
Definition: handlers.h:105
#define PAGE_MASK
Definition: pgtable.h:35
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
INTSTATUS IntDetSetHook(QWORD FunctionAddress, QWORD ModuleBase, API_HOOK_DESCRIPTOR *Descriptor, API_HOOK_HANDLER *Handler)
Will inject code inside the guest.
Definition: detours.c:1061
WCHAR * utf8toutf16(WCHAR *Destination, const char *Source, DWORD DestinationMaxLength)
Definition: introcrt.c:507
BYTE RelocatedCodeOffset
Offset, relative to HandlerAddress, where the prologue that has been replaced by our jump at the begi...
Definition: detours.h:475
#define DET_ARGS_MAX
The maximum number of arguments passed from the guest to introcore.
Definition: detours.h:69
Describes a function to be hooked.
Definition: detours.h:329
void IntDisasmBuffer(void *Buffer, DWORD Length, QWORD Rip)
This function disassembles a given code buffer and then dumps the instructions (textual disassembly)...
Definition: dumper.c:294
The detour will use a VMCALL instruction in order to notify introcore about an event.
Definition: detours.h:191
LINUX_GUEST * gLixGuest
Global variable holding the state of a Linux guest.
Definition: lixguest.c:30
#define INT_STATUS_INVALID_DATA_SIZE
Definition: introstatus.h:142
BOOLEAN IntDetIsPtrInHandler(QWORD Ptr, THS_PTR_TYPE Type, DETOUR_TAG *Tag)
Checks if a guest pointer is inside a detour handler.
Definition: detours.c:1980
DETOUR_TAG Tag
Detour tag.
Definition: detours.h:444
#define FALSE
Definition: intro_types.h:34
static INTSTATUS IntDetRelocate(DETOUR *Detour, BYTE *FunctionCode, DWORD FunctionSize, DWORD *InstrCount)
Relocate the over-written instruction from detoured function.
Definition: detours.c:452
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
#define DET_ARG_REGS(Arg)
Checks if the argument should be taken from the guest general purpose registers.
Definition: detours.h:39
Describes a Linux-function to be hooked.
Definition: detours.h:416
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68