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 
20 
22 #define DETOUR_MAX_FUNCTION_SIZE 24
23 
27 typedef struct _DETOURS_STATE
28 {
30  union
31  {
34  };
36 
39 {
40 
42 };
43 
52 #define for_each_detour(_var_name) list_for_each (gDetours.DetoursList, DETOUR, _var_name)
53 
54 
55 static INTSTATUS
57  _Inout_ DETOUR *Detour
58  )
63 //
71 {
73 
74  if (!Detour->LixFnDetour)
75  {
77  }
78 
79  TRACE("[DETOUR] Disabling detour - Function name : %s, Hijack function name: %s\n", Detour->LixFnDetour->FunctionName,
80  Detour->LixFnDetour->HijackFunctionName != NULL ? Detour->LixFnDetour->HijackFunctionName : "none");
81 
82  Detour->Disabled = TRUE;
83 
84  status = IntKernVirtMemPatchQword(Detour->LixGuestDetour + OFFSET_OF(LIX_GUEST_DETOUR, EnableOptions), 0);
85  if (!INT_SUCCESS(status))
86  {
87  ERROR("[ERROR] IntKernVirtMemWrite failed for GVA 0x%016llx with status: 0x%08x\n", Detour->LixGuestDetour, status);
88  return status;
89  }
90 
91  return INT_STATUS_SUCCESS;
92 }
93 
94 
95 static INTSTATUS
97  _Inout_ DETOUR *Detour
98  )
114 {
115  BYTE nop = 0x90;
116  BYTE atomic_nop_3[3] = {0x66, 0x66, 0x90};
117 
118  if (NULL == Detour->HandlerCloakHandle)
119  {
121  }
122 
123  Detour->Disabled = TRUE;
124 
125  if (Detour->HypercallOffset == DETOUR_INVALID_HYPERCALL)
126  {
128  }
129 
130  switch (Detour->HypercallType)
131  {
132  case hypercallTypeInt3:
133  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
134  Detour->HypercallOffset,
135  sizeof(nop),
136  &nop);
137 
138  case hypercallTypeVmcall:
139  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
140  Detour->HypercallOffset,
141  sizeof(atomic_nop_3),
142  atomic_nop_3);
143 
144  case hypercallTypeNone:
146  }
147 
149 }
150 
151 
152 static INTSTATUS
154  _Inout_ DETOUR *Detour
155  )
171 {
172  BYTE int3 = 0xCC;
173  BYTE vmcall[3] = {0x0F, 0x01, 0xC1};
174 
175  if (NULL == Detour->HandlerCloakHandle)
176  {
178  }
179 
180  Detour->Disabled = FALSE;
181 
182  if (Detour->HypercallOffset == DETOUR_INVALID_HYPERCALL)
183  {
185  }
186 
187  switch (Detour->HypercallType)
188  {
189  case hypercallTypeInt3:
190  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
191  Detour->HypercallOffset,
192  sizeof(int3),
193  &int3);
194 
195  case hypercallTypeVmcall:
196  return IntMemClkModifyPatchedData(Detour->HandlerCloakHandle,
197  Detour->HypercallOffset,
198  sizeof(vmcall),
199  vmcall);
200 
201  case hypercallTypeNone:
203  }
204 
206 }
207 
208 
209 static void
211  _Inout_ DETOUR *Detour
212  )
223 {
224  if (NULL == Detour->FunctionCloakHandle)
225  {
226  return;
227  }
228 
229  IntMemClkUncloakRegion(Detour->FunctionCloakHandle, MEMCLOAK_OPT_APPLY_PATCH);
230 
231  Detour->FunctionCloakHandle = NULL;
232 }
233 
234 
235 static void
237  _Inout_ DETOUR *Detour
238  )
251 {
252  if (NULL != Detour->HandlerCloakHandle)
253  {
254  IntMemClkUncloakRegion(Detour->HandlerCloakHandle, MEMCLOAK_OPT_APPLY_PATCH);
255  }
256 
257  Detour->HandlerCloakHandle = NULL;
258 
259  if (0 != Detour->HandlerAddress)
260  {
261  IntSlackFree(Detour->HandlerAddress);
262  }
263 
264  Detour->HandlerAddress = 0;
265 }
266 
267 
268 static void
270  _Inout_ DETOUR *Detour
271  )
284 {
285  IntDetRemoveBranch(Detour);
286 
287  IntDetRemoveHandler(Detour);
288 
290 }
291 
292 
293 static void
295  _Inout_ DETOUR *Detour
296  )
307 {
308  IntDetRemoveBranch(Detour);
309 
311  {
313  }
314  else if (gGuest.OSType == introGuestWindows)
315  {
317  }
318 }
319 
320 
321 INTSTATUS
323  _In_ DETOUR_TAG Tag
324  )
338 {
340  {
342  }
343 
344  if (Tag >= detTagMax)
345  {
347  }
348 
349  if (NULL == gDetours.DetoursTable[Tag])
350  {
352  }
353 
354  return IntDetDisableWinHypercall(gDetours.DetoursTable[Tag]);
355 }
356 
357 
358 INTSTATUS
360  _In_ DETOUR_TAG Tag
361  )
373 {
374  DETOUR *pDetour;
375 
376  if (Tag >= detTagMax)
377  {
379  }
380 
381  pDetour = gDetours.DetoursTable[Tag];
382  if (NULL == pDetour)
383  {
385  }
386 
387  return IntDetEnableHypercall(pDetour);
388 }
389 
390 
391 static INTSTATUS
393  _In_ QWORD FunctionAddress,
394  _In_ const LIX_FN_DETOUR *FnDetour,
395  _Out_ LIX_GUEST_DETOUR *DetourStruct,
396  _Out_ DETOUR **Detour
397  )
409 {
410  INTSTATUS status = INT_STATUS_SUCCESS;
411  DETOUR *pDetour = NULL;
412  QWORD detourStructGva = gLixGuest->MmAlloc.Detour.Data.Address + OFFSET_OF(LIX_HYPERCALL_PAGE,
413  Detours) + sizeof(LIX_GUEST_DETOUR) * FnDetour->Id;
414 
415  // This reads a LIX_GUEST_DETOUR structure from the guest memory, but the region that contains the structure,
416  // gLixGuest->MmAlloc.Detour.Data.Address, is protected against writes (and writes are blocked
417  // even if beta is enabled).
418  status = IntKernVirtMemRead(detourStructGva,
419  sizeof(LIX_GUEST_DETOUR),
420  DetourStruct,
421  NULL);
422  if (!INT_SUCCESS(status))
423  {
424  ERROR("[ERROR] IntKernVirtMemRead failed with status: 0x%08x\n", status);
425  return status;
426  }
427 
428  pDetour = HpAllocWithTag(sizeof(*pDetour), IC_TAG_DETG);
429  if (NULL == pDetour)
430  {
432  }
433 
434  pDetour->Callback = FnDetour->Callback;
435  pDetour->FunctionAddress = FunctionAddress;
437  pDetour->HandlerAddress = gLixGuest->MmAlloc.Detour.Data.Address + DetourStruct->Address;
438  pDetour->RelocatedCodeOffset = (BYTE)(DetourStruct->RelocatedCode - DetourStruct->Address);
439  pDetour->RelocatedCodeLength = 0;
440  pDetour->LixGuestDetour = detourStructGva;
441  pDetour->LixFnDetour = FnDetour;
442 
443  *Detour = pDetour;
444 
445  return INT_STATUS_SUCCESS;
446 }
447 
448 
449 static INTSTATUS
451  _In_ DETOUR *Detour,
452  _In_ BYTE *FunctionCode,
453  _In_ DWORD FunctionSize,
454  _Out_opt_ DWORD *InstrCount
455  )
473 {
474  INTSTATUS status;
475  INSTRUX instrux;
476  BYTE handler[DETOUR_MAX_HANDLER_SIZE] = {0};
477  BYTE prologueSize = 0;
478 
479  QWORD handlerAddress = Detour->HandlerAddress;
480  DWORD handlerSize = DETOUR_MAX_HANDLER_SIZE;
481 
482  if (InstrCount)
483  {
484  *InstrCount = 0;
485  }
486 
487  status = IntKernVirtMemRead(handlerAddress, handlerSize, handler, NULL);
488  if (!INT_SUCCESS(status))
489  {
490  ERROR("[ERROR] IntKernVirtMemRead failed with status: 0x%08x\n", status);
491  return status;
492  }
493 
494  while (prologueSize < 5)
495  {
496  BOOLEAN relInstr = FALSE;
497 
498  status = IntDecDecodeInstructionFromBuffer(FunctionCode + prologueSize,
499  FunctionSize - prologueSize,
501  &instrux);
502  if (!INT_SUCCESS(status))
503  {
504  return status;
505  }
506 
507  if (InstrCount)
508  {
509  *InstrCount += 1;
510  }
511 
512  relInstr = NdIsInstruxRipRelative(&instrux);
513 
514  if (relInstr)
515  {
516  if ((instrux.PrimaryOpCode != 0xe8) && // CALL
517  (instrux.PrimaryOpCode != 0xe9) && // JMP
518  (instrux.PrimaryOpCode != 0x89) && // MOV
519  (instrux.PrimaryOpCode != 0x8b) && // MOV
520  (instrux.PrimaryOpCode != 0x80) && // CMP
521  (instrux.Instruction != ND_INS_Jcc || instrux.Length != 6))
522  {
523  WARNING("[WARNING] RIP relative found at GVA 0x%016llx, can't continue!\n",
524  Detour->FunctionAddress + prologueSize);
525  IntDumpInstruction(&instrux, Detour->FunctionAddress + prologueSize);
526 
528  }
529  }
530 
531  if (instrux.PrimaryOpCode == 0xe8 || instrux.PrimaryOpCode == 0xe9)
532  {
533  QWORD calledFunc;
534  DWORD patched;
535 
536  calledFunc = Detour->FunctionAddress + prologueSize +
537  SIGN_EX(instrux.RelOffsLength, instrux.RelativeOffset);
538 
539  patched = (DWORD)(calledFunc - (handlerAddress + Detour->RelocatedCodeOffset + prologueSize));
540  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.PrimaryOpCode;
541 
542  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 1, &patched, 4);
543  }
544  else if (relInstr && (instrux.PrimaryOpCode == 0x8b || instrux.PrimaryOpCode == 0x89))
545  {
546  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
547 
548  QWORD movGva = Detour->FunctionAddress + prologueSize +
549  SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
550 
551  DWORD patched = (DWORD)(movGva - (handlerAddress + Detour->RelocatedCodeOffset + prologueSize));
552 
553  if (instrux.Rex.Rex != 0)
554  {
555  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
556  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
557  handler[Detour->RelocatedCodeOffset + prologueSize + 2] = instrux.InstructionBytes[2];
558 
559  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 3, &patched, 4);
560  }
561  else
562  {
563  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
564  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
565 
566  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
567  }
568  }
569  else if (relInstr && instrux.PrimaryOpCode == 0x80)
570  {
571  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
572 
573  QWORD cmpGva = Detour->FunctionAddress + prologueSize +
574  SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
575 
576  DWORD patched = (DWORD)(cmpGva - (handlerAddress + Detour->RelocatedCodeOffset + prologueSize));
577 
578  handler[Detour->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
579  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
580 
581  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
582 
583  handler[Detour->RelocatedCodeOffset + prologueSize + 6] = instrux.InstructionBytes[6];
584  }
585  else if (relInstr && instrux.Instruction == ND_INS_Jcc)
586  {
587  QWORD target = Detour->FunctionAddress + instrux.Operands[0].Info.RelativeOffset.Rel;
588  DWORD newRel = (DWORD)(target - (handlerAddress + Detour->RelocatedCodeOffset));
589 
590  // Translate the old JMP into the new JMP. We make it long and we modify the relative offset so that it
591  // points to the same address. Important note: we must make sure that the target is NOT inside the
592  // relocated code, otherwise we need to do some more black magic.
593  handler[Detour->RelocatedCodeOffset + prologueSize] = 0x0F;
594  handler[Detour->RelocatedCodeOffset + prologueSize + 1] = 0x80 | instrux.Predicate;
595 
596  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize + 2, &newRel, 4);
597  }
598  else
599  {
600  // Copy the current instruction inside the detour handler
601  memcpy(handler + Detour->RelocatedCodeOffset + prologueSize, FunctionCode + prologueSize, instrux.Length);
602  }
603 
604  prologueSize += instrux.Length;
605  }
606 
607  status = IntVirtMemWrite(handlerAddress, handlerSize, gGuest.Mm.SystemCr3, handler);
608  if (!INT_SUCCESS(status))
609  {
610  ERROR("[ERROR] IntVirtMemWrite failed with status: 0x%x.", status);
611  return status;
612  }
613 
614  Detour->RelocatedCodeLength = prologueSize;
615 
616  return INT_STATUS_SUCCESS;
617 }
618 
619 
620 static DETOUR *
622  _In_ DETOUR_TAG Tag
623  )
631 {
632  if (Tag >= detTagMax)
633  {
634  return NULL;
635  }
636 
637  return gDetours.DetoursTable[Tag];
638 }
639 
640 
641 static INTSTATUS
643  _In_ void *Hook,
644  _In_ QWORD PhysicalAddress,
645  _In_ QWORD RegionVirtualAddress,
646  _In_ void *CloakHandle,
647  _Out_ INTRO_ACTION *Action
648  )
667 {
668  INTSTATUS status = INT_STATUS_SUCCESS;
669  DWORD regionOffset;
670  BOOLEAN relocateAfter;
672  BYTE *memcloakData = NULL;
673  DWORD memcloakDataSize;
674  LIX_ACTIVE_PATCH *pActivePatch = NULL;
675 
676  if (NULL == Hook)
677  {
679  }
680 
681  if (NULL == CloakHandle)
682  {
684  }
685 
686  if (NULL == Action)
687  {
689  }
690 
691  // We will handle and emulate the write instead of allowing it. Even if we fail, the guest still shouldn't
692  // be allowed to perform any writes inside our detours.
693  *Action = introGuestNotAllowed;
694 
696  {
697  ERROR("[ERROR] Detour writes are not being handled on this OS!");
699  }
700 
701  for (DWORD index = 0; index < ARRAYSIZE(gLixGuest->ActivePatch); index++)
702  {
703  status = IntLixDrvIsLegitimateTextPoke(Hook, PhysicalAddress, &gLixGuest->ActivePatch[index], &action);
704  if (INT_SUCCESS(status))
705  {
706  pActivePatch = &gLixGuest->ActivePatch[index];
707  break;
708  }
709  }
710 
711  if (!INT_SUCCESS(status))
712  {
713  WARNING("[WARNING] IntLixDrvIsLegitimateTextPoke failed for address 0x%llx. Status: 0x%08x\n",
714  PhysicalAddress, status);
715  return status;
716  }
717 
718  if (action != introGuestAllowed)
719  {
720  ERROR("[ERROR] The guest attempted to perform a write inside detour %d with a non-legitimate "
721  "text poke. Will deny!", pActivePatch->DetourTag);
722  return INT_STATUS_SUCCESS;
723  }
724 
725  if (!pActivePatch->IsDetour)
726  {
727  ERROR("[ERROR] Detour write handler called, but the ActivePatch is not inside a detour! 0x%llx (+%d)\n",
728  pActivePatch->Gva, pActivePatch->Length);
730  }
731 
732  // At this point the patch is legitimate and we should handle it.
733 
734  status = IntMemClkGetOriginalData(CloakHandle, &memcloakData, &memcloakDataSize);
735  if (!INT_SUCCESS(status))
736  {
737  ERROR("[ERROR] IntMemClkGetOriginalData failed: 0x%08x\n", status);
738  return status;
739  }
740 
741  regionOffset = (DWORD)(pActivePatch->Gva - RegionVirtualAddress);
742 
743  WARNING("[WARNING] The guest is applying a patch with size %d at 0x%llx (+ %d).\n",
744  pActivePatch->Length, RegionVirtualAddress, regionOffset);
745 
746  // Basically, we are checking if this write tries to overwrite the 0xCC, which means that
747  // the poke is completed and we should attempt to update the relocated code inside our detour handler.
748  relocateAfter = (pActivePatch->Length == 1);
749  relocateAfter = relocateAfter && (RegionVirtualAddress == pActivePatch->Gva);
750  relocateAfter = relocateAfter && (memcloakData[regionOffset] == 0xCC);
751 
752  // This is just a safety measure in case this function will be called twice for the same write.
753  // 99% this is happening on KVM or if the write is at a page boundary.
754  relocateAfter = relocateAfter && (pActivePatch->Data[0] != 0xCC);
755 
756  // This way we are tricking the guest into thinking that the write was actually performed.
757  status = IntMemClkModifyOriginalData(CloakHandle,
758  regionOffset,
759  pActivePatch->Length,
760  pActivePatch->Data);
761  if (!INT_SUCCESS(status))
762  {
763  ERROR("[ERROR] IntMemClkModifyPatchedData failed with status 0x%08x\n", status);
764  return status;
765  }
766 
767  TRACE("[INFO] Updated memcloak original buffer at 0x%llx. Size: %d\n. ",
768  pActivePatch->Gva, pActivePatch->Length);
769 
770  if (relocateAfter)
771  {
772  DETOUR *pDet = IntDetFindByTag(pActivePatch->DetourTag);
773 
774  // Here we should call this again because the memcloak buffer may have been changed since the last call
775  status = IntMemClkGetOriginalData(CloakHandle, &memcloakData, &memcloakDataSize);
776  if (!INT_SUCCESS(status))
777  {
778  ERROR("[ERROR] IntMemClkGetOriginalData failed: 0x%08x\n", status);
779  return INT_STATUS_SUCCESS;
780  }
781 
782  if (memcloakDataSize < pDet->RelocatedCodeLength)
783  {
784  // This should never ever happen.
785  BUG_ON(TRUE);
786 
787  ERROR("[ERROR] Cloaked region size for detour %d is smaller than the relocated code length (%d vs %d)!\n",
788  pDet->Tag, memcloakDataSize, pDet->RelocatedCodeLength);
789 
790  return INT_STATUS_SUCCESS;
791  }
792 
793  IntPauseVcpus();
794 
795  status = IntDetRelocate(pDet, memcloakData, pDet->RelocatedCodeLength, NULL);
796  if (!INT_SUCCESS(status))
797  {
798  // Not that big of a problem, the relocated code inside our detour handler will not be updated.
799  ERROR("[ERROR] Failed to relocate patch for detour %d. Status: 0x%08x\n\n",
800  pActivePatch->DetourTag, status);
801  }
802 
803  IntResumeVcpus();
804 
806  IntDisasmBuffer(memcloakData, pDet->RelocatedCodeLength, pDet->FunctionAddress);
807  }
808 
809  return INT_STATUS_SUCCESS;
810 }
811 
812 
813 static INTSTATUS
815  _In_ DETOUR *Detour,
816  _In_ BYTE *FunctionCode,
817  _In_ DWORD FunctionSize
818  )
835 {
836  INTSTATUS status = INT_STATUS_SUCCESS;
837  BYTE functionDetour[DETOUR_MAX_FUNCTION_SIZE] = {0};
838  QWORD handlerAddress = 0;
839 
840  UNREFERENCED_PARAMETER(FunctionSize);
841 
842  handlerAddress = Detour->HandlerAddress;
843 
844  functionDetour[0] = 0xE9;
845  *(DWORD *)(functionDetour + 1) = (DWORD)(handlerAddress - (Detour->FunctionAddress + 5));
846 
847  for (DWORD i = 5; i < Detour->RelocatedCodeLength; i++)
848  {
849  functionDetour[i] = 0x90;
850  }
851 
852  status = IntMemClkCloakRegion(Detour->FunctionAddress,
853  0,
854  Detour->RelocatedCodeLength,
856  FunctionCode,
857  functionDetour,
859  &Detour->FunctionCloakHandle);
860  if (!INT_SUCCESS(status))
861  {
862  ERROR("[ERROR] IntMemClkCloackRegion failed: 0x%08x\n", status);
863  goto _exit;
864  }
865 
866  return INT_STATUS_SUCCESS;
867 
868 _exit:
869  return status;
870 }
871 
872 
873 INTSTATUS
875  _In_ QWORD FunctionAddress,
876  _In_ const LIX_FN_DETOUR *FnDetour,
877  _Out_ BOOLEAN *MultipleInstructions
878  )
902 {
903  INTSTATUS status = INT_STATUS_SUCCESS;
904  DETOUR *pDetour = NULL;
905  LIX_GUEST_DETOUR detourStruct = { 0 };
906  DWORD relInstrCount = 0;
907  BYTE functionCode[DETOUR_MAX_FUNCTION_SIZE] = {0};
908 
909  if (FunctionAddress == 0)
910  {
912  }
913 
914  if (FnDetour == NULL)
915  {
917  }
918 
919  status = IntKernVirtMemRead(FunctionAddress, sizeof(functionCode), functionCode, NULL);
920  if (!INT_SUCCESS(status))
921  {
922  ERROR("[ERROR] IntKernVirtMemRead failed with status: 0x%08x\n", status);
923  return status;
924  }
925 
926  status = IntDetCreateObjectLix(FunctionAddress, FnDetour, &detourStruct, &pDetour);
927  if (!INT_SUCCESS(status))
928  {
929  ERROR("[ERROR] IntDetCreateObjectLix failed with status: 0x%08x.", status);
930  return status;
931  }
932 
933 
934  status = IntDetRelocate(pDetour, functionCode, sizeof(functionCode), &relInstrCount);
935  if (!INT_SUCCESS(status))
936  {
937  ERROR("[WARNING] IntDetRelocate failed with status: 0x%08x.", status);
938  goto _exit;
939  }
940 
941  *MultipleInstructions = FALSE;
942  if (relInstrCount > 1)
943  {
944  *MultipleInstructions = TRUE;
945  }
946 
947  status = IntDetPatchFunction(pDetour, functionCode, sizeof(functionCode));
948  if (!INT_SUCCESS(status))
949  {
950  ERROR("[ERROR] IntDetPatchFunction failed with status: 0x%08x.", status);
951  goto _exit;
952  }
953 
954  detourStruct.EnableOptions = FnDetour->EnableFlags;
955  detourStruct.JumpBack = FunctionAddress + pDetour->RelocatedCodeLength;
956 
957  // The written area is protected, so no need to use IntVirtMemSafeWrite.
958  status = IntKernVirtMemWrite(pDetour->LixGuestDetour, sizeof(detourStruct), &detourStruct);
959  if (!INT_SUCCESS(status))
960  {
961  ERROR("[ERROR] IntKernVirtMemWrite failed with status: 0x%08x\n", status);
962  goto _exit;
963  }
964 
965  InsertTailList(&gDetours.DetoursList, &pDetour->Link);
966 
967  gDetours.LixDetourTable[FnDetour->Id] = pDetour;
968 
969  LOG("[DETOUR] %llx: handler @ %llx (%s)\n",
970  pDetour->FunctionAddress, pDetour->HandlerAddress, FnDetour->FunctionName);
971 
972  status = INT_STATUS_SUCCESS;
973 
974 _exit:
975  if (!INT_SUCCESS(status) && (NULL != pDetour))
976  {
977  IntDetRemoveDetour(pDetour);
978  }
979 
980  return status;
981 }
982 
983 
984 INTSTATUS
986  _In_ QWORD FunctionAddress,
987  _In_ QWORD ModuleBase,
988  _Inout_ API_HOOK_DESCRIPTOR *Descriptor,
989  _Inout_ API_HOOK_HANDLER *Handler
990  )
1040 {
1041  INTSTATUS status;
1042  DETOUR *pDetour;
1043  INSTRUX instrux;
1046  DWORD prologueSize, newJumpBackOffset, totalHandlerSize, i;
1047 
1048  prologueSize = 0;
1049 
1050  if (NULL == Descriptor)
1051  {
1053  }
1054 
1055  if (Descriptor->Tag >= detTagMax)
1056  {
1058  }
1059 
1060  if (NULL == Handler)
1061  {
1063  }
1064 
1065  if (0 == ModuleBase)
1066  {
1067  ModuleBase = gGuest.KernelVa;
1068  }
1069 
1070  if (FunctionAddress)
1071  {
1072  //
1073  // Get the original function code (from the kernel buffer if we have that)
1074  //
1075  if ((gGuest.OSType == introGuestWindows) && (ModuleBase == gGuest.KernelVa))
1076  {
1077  DWORD funOffset = (DWORD)(FunctionAddress - ModuleBase);
1078 
1079  if (funOffset + sizeof(origFn) > gWinGuest->KernelBufferSize)
1080  {
1081  ERROR("[ERROR] Function not inside kernel buffer. Offset: 0x%08x, size: 0x%08zx, buffer size: 0x%08x\n",
1082  funOffset, sizeof(origFn), gWinGuest->KernelBufferSize);
1084  }
1085 
1086  memcpy(origFn, gWinGuest->KernelBuffer + funOffset, sizeof(origFn));
1087  }
1088  else
1089  {
1090  status = IntKernVirtMemRead(FunctionAddress, sizeof(origFn), origFn, NULL);
1091  if (!INT_SUCCESS(status))
1092  {
1093  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1094  return status;
1095  }
1096  }
1097 
1098  // If the function is already hooked, leave.
1099  if (origFn[0] == 0xE9)
1100  {
1101  WARNING("[INFO] API function already detoured (0x%016llx), don't know by who! Aborting!\n",
1102  FunctionAddress + SIGN_EX_32(*(DWORD *)&origFn[1]) + 5);
1103  return INT_STATUS_NOT_SUPPORTED;
1104  }
1105 
1106  // Compute the size of the prologue. We need this before actually parsing the prologue in order to know how
1107  // much slack to allocate.
1108  while (prologueSize < 5)
1109  {
1110  status = IntDecDecodeInstructionFromBuffer(origFn + prologueSize,
1111  sizeof(origFn) - prologueSize,
1113  &instrux);
1114  if (!INT_SUCCESS(status))
1115  {
1116  return status;
1117  }
1118 
1119  prologueSize += instrux.Length;
1120  }
1121  }
1122 
1123  totalHandlerSize = Handler->CodeLength + prologueSize;
1124 
1125  if (sizeof(newHnd) < totalHandlerSize)
1126  {
1127  ERROR("[ERROR] The size of the handler exceeds %zu bytes: %d!\n", sizeof(newHnd), totalHandlerSize);
1128  return INT_STATUS_NOT_SUPPORTED;
1129  }
1130 
1131  pDetour = HpAllocWithTag(sizeof(*pDetour), IC_TAG_DETG);
1132  if (NULL == pDetour)
1133  {
1135  }
1136 
1137  status = IntSlackAlloc(ModuleBase, FALSE, totalHandlerSize, &pDetour->HandlerAddress, 0);
1138  if (!INT_SUCCESS(status))
1139  {
1140  ERROR("[ERROR] IntSlackAlloc failed: 0x%08x\n", status);
1141  goto cleanup_and_exit;
1142  }
1143 
1144  if (NULL != Descriptor->PreCallback)
1145  {
1146  status = Descriptor->PreCallback(FunctionAddress, Handler, pDetour->HandlerAddress);
1147  if (!INT_SUCCESS(status))
1148  {
1149  ERROR("[ERROR] PreCallback for hook %d (`%s!%s`) failed: 0x%08x\n",
1150  Descriptor->Tag, utf16_for_log(Descriptor->ModuleName), Descriptor->FunctionName, status);
1151  goto cleanup_and_exit;
1152  }
1153  else if (INT_STATUS_NOT_NEEDED_HINT == status)
1154  {
1155  LOG("[INFO] PreCallback for hook %d (`%s!%s`) returned NOT NEEDED. Will skip the hook (critical: %d)\n",
1156  Descriptor->Tag,
1157  utf16_for_log(Descriptor->ModuleName),
1158  Descriptor->FunctionName,
1159  !Descriptor->NotCritical);
1160 
1161  goto cleanup_and_exit;
1162  }
1163  }
1164 
1165  status = IntKernVirtMemRead(pDetour->HandlerAddress, totalHandlerSize, origHnd, NULL);
1166  if (!INT_SUCCESS(status))
1167  {
1168  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1169  goto cleanup_and_exit;
1170  }
1171 
1172  memcpy(newHnd, Handler->Code, totalHandlerSize - prologueSize);
1173 
1174  if (0 == FunctionAddress)
1175  {
1176  goto _no_function;
1177  }
1178 
1179  // Clone the modified bytes inside the handler code. We need to relocate minimum 5 bytes
1180  prologueSize = 0;
1181  while (prologueSize < 5)
1182  {
1183  BOOLEAN relInstr = FALSE;
1184 
1185  status = IntDecDecodeInstructionFromBuffer(origFn + prologueSize,
1186  sizeof(origFn) - prologueSize,
1188  &instrux);
1189  if (!INT_SUCCESS(status))
1190  {
1191  goto cleanup_and_exit;
1192  }
1193 
1194  relInstr = NdIsInstruxRipRelative(&instrux);
1195 
1196  if (relInstr)
1197  {
1198  if ((instrux.PrimaryOpCode != 0xe8) && // CALL
1199  (instrux.PrimaryOpCode != 0x89) && // MOV
1200  (instrux.PrimaryOpCode != 0x8b) && // MOV
1201  (instrux.PrimaryOpCode != 0x80) && // CMP
1202  (instrux.PrimaryOpCode != 0x63) && // MOVSXD
1203  (instrux.Instruction != ND_INS_Jcc || instrux.Length != 6))
1204  {
1205  CHAR insText[ND_MIN_BUF_SIZE] = {0};
1206 
1207  NdToText(&instrux, FunctionAddress + prologueSize, sizeof(insText), insText);
1208 
1209  WARNING("[WARNING] RIP relative instruction '%s' found at GVA 0x%016llx, can't continue!\n",
1210  insText, FunctionAddress + prologueSize);
1211  IntDumpInstruction(&instrux, FunctionAddress + prologueSize);
1212 
1213  status = INT_STATUS_NOT_SUPPORTED;
1214  goto cleanup_and_exit;
1215  }
1216  }
1217 
1218  if (instrux.PrimaryOpCode == 0xe8)
1219  {
1220  QWORD calledFunc = FunctionAddress + prologueSize + SIGN_EX(instrux.RelOffsLength, instrux.RelativeOffset);
1221 
1222  DWORD patched = (DWORD)(calledFunc -
1223  (pDetour->HandlerAddress + Handler->RelocatedCodeOffset + prologueSize));
1224 
1225  newHnd[Handler->RelocatedCodeOffset + prologueSize] = 0xe8;
1226  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 1, &patched, 4);
1227  }
1228  else if (relInstr && (instrux.PrimaryOpCode == 0x8b || instrux.PrimaryOpCode == 0x89))
1229  {
1230  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
1231 
1232  QWORD movGva = FunctionAddress + prologueSize + SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
1233  DWORD patched = (DWORD)(movGva - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset + prologueSize));
1234 
1235  if (instrux.Rex.Rex != 0)
1236  {
1237  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1238  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1239  newHnd[Handler->RelocatedCodeOffset + prologueSize + 2] = instrux.InstructionBytes[2];
1240 
1241  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 3, &patched, 4);
1242  }
1243  else
1244  {
1245  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1246  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1247 
1248  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
1249  }
1250  }
1251  else if (relInstr && instrux.PrimaryOpCode == 0x80)
1252  {
1253  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
1254 
1255  QWORD cmpGva = FunctionAddress + prologueSize + SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
1256  DWORD patched = (DWORD)(cmpGva - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset + prologueSize));
1257 
1258  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1259  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1260 
1261  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &patched, 4);
1262 
1263  newHnd[Handler->RelocatedCodeOffset + prologueSize + 6] = instrux.InstructionBytes[6];
1264  }
1265  else if (relInstr && instrux.Instruction == ND_INS_Jcc)
1266  {
1267  QWORD target = FunctionAddress + instrux.Operands[0].Info.RelativeOffset.Rel;
1268  DWORD newRel = (DWORD)(target - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset));
1269 
1270  // Translate the old JMP into the new JMP. We make it long and we modify the relative offset so that it
1271  // points to the same address. Important note: we must make sure that the target is NOT inside the
1272  // relocated code, otherwise we need to do some more black magic.
1273  newHnd[Handler->RelocatedCodeOffset + prologueSize] = 0x0F;
1274  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = 0x80 | instrux.Predicate;
1275 
1276  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &newRel, 4);
1277  }
1278  else if (relInstr && instrux.PrimaryOpCode == 0x63)
1279  {
1280  DWORD relOp = (instrux.Operands[0].Type == ND_OP_MEM) ? 0 : 1;
1281  QWORD crtInstruxGva = FunctionAddress + prologueSize + SIGN_EX(4, instrux.Operands[relOp].Info.Memory.Disp);
1282  DWORD crtInstruxPatch = (DWORD)(crtInstruxGva - (pDetour->HandlerAddress + Handler->RelocatedCodeOffset +
1283  prologueSize));
1284 
1285  if (instrux.Rex.Rex != 0)
1286  {
1287  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1288  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1289  newHnd[Handler->RelocatedCodeOffset + prologueSize + 2] = instrux.InstructionBytes[2];
1290 
1291 
1292  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 3, &crtInstruxPatch, 4);
1293  }
1294  else
1295  {
1296  newHnd[Handler->RelocatedCodeOffset + prologueSize] = instrux.InstructionBytes[0];
1297  newHnd[Handler->RelocatedCodeOffset + prologueSize + 1] = instrux.InstructionBytes[1];
1298 
1299  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize + 2, &crtInstruxPatch, 4);
1300  }
1301  }
1302  else
1303  {
1304  // Copy the current instruction inside the detour handler
1305  memcpy(newHnd + Handler->RelocatedCodeOffset + prologueSize, origFn + prologueSize, instrux.Length);
1306  }
1307 
1308  prologueSize += instrux.Length;
1309  }
1310 
1311  // Patch the jump from the handler code. Note that the jump address inside the handler code is
1312  // variable. The fact that the MAX_PROLOGUE_SIZE is defined to 8 bytes is just a template. The
1313  // actual layout of the handler code once injected inside the guest is variable. The JMP back
1314  // will be located immediately after the prologue, be it 5 or 20 bytes in length.
1315  newJumpBackOffset = Handler->RelocatedCodeOffset + prologueSize;
1316 
1317  // Set the JMP opcode.
1318  *(newHnd + newJumpBackOffset) = 0xE9;
1319 
1320  *(DWORD *)(newHnd + newJumpBackOffset + 1) = (DWORD)((FunctionAddress + 5) - (pDetour->HandlerAddress +
1321  newJumpBackOffset + 5));
1322 
1323  // Now re-patch the jump from handler code to jump after the whole prologue (and don't execute the NOP)
1324  *(DWORD *)(newHnd + newJumpBackOffset + 1) += prologueSize - 5;
1325 
1326  // Set a jmp handler
1327  newFn[0] = 0xE9;
1328  *(DWORD *)(newFn + 1) = (DWORD)(pDetour->HandlerAddress - (FunctionAddress + 5));
1329 
1330  for (i = 5; i < prologueSize; i++)
1331  {
1332  newFn[i] = 0x90;
1333  }
1334 
1335 _no_function:
1336 
1337  pDetour->Tag = Descriptor->Tag;
1338  // This can be NULL! We can have dummy handlers/handlers that don't issue hypercalls.
1339  pDetour->Callback = Descriptor->Callback;
1340  pDetour->HandlerSize = totalHandlerSize;
1341  pDetour->FunctionAddress = FunctionAddress;
1342  // The hypercall can be missing from the handler, in which case the HypercallAddress will be 0.
1343  pDetour->HypercallAddress = (Handler->HypercallOffset == DETOUR_INVALID_HYPERCALL) ? 0 : pDetour->HandlerAddress +
1344  Handler->HypercallOffset;
1345  pDetour->HypercallOffset = Handler->HypercallOffset;
1346  pDetour->ModuleBase = ModuleBase;
1347  pDetour->HypercallType = Handler->HypercallType;
1348  pDetour->JumpBackOffset = Handler->RelocatedCodeOffset + (BYTE)prologueSize;
1349  pDetour->RelocatedCodeOffset = Handler->RelocatedCodeOffset;
1350  pDetour->RelocatedCodeLength = (BYTE)prologueSize;
1351 
1352  memcpy(pDetour->PublicDataOffsets, Handler->PublicDataOffsets,
1353  sizeof(API_HOOK_PUBLIC_DATA) * Handler->NrPublicDataOffsets);
1354  pDetour->NrPublicDataOffsets = Handler->NrPublicDataOffsets;
1355 
1356  pDetour->Descriptor = Descriptor;
1357 
1358  status = IntMemClkCloakRegion(pDetour->HandlerAddress,
1359  0,
1360  pDetour->HandlerSize,
1362  origHnd,
1363  newHnd,
1364  NULL,
1365  &pDetour->HandlerCloakHandle);
1366  if (!INT_SUCCESS(status))
1367  {
1368  ERROR("[ERROR] IntMemClkCloackRegion failed: 0x%08x\n", status);
1369  goto cleanup_and_exit;
1370  }
1371 
1372  if (FunctionAddress)
1373  {
1374  status = IntMemClkCloakRegion(pDetour->FunctionAddress,
1375  0,
1376  pDetour->RelocatedCodeLength,
1378  origFn,
1379  newFn,
1380  NULL,
1381  &pDetour->FunctionCloakHandle);
1382  if (!INT_SUCCESS(status))
1383  {
1384  ERROR("[ERROR] IntMemClkCloackRegion failed: 0x%08x\n", status);
1385  goto cleanup_and_exit;
1386  }
1387  }
1388 
1389  // Add thew new detour to the list.
1390  InsertTailList(&gDetours.DetoursList, &pDetour->Link);
1391 
1392  gDetours.DetoursTable[pDetour->Tag] = pDetour;
1393 
1394  TRACE("[DETOUR] Hooked function @ 0x%016llx, handler @ 0x%016llx, hypercall @ 0x%016llx\n",
1395  pDetour->FunctionAddress, pDetour->HandlerAddress, pDetour->HypercallAddress);
1396 
1397  // This just checks if the current detour overlaps with the SSDT somehow...
1399  {
1400  QWORD start = gWinGuest->Ssdt & PAGE_MASK;
1401  // Multiply the number of services by 4 because on 32-bit kernels the SSDT contains pointers, while on
1402  // 64-bit kernels it contains offsets inside the kernel image.
1403  QWORD end = start + gWinGuest->NumberOfServices * 4ull;
1404 
1405  end = ALIGN_UP(end, PAGE_SIZE);
1406 
1407  TRACE("[INFO] Will ensure that SSDT isn't in our detours!\n");
1408 
1409  if (IN_RANGE(pDetour->FunctionAddress, start, end) ||
1410  IN_RANGE(pDetour->FunctionAddress + pDetour->RelocatedCodeLength, start, end))
1411  {
1412  WARNING("[WARNING] SSDT 0x%016llx (%d services) is in the same page (function) as detour %d \n",
1414  }
1415 
1416  if (IN_RANGE(pDetour->HandlerAddress, start, end) ||
1417  IN_RANGE(pDetour->HandlerAddress + pDetour->HandlerSize, start, end))
1418  {
1419  WARNING("[WARNING] SSDT 0x%016llx (%d services) is in the same page (handler) as detour %d \n",
1421  }
1422  }
1423 
1424 cleanup_and_exit:
1425  if (!INT_SUCCESS(status) && (NULL != pDetour))
1426  {
1427  IntDetRemoveDetour(pDetour);
1428  }
1429  else
1430  {
1431  if (NULL != Descriptor->PostCallback)
1432  {
1433  status = Descriptor->PostCallback(Handler);
1434  if (!INT_SUCCESS(status))
1435  {
1436  WARNING("[ERROR] PostCallback for hook %d (`%s!%s`) failed: 0x%08x\n",
1437  Descriptor->Tag, utf16_for_log(Descriptor->ModuleName), Descriptor->FunctionName, status);
1438  }
1439 
1440  status = INT_STATUS_SUCCESS;
1441  }
1442  }
1443 
1444  return status;
1445 }
1446 
1447 
1448 INTSTATUS
1450  _In_ DETOUR const *Detour,
1451  _Inout_opt_ IG_ARCH_REGS *Registers,
1452  _In_ QWORD ReturnValue
1453  )
1472 {
1473  INTSTATUS status;
1474  IG_ARCH_REGS regs;
1475 
1476  if (NULL == Registers)
1477  {
1478  status = IntGetGprs(gVcpu->Index, &regs);
1479  if (!INT_SUCCESS(status))
1480  {
1481  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
1482  return status;
1483  }
1484 
1485  Registers = &regs;
1486  }
1487 
1488  switch (Detour->HypercallType)
1489  {
1490  case hypercallTypeInt3:
1491  Registers->Rax = ReturnValue;
1492  break;
1493 
1494  case hypercallTypeVmcall:
1495  Registers->Rsi = ReturnValue;
1496  break;
1497 
1498  default:
1499  {
1500  ERROR("[DETOUR] Invalid hypercall type %d ...", Detour->HypercallType);
1501  return INT_STATUS_NOT_SUPPORTED;
1502  }
1503  }
1504 
1505  status = IntSetGprs(gVcpu->Index, Registers);
1506  if (!INT_SUCCESS(status))
1507  {
1508  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1509  return status;
1510  }
1511 
1512  return INT_STATUS_SUCCESS;
1513 }
1514 
1515 
1516 INTSTATUS
1518  void
1519  )
1543 {
1545  DETOUR *pDetour = NULL;
1546  BOOLEAN bCalled = FALSE;
1547 
1548  if (gGuest.OSType == introGuestLinux &&
1549  IN_RANGE_LEN(gVcpu->Regs.Rip, gLixGuest->MmAlloc.Detour.Code.Address, gLixGuest->MmAlloc.Detour.Code.Length))
1550  {
1551  DETOUR *pDet = NULL;
1552  if (gVcpu->Regs.Rbx >= det_max_id)
1553  {
1554  return INT_STATUS_NOT_FOUND;
1555  }
1556 
1557  pDet = gDetours.LixDetourTable[gVcpu->Regs.Rbx];
1558 
1559  if (!pDet->Disabled)
1560  {
1561  pDet->HitCount++;
1562  status = gLixHookHandlersx64[gVcpu->Regs.Rbx].Callback(pDet);
1563  }
1564  else
1565  {
1566  status = INT_STATUS_SUCCESS;
1567  }
1568 
1569  pDetour = pDet;
1570  }
1571  else
1572  {
1573  for_each_detour(pDet)
1574  {
1575  // We match a detour using the RIP that generated the VMEXIT. Also, ignore detours with missing handlers.
1576  if (pDet->HypercallAddress != gVcpu->Regs.Rip ||
1577  pDet->HypercallAddress == 0 ||
1578  pDet->HypercallOffset == DETOUR_INVALID_HYPERCALL ||
1579  pDet->Callback == NULL)
1580  {
1581  continue;
1582  }
1583 
1584  if (!pDet->Disabled)
1585  {
1586  // Note: detours can be safely added from other detour handlers.
1587  // However, we can only remove the current detour, as removing other detours will possibly
1588  // corrupt the detours list.
1589  status = pDet->Callback(pDet);
1590 
1591  bCalled = TRUE;
1592 
1593  pDet->HitCount++;
1594  }
1595  else
1596  {
1597  status = INT_STATUS_SUCCESS;
1598  }
1599 
1600  pDetour = pDet;
1601 
1602  break;
1603  }
1604  }
1605 
1606  if (INT_STATUS_DISABLE_DETOUR_ON_RET == status)
1607  {
1608  LOG("[DETOUR] Removing detour with tag %d\n", pDetour->Tag);
1610  }
1611  else if (INT_STATUS_REMOVE_DETOUR_AND_SET_RIP == status)
1612  {
1613  if (pDetour->HypercallType == hypercallTypeVmcall)
1614  {
1615  WARNING("[WARNING] The detour uses vmcall as a hypercall. Cannot remove and set RIP ...");
1616  return INT_STATUS_SUCCESS;
1617  }
1618 
1619  if (pDetour->HypercallType == hypercallTypeInt3)
1620  {
1621  LOG("[DETOUR] Removing detour with tag %d and setting RIP[%d] on 0x%016llx\n",
1622  pDetour->Tag, gVcpu->Index, pDetour->FunctionAddress);
1623 
1625 
1626  gVcpu->Regs.Rip = pDetour->FunctionAddress;
1627 
1629 
1630  return INT_STATUS_NO_DETOUR_EMU;
1631  }
1632  }
1633  else if (bCalled)
1634  {
1635  // Force success if any callback was called, in case that some unknown statuses are returned, such as
1636  // INT_STATUS_NOT_FOUND, which can generate guest crashes by re-injecting the int3.
1637  return INT_STATUS_SUCCESS;
1638  }
1639 
1640  return status;
1641 }
1642 
1643 
1644 INTSTATUS
1646  _In_ DETOUR_TAG Tag,
1647  _In_ void const *Data,
1648  _In_ DWORD DataSize,
1649  _In_ char const *PublicDataName
1650  )
1673 {
1674  INTSTATUS status;
1675  DETOUR *pDet;
1676  API_HOOK_PUBLIC_DATA *pPublicData = NULL;
1677 
1678  if (Tag >= detTagMax)
1679  {
1681  }
1682 
1683  if (NULL == Data)
1684  {
1686  }
1687 
1688  if (0 == DataSize)
1689  {
1691  }
1692 
1693  if (NULL == PublicDataName)
1694  {
1696  }
1697 
1698  pDet = gDetours.DetoursTable[Tag];
1699  if (NULL == pDet)
1700  {
1702  }
1703 
1704  if (NULL == pDet->HandlerCloakHandle)
1705  {
1707  }
1708 
1709  for (DWORD i = 0; i < pDet->NrPublicDataOffsets; i++)
1710  {
1711  if (!strcmp(PublicDataName, pDet->PublicDataOffsets[i].PublicDataName))
1712  {
1713  pPublicData = &pDet->PublicDataOffsets[i];
1714  break;
1715  }
1716  }
1717 
1718  if (NULL == pPublicData)
1719  {
1720  return INT_STATUS_NOT_FOUND;
1721  }
1722 
1723  if (DataSize > pPublicData->PublicDataSize)
1724  {
1726  }
1727 
1728  LOG("[DETOUR] Will modify patched data for public data %s at offset %d with size %u\n",
1729  pPublicData->PublicDataName, pPublicData->PublicDataOffset, DataSize);
1730 
1731  status = IntMemClkModifyPatchedData(pDet->HandlerCloakHandle, pPublicData->PublicDataOffset, DataSize, Data);
1732  if (!INT_SUCCESS(status))
1733  {
1734  ERROR("[ERROR] IntMemClkModifyPatchedData failed: 0x%08x\n", status);
1735  }
1736 
1737  return status;
1738 }
1739 
1740 
1741 void
1743  void
1744  )
1753 {
1754  for_each_detour(pDet)
1755  {
1757  }
1758 }
1759 
1760 
1761 void
1763  void
1764  )
1773 {
1774  for_each_detour(pDet)
1775  {
1776  RemoveEntryList(&pDet->Link);
1777 
1778  gDetours.DetoursTable[pDet->Tag] = NULL;
1779 
1780  IntDetRemoveDetour(pDet);
1781  }
1782 
1783  memzero(&gDetours, sizeof(gDetours));
1784  InitializeListHead(&gDetours.DetoursList);
1785 }
1786 
1787 
1788 void
1790  void
1791  )
1795 {
1796  LOG("[DBGINTRO] Introspection detours:\n");
1797 
1798  for_each_detour(pDetour)
1799  {
1800  char *pFnName = NULL;
1801  if (gGuest.OSType == introGuestLinux)
1802  {
1803  if (pDetour->LixFnDetour != NULL)
1804  {
1805  pFnName = pDetour->LixFnDetour->FunctionName;
1806  }
1807  else if (pDetour->Descriptor != NULL)
1808  {
1809  pFnName = pDetour->Descriptor->FunctionName;
1810  }
1811  }
1812  else if (gGuest.OSType == introGuestWindows)
1813  {
1814  pFnName = pDetour->Descriptor != NULL ? pDetour->Descriptor->FunctionName : NULL;
1815  }
1816 
1817  LOG(" ## %-32s Hits: %12llu, RIP: %llx, Tag: %02d, Handler RIP: %llx (+ %02x), Hooked RIP: %llx\n",
1818  pFnName != NULL ? pFnName : "unknown",
1819  pDetour->HitCount,
1820  pDetour->HypercallAddress,
1821  pDetour->Tag,
1822  pDetour->HandlerAddress,
1823  pDetour->HandlerSize,
1824  pDetour->FunctionAddress);
1825 
1826  if (gGuest.OSType == introGuestWindows && pDetour->ModuleBase != gGuest.KernelVa)
1827  {
1828  LOG(" Module: %llx\n",
1829  pDetour->ModuleBase);
1830  }
1831  }
1832 }
1833 
1834 
1835 BOOLEAN
1837  _In_ QWORD Ptr,
1838  _Out_opt_ DETOUR_TAG *Tag
1839  )
1849 {
1850  for_each_detour(pDet)
1851  {
1852  if (Ptr >= pDet->FunctionAddress &&
1853  Ptr < pDet->FunctionAddress + pDet->RelocatedCodeLength)
1854  {
1855  if (NULL != Tag)
1856  {
1857  *Tag = pDet->Tag;
1858  }
1859 
1860  return TRUE;
1861  }
1862  }
1863 
1864  if (NULL != Tag)
1865  {
1866  *Tag = detTagMax;
1867  }
1868 
1869  return FALSE;
1870 }
1871 
1872 
1873 BOOLEAN
1875  _In_ QWORD Ptr,
1876  _In_ THS_PTR_TYPE Type,
1877  _Out_opt_ DETOUR_TAG *Tag
1878  )
1888 {
1889 
1890  if (gGuest.OSType == introGuestLinux)
1891  {
1892  if (Tag)
1893  {
1894  *Tag = 0;
1895  }
1896 
1897  if (IN_RANGE_LEN(Ptr, gLixGuest->MmAlloc.Detour.Code.Address, gLixGuest->MmAlloc.Detour.Code.Length))
1898  {
1899  return TRUE;
1900  }
1901  }
1902 
1903  for_each_detour(pDet)
1904  {
1905  if ((Ptr >= pDet->HandlerAddress) && (Ptr < pDet->HandlerAddress + pDet->HandlerSize))
1906  {
1907  WARNING("[WARNING] Found %s ptr 0x%016llx in detours: handler RIP 0x%016llx, hook RIP 0x%016llx\n",
1908  Type == ptrLiveRip ? "live RIP" : "stack value", Ptr, pDet->HandlerAddress, pDet->FunctionAddress);
1909 
1910  // This is ugly, but we want to avoid modifying the RtlpVirtualUnwind handler while a RIP
1911  // is pointing inside of it, since we can't atomically modify 2 instructions if the first
1912  // one has already executed...
1913  if (pDet->Tag >= detTagRtlVirtualUnwind1 && pDet->Tag < detTagRtlVirtualUnwindMax)
1914  {
1916 
1917  if (Ptr < pDet->HandlerAddress + pDet->RelocatedCodeOffset)
1918  {
1919  gRipInsideRtlpVirtualUnwindReloc = TRUE;
1920  }
1921  }
1922 
1923  if (NULL != Tag)
1924  {
1925  *Tag = pDet->Tag;
1926  }
1927 
1928  return TRUE;
1929  }
1930  }
1931 
1932  if (NULL != Tag)
1933  {
1934  *Tag = detTagMax;
1935  }
1936 
1937  return FALSE;
1938 }
1939 
1940 
1941 QWORD
1943  _In_ QWORD Ptr
1944  )
1952 {
1953  for_each_detour(pDetour)
1954  {
1955  if ((Ptr > pDetour->FunctionAddress) &&
1956  (Ptr < pDetour->FunctionAddress + pDetour->RelocatedCodeLength))
1957  {
1958  return pDetour->HandlerAddress + pDetour->RelocatedCodeOffset + (Ptr - pDetour->FunctionAddress);
1959  }
1960  }
1961 
1962  // No modification, return it as it is.
1963  return Ptr;
1964 }
1965 
1966 
1967 INTSTATUS
1969  _In_ QWORD Ptr,
1970  _Out_ QWORD *Address,
1971  _Out_ DWORD *Size,
1972  _Out_ DETOUR_TAG *Tag
1973  )
1985 {
1986  for_each_detour(pDetour)
1987  {
1988  if ((Ptr > pDetour->HandlerAddress) &&
1989  (Ptr < pDetour->HandlerAddress + pDetour->HandlerSize))
1990  {
1991  *Address = pDetour->HandlerAddress;
1992  *Size = pDetour->HandlerSize;
1993  *Tag = pDetour->Tag;
1994 
1995  return INT_STATUS_SUCCESS;
1996  }
1997  }
1998 
1999  return INT_STATUS_NOT_FOUND;
2000 }
2001 
2002 
2003 INTSTATUS
2005  _In_ DETOUR_TAG Tag,
2006  _Out_ QWORD *Address,
2007  _Out_opt_ DWORD *Size
2008  )
2019 {
2020  DETOUR *pDet = IntDetFindByTag(Tag);
2021  if (pDet)
2022  {
2023  *Address = pDet->HandlerAddress;
2024 
2025  if (NULL != Size)
2026  {
2027  *Size = pDet->HandlerSize;
2028  }
2029 
2030  return INT_STATUS_SUCCESS;
2031  }
2032 
2033  return INT_STATUS_NOT_FOUND;
2034 }
2035 
2036 
2037 static INTSTATUS
2039  _In_ DWORD Arg,
2040  _In_opt_ BYTE const *StackBuffer,
2041  _In_ DWORD StackBufferSize,
2042  _Out_ QWORD *Value
2043  )
2057 {
2058  IG_ARCH_REGS const *pRegs = &gVcpu->Regs;
2059  BOOLEAN haveStackBuffer = StackBuffer && StackBufferSize;
2060 
2061  *Value = 0;
2062 
2063  if (DET_ARG_ON_STACK(Arg)) // Read argument from the stack
2064  {
2065  DWORD stackOffset = DET_ARG_STACK_OFFSET(Arg);
2066 
2067  if (haveStackBuffer && stackOffset + gGuest.WordSize <= StackBufferSize)
2068  {
2069  if (gGuest.WordSize == 4)
2070  {
2071  *Value = *(DWORD const *)(StackBuffer + stackOffset);
2072  }
2073  else
2074  {
2075  *Value = *(QWORD const *)(StackBuffer + stackOffset);
2076  }
2077  }
2078  else
2079  {
2080  INTSTATUS status;
2081 
2082  status = IntKernVirtMemRead(pRegs->Rsp + stackOffset, gGuest.WordSize, Value, NULL);
2083  if (!INT_SUCCESS(status))
2084  {
2085  ERROR("[ERROR] IntKernVirtMemRead failed for %llx: 0x%08x\n", pRegs->Rsp + stackOffset, status);
2086  return status;
2087  }
2088  }
2089  }
2090  else if (DET_ARG_REGS(Arg)) // Simply get from registers
2091  {
2092  *Value = ((QWORD const *)pRegs)[Arg];
2093  }
2094  else
2095  {
2096  ERROR("[ERROR] Invalid argument type: 0x%08x\n", Arg);
2098  }
2099 
2100  return INT_STATUS_SUCCESS;
2101 }
2102 
2103 
2104 INTSTATUS
2106  _In_ void const *Detour,
2107  _In_ DWORD Index,
2108  _In_opt_ BYTE const *StackBuffer,
2109  _In_ DWORD StackBufferSize,
2110  _Out_ QWORD *Value
2111  )
2131 {
2132  API_HOOK_DESCRIPTOR const *pApi;
2133  DETOUR const *pDetour;
2134  DWORD const *pArgs;
2135 
2136  if (NULL == Detour)
2137  {
2139  }
2140 
2141  if (Index >= DET_ARGS_MAX)
2142  {
2144  }
2145 
2146  if (NULL == Value)
2147  {
2149  }
2150 
2151  pDetour = Detour;
2152  pApi = pDetour->Descriptor;
2153  if (NULL == pApi)
2154  {
2156  }
2157 
2158  pArgs = pApi->Arguments.Argv;
2159  return IntDetGetArgumentInternal(pArgs[Index], StackBuffer, StackBufferSize, Value);
2160 }
2161 
2162 
2163 INTSTATUS
2165  _In_ void const *Detour,
2166  _In_ DWORD Argc,
2167  _Out_writes_(Argc) QWORD *Argv
2168  )
2189 {
2190  BYTE stackBuffer[256];
2191  DETOUR const *detour;
2192  API_HOOK_DESCRIPTOR const *api;
2193  DWORD const *args;
2194  DWORD stackBufferSize = 0;
2195  BYTE const *pStackBuffer = NULL;
2196  DWORD argc;
2197 
2198  if (NULL == Detour)
2199  {
2201  }
2202 
2203  if (0 == Argc || DET_ARGS_MAX <= Argc)
2204  {
2206  }
2207 
2208  if (NULL == Argv)
2209  {
2211  }
2212 
2213  detour = Detour;
2214  api = detour->Descriptor;
2215  if (NULL == api)
2216  {
2218  }
2219 
2220  args = api->Arguments.Argv;
2221  argc = Argc;
2222  if (argc > api->Arguments.Argc)
2223  {
2224  ERROR("[ERROR] Requested to read %u arguments for for detour %d, but only %u exist.\n",
2225  argc, detour->Tag, api->Arguments.Argc);
2227  }
2228 
2229  for (DWORD i = 0; i < argc; i++)
2230  {
2231  if (DET_ARG_ON_STACK(args[i]))
2232  {
2233  stackBufferSize = MAX(stackBufferSize, DET_ARG_STACK_OFFSET(args[i]));
2234  }
2235  }
2236 
2237  if (stackBufferSize)
2238  {
2239  INTSTATUS status;
2240  QWORD rsp = gVcpu->Regs.Rsp;
2241 
2242  // Add the size of the last parameter
2243  stackBufferSize += gGuest.WordSize;
2244  // Just in case we have something so far up the stack
2245  stackBufferSize = MIN(stackBufferSize, sizeof(stackBuffer));
2246 
2247  status = IntKernVirtMemRead(rsp, stackBufferSize, &stackBuffer, NULL);
2248  if (!INT_SUCCESS(status))
2249  {
2250  WARNING("[WARNING] IntKernVirtMemRead failed for [0x%016llx, 0x%016llx]: 0x%08x\n",
2251  rsp, rsp + stackBufferSize, status);
2252  stackBufferSize = 0;
2253  // We could still try to read them one at a time
2254  }
2255  else
2256  {
2257  pStackBuffer = stackBuffer;
2258  }
2259  }
2260 
2261  for (DWORD i = 0; i < argc; i++)
2262  {
2263  // pStackBuffer will be NULL if we get here and we could not read the stack
2264  // or there are no arguments on the stack; IntDetGetArgumentInternal will handle that
2265  INTSTATUS status = IntDetGetArgumentInternal(args[i], pStackBuffer, stackBufferSize, &Argv[i]);
2266  if (!INT_SUCCESS(status))
2267  {
2268  ERROR("[ERROR] IntDetGetArgument failed for %u: 0x%08x\n", i, status);
2269  return status;
2270  }
2271  }
2272 
2273  return INT_STATUS_SUCCESS;
2274 }
2275 
2276 
2277 INTSTATUS
2279  _In_ void const *Detour,
2280  _In_ DWORD Index,
2281  _In_ QWORD Value
2282  )
2296 {
2297  INTSTATUS status;
2298  API_HOOK_DESCRIPTOR const *pApi;
2299  DETOUR const *pDetour;
2300  PIG_ARCH_REGS pRegs;
2301  QWORD arg;
2302 
2303  if (NULL == Detour)
2304  {
2306  }
2307 
2308  pDetour = Detour;
2309  pApi = pDetour->Descriptor;
2310  if (NULL == pApi)
2311  {
2313  }
2314 
2315  if (Index >= pApi->Arguments.Argc)
2316  {
2318  }
2319 
2320  pRegs = &gVcpu->Regs;
2321  arg = pApi->Arguments.Argv[Index];
2322 
2323  if (DET_ARG_ON_STACK(arg))
2324  {
2326  pRegs->Rsp + DET_ARG_STACK_OFFSET(arg),
2327  gGuest.WordSize,
2328  &Value,
2329  IG_CS_RING_0);
2330  if (!INT_SUCCESS(status))
2331  {
2332  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
2333  return status;
2334  }
2335  }
2336  else if (DET_ARG_REGS(arg))
2337  {
2338  ((QWORD *)pRegs)[arg] = Value;
2339 
2340  status = IntSetGprs(gVcpu->Index, pRegs);
2341  if (!INT_SUCCESS(status))
2342  {
2343  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
2344  return status;
2345  }
2346  }
2347  else
2348  {
2349  ERROR("[ERROR] Invalid argument type: 0x%016llx\n", arg);
2351  }
2352 
2353  return INT_STATUS_SUCCESS;
2354 }
static DETOUR * IntDetFindByTag(DETOUR_TAG Tag)
Searches a detour by its tag.
Definition: detours.c:621
INTSTATUS IntDetSetLixHook(QWORD FunctionAddress, const LIX_FN_DETOUR *FnDetour, BOOLEAN *MultipleInstructions)
Detours a function from guest.
Definition: detours.c:874
QWORD LixGuestDetour
The address of the linux-detour header.
Definition: detours.h:459
#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:478
_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:450
static INTSTATUS IntDetEnableHypercall(DETOUR *Detour)
Enables a detour hypercall.
Definition: detours.c:153
Describes the information about a Linux active-patch.
Definition: lixguest.h:458
static void IntDetRemoveBranch(DETOUR *Detour)
Restores the original instructions of a hooked function.
Definition: detours.c:210
#define ALIGN_UP(x, a)
Definition: introdefs.h:164
PDETOUR DetoursTable[detTagMax]
Table of detours, indexed by DETOUR_TAG.
Definition: detours.c:32
BYTE NrPublicDataOffsets
The number of valid entries inside the PublicDataOffsets array.
Definition: detours.h:476
DWORD Argc
The number of valid entries inside the Argv array.
Definition: detours.h:110
uint8_t BYTE
Definition: intro_types.h:47
#define OFFSET_OF(Type, Member)
Definition: introlists.h:33
WINDOWS_GUEST * gWinGuest
Global variable holding the state of a Windows guest.
Definition: winguest.c:35
INTSTATUS IntKernVirtMemWrite(QWORD KernelGva, DWORD Length, void *Buffer)
Writes data to a guest kernel virtual memory range.
Definition: introcore.c:699
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:524
#define _In_
Definition: intro_sal.h:21
struct _LIX_GUEST_DETOUR LIX_GUEST_DETOUR
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
#define INT_STATUS_REMOVE_DETOUR_AND_SET_RIP
Definition: introstatus.h:426
Holds information about the currently set detours.
Definition: detours.c:27
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
Described a detour handler.
Definition: detours.h:279
QWORD HitCount
The number of times this detour issued a hypercall.
Definition: detours.h:494
Describes a detour set inside the guest memory.
Definition: detours.h:432
#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:445
BOOLEAN IsDetour
Definition: lixguest.h:464
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
INTSTATUS IntDetGetArgument(void const *Detour, DWORD Index, BYTE const *StackBuffer, DWORD StackBufferSize, QWORD *Value)
Reads the specified argument for a detour.
Definition: detours.c:2105
#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
WORD Length
The patch length.
Definition: lixguest.h:461
struct _DETOURS_STATE * PDETOURS_STATE
static DETOURS_STATE gDetours
The global detour state.
Definition: detours.c:38
#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:1645
#define SIGN_EX(sz, x)
Definition: introdefs.h:206
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
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:359
#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:805
unsigned long long EnableOptions
Definition: handlers.h:101
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:56
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:274
#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:1742
#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:466
The detour will use a INT3 instruction in order to notify introcore about an event.
Definition: detours.h:185
INTSTATUS IntSlackAlloc(QWORD ModuleBase, BOOLEAN Pageable, DWORD Size, QWORD *Buffer, QWORD SecHint)
Allocate slack inside the guest.
Definition: slack.c:367
PFUNC_DetourCallback Callback
Callback to be invoked when the detour issues a hypercall. May be NULL.
Definition: detours.h:437
INTSTATUS IntDetGetByTag(DETOUR_TAG Tag, QWORD *Address, DWORD *Size)
Get a detour handler address and size by its tag.
Definition: detours.c:2004
void * FunctionCloakHandle
The memory cloak handle used to hide the modified function start. See Memory cloaking.
Definition: detours.h:490
#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:183
static __default_fn_attr size_t vmcall(DETOUR_ID id)
Definition: handlers.c:117
CHAR PublicDataName[PUBLIC_DATA_MAX_NAME_SIZE]
Name used to identify the data.
Definition: detours.h:263
The guest detour API.
static INTSTATUS IntDetDisableWinHypercall(DETOUR *Detour)
Disables a Windows detour hypercall.
Definition: detours.c:96
#define IC_TAG_DETG
Guest detour state.
Definition: memtags.h:16
INTSTATUS IntDetCallCallback(void)
Calls the appropriate detour handler for hypercall.
Definition: detours.c:1517
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:2038
INTSTATUS IntDetSetReturnValue(DETOUR const *Detour, IG_ARCH_REGS *Registers, QWORD ReturnValue)
Sets the return value for a hooked guest function.
Definition: detours.c:1449
#define _Inout_opt_
Definition: intro_sal.h:31
BYTE HypercallOffset
Offset, relative to HandlerAddress, where the hypercall instruction is found.
Definition: detours.h:468
INTSTATUS IntDetPatchArgument(void const *Detour, DWORD Index, QWORD Value)
Modifies the value of a detour argument.
Definition: detours.c:2278
#define _Inout_
Definition: intro_sal.h:20
TIMER_FRIENDLY void IntDumpInstruction(INSTRUX *Instruction, QWORD Rip)
This function dumps a given instruction (textual disassembly).
Definition: dumper.c:513
INTSTATUS IntDetDisableDetour(DETOUR_TAG Tag)
Disables a detour based on its tag.
Definition: detours.c:322
DETOUR_ARGS Arguments
Encoding of the arguments needed by introcore from the hooked function.
Definition: detours.h:375
#define _Out_opt_
Definition: intro_sal.h:30
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
DWORD Argv[DET_ARGS_MAX]
Argument encoding. See DET_ARG_REGS and DET_ARG_ON_STACK.
Definition: detours.h:111
Public data which allows for external modification to a in-guest hook handler.
Definition: detours.h:260
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:286
#define for_each_detour(_var_name)
Iterates the linked list in gDetours.
Definition: detours.c:52
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:497
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:392
HYPERCALL_TYPE HypercallType
The type of the hypercall that this detour uses.
Definition: detours.h:462
#define IN_RANGE_LEN(x, start, len)
Definition: introdefs.h:175
Must always be the last one.
Definition: detours.h:174
struct _DETOURS_STATE DETOURS_STATE
Holds information about the currently set detours.
#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:804
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 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:190
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:279
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:1762
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
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:1836
QWORD FunctionAddress
The guest virtual address of the hooked function.
Definition: detours.h:447
#define WARNING(fmt,...)
Definition: glue.h:60
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:53
static void IntDetRemoveHandler(DETOUR *Detour)
Removes a detour handler from the guest.
Definition: detours.c:236
QWORD IntDetRelocatePtrIfNeeded(QWORD Ptr)
Returns the new value Ptr should have if it is currently pointing inside a relocated prologue...
Definition: detours.c:1942
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
INTSTATUS IntSlackFree(QWORD Buffer)
Free slack space.
Definition: slack.c:429
DWORD HandlerSize
The size of the detour handler.
Definition: detours.h:456
#define DETOUR_MAX_FUNCTION_SIZE
The maximum size of the original function code we will replace.
Definition: detours.c:22
uint32_t DWORD
Definition: intro_types.h:49
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:838
PDETOUR LixDetourTable[det_max_id]
Table of detours, indexed by DETOUR_TAG (linux).
Definition: detours.c:33
LIST_ENTRY Link
The link inside the DETOURS_STATE.DetoursList list.
Definition: detours.h:435
#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:466
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:1968
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:462
#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:370
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
THS_PTR_TYPE
The type of pointer to be checked.
struct _LINUX_GUEST::@125::@128 Detour
LIST_HEAD DetoursList
List of detours. Each entry is a DETOUR structure.
Definition: detours.c:29
QWORD ModuleBase
The guest virtual address of the base of the kernel module that owns the hooked function.
Definition: detours.h:487
BYTE PublicDataOffset
The offset at which the data is available inside the detour handler.
Definition: detours.h:265
#define DETOUR_INVALID_HYPERCALL
Used to specify that no hypercall is present in the detour handler so the HypercallOffset field insid...
Definition: detours.h:317
BYTE RelocatedCodeLength
The size of the relocated code.
Definition: detours.h:473
void * HandlerCloakHandle
The memory cloak handle used to hide the detour handler. See Memory cloaking.
Definition: detours.h:492
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
static void IntDetRemoveDetour(DETOUR *Detour)
Removes and frees a detour.
Definition: detours.c:269
const LIX_FN_DETOUR * LixFnDetour
Definition: detours.h:498
#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:294
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:837
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:57
PFUNC_LixDetourCallback Callback
Callback to be invoked when the detour issues a hypercall.
Definition: detours.h:418
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:642
BYTE PublicDataSize
The size of the data.
Definition: detours.h:267
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:385
INTSTATUS IntDetGetArguments(void const *Detour, DWORD Argc, QWORD *Argv)
Reads multiple arguments from a detour.
Definition: detours.c:2164
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:460
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:814
#define DET_ARG_ON_STACK(Arg)
Checks if the argument should be taken from the guest stack.
Definition: detours.h:57
BOOLEAN Disabled
True if this detour has been disabled.
Definition: detours.h:485
char CHAR
Definition: intro_types.h:56
void IntDetDumpDetours(void)
Prints all the detours in the gDetours list of detours.
Definition: detours.c:1789
unsigned long long JumpBack
Definition: handlers.h:100
#define PAGE_MASK
Definition: pgtable.h:35
struct _LINUX_GUEST::@125 MmAlloc
#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:985
BYTE RelocatedCodeOffset
Offset, relative to HandlerAddress, where the prologue that has been replaced by our jump at the begi...
Definition: detours.h:471
#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:325
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:270
The detour will use a VMCALL instruction in order to notify introcore about an event.
Definition: detours.h:187
LINUX_GUEST * gLixGuest
Global variable holding the state of a Linux guest.
Definition: lixguest.c:29
#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:1874
DETOUR_TAG Tag
Detour tag.
Definition: detours.h:440
#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:450
#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:412
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68