Bitdefender Hypervisor Memory Introspection
winagent.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winagent.h"
6 #include "alerts.h"
7 #include "guests.h"
8 #include "hnd_loggather.h"
9 #include "hnd_remediation.h"
10 #include "icache.h"
11 #include "kernvm.h"
12 #include "loader.h"
13 #include "memcloak.h"
14 #include "ptfilter.h"
15 #include "slack.h"
16 #include "vecore.h"
17 #include "winbootdrv_Win32.h"
18 #include "winbootdrv_x64.h"
19 #include "winpe.h"
20 #include "winprocesshp.h"
21 #include "winstubs.h"
22 
23 
135 
136 // We obviously have different drivers for x86 and x64...
137 #define REM_DRV(arch64) ((arch64) ? (gBootDriverx64) : (gBootDriverWin32))
138 #define REM_SIZE(arch64) ((arch64) ? (sizeof(gBootDriverx64)) : sizeof((gBootDriverWin32)))
139 
140 #define AGENT_FLAG_STARTED 0x00000001
141 #define AGENT_FLAG_ALLOCATED 0x00000002
142 #define AGENT_FLAG_ACTIVE 0x00000004
143 #define AGENT_FLAG_COMPLETED 0x00000008
144 #define AGENT_FLAG_ALL_DONE 0x0000000F
145 
146 
153 typedef struct _AGENT_NAME
154 {
162 
164 BYTE gTrampolineZero[4096] = {0};
165 
166 
170 typedef struct _AGENT_STATE
171 {
176 
180 
182 
185  void *ActiveAgent;
191 
193 
194 
195 
196 BOOLEAN
198  _In_ QWORD Rip
199  )
207 {
208  PWIN_AGENT currentAgent = gAgentState.ActiveAgent;
209 
210  if (currentAgent)
211  {
212  if (Rip >= currentAgent->DriverAddress && Rip < currentAgent->DriverAddress + currentAgent->DriverSize)
213  {
214  return TRUE;
215  }
216  }
217 
218  return FALSE;
219 }
220 
221 
222 //
223 // Execution callback in the SYSCALL page - used to redirect RIP to our bootstrap code.
224 //
225 INTSTATUS
227  _In_ DWORD Size,
228  _Out_ QWORD *Address
229  );
230 
231 INTSTATUS
233  _In_ QWORD Address
234  );
235 
236 
237 static INTSTATUS
239  _In_ PWIN_AGENT Agent,
240  _In_ QWORD DataInfo
241  )
255 {
256  UNREFERENCED_PARAMETER(DataInfo);
257 
258  if ((NULL != Agent->AgentContent) && !Agent->AgentInternal)
259  {
260  IntReleaseBuffer(Agent->AgentContent, Agent->AgentSize);
261 
262  Agent->AgentContent = NULL;
263  }
264 
266 
267  return INT_STATUS_SUCCESS;
268 }
269 
270 
271 static INTSTATUS
273  _In_ PWIN_AGENT Agent
274  )
284 {
285  INTSTATUS status;
286 
287  // Pause the VCPUs while we do the cleanup.
288  IntPauseVcpus();
289 
290  // If needed, restore the patched instruction.
291  if (!Agent->InstructionRestored)
292  {
293  if (NULL != Agent->InsCloakRegion)
294  {
295  status = IntMemClkUncloakRegion(Agent->InsCloakRegion, MEMCLOAK_OPT_APPLY_PATCH);
296  if (!INT_SUCCESS(status))
297  {
298  ERROR("[ERROR] IntMemClkUncloakRegion failed: 0x%08x\n", status);
299  }
300 
301  Agent->InsCloakRegion = NULL;
302  }
303  else
304  {
305  if (!!(Agent->Options & AG_OPT_INJECT_ON_RIP_POWSTATE_CHANGE))
306  {
307  BYTE fiveByteNop[] = {0x66, 0x66, 0x66, 0x66, 0x90};
308 
309  status = IntDetModifyPublicData(detTagPowerState, fiveByteNop, sizeof(fiveByteNop), "5bytenop");
310  if (!INT_SUCCESS(status))
311  {
312  ERROR("[ERROR] IntDetModifyPublicData failed: 0x%08x\n", status);
313  }
314  }
315  else
316  {
317  ERROR("[ERROR] Instruction was not restored and the cloak region is NULL!\n");
319  }
320  }
321 
322  Agent->InstructionRestored = TRUE;
323  }
324 
325 
326  // If needed, remove the bootstrap code.
327  if (0 != Agent->BootstrapAddress)
328  {
329  // Remove the cloaked boot region.
330  if (NULL != Agent->BootCloakRegion)
331  {
332  status = IntMemClkUncloakRegion(Agent->BootCloakRegion, MEMCLOAK_OPT_APPLY_PATCH);
333  if (!INT_SUCCESS(status))
334  {
335  ERROR("[ERROR] IntMemClkUncloakRegion failed: 0x%08x\n", status);
336  }
337 
338  Agent->BootCloakRegion = NULL;
339  }
340 
341  status = IntWinAgentReleaseBootstrapAddress(Agent->BootstrapAddress);
342  if (!INT_SUCCESS(status))
343  {
344  ERROR("[ERROR] IntWinAgentReleaseBootstrapAddress failed: 0x%08x\n", status);
345  }
346 
347  Agent->BootstrapAddress = 0;
348  }
349 
350  IntResumeVcpus();
351 
352  // Free the agent.
353  status = IntWinAgentFree(Agent, 0);
354  if (!INT_SUCCESS(status))
355  {
356  ERROR("[ERROR] IntWinAgentFree failed: 0x%08x\n", status);
357  }
358 
359  return status;
360 }
361 
362 
363 INTSTATUS
365  void
366  )
376 {
377  INTSTATUS status;
378  DWORD slackSize;
380  BYTE origTrampoline[TRAMPOLINE_MAX_SIZE] = {0};
381 
382  if (gGuest.Guest64)
383  {
384  slackSize = sizeof(gTrampolineAgentx64);
385  trampoline = gTrampolineAgentx64;
386 
387  gAgentState.OffsetStop = TRAMP_X64_STOP;
388  gAgentState.OffsetVmcall1 = TRAMP_X64_VMCALL1;
389  gAgentState.OffsetVmcall2 = TRAMP_X64_VMCALL2;
390  }
391  else
392  {
393  slackSize = sizeof(gTrampolineAgentx86);
394  trampoline = gTrampolineAgentx86;
395 
396  gAgentState.OffsetStop = TRAMP_X86_STOP;
397  gAgentState.OffsetVmcall1 = TRAMP_X86_VMCALL1;
398  gAgentState.OffsetVmcall2 = TRAMP_X86_VMCALL2;
399  }
400 
401  status = IntSlackAlloc(gGuest.KernelVa, FALSE, slackSize, &gAgentState.Trampoline, 0);
402  if (!INT_SUCCESS(status))
403  {
404  ERROR("[ERROR] IntSlackAlloc failed: 0x%08x\n", status);
405  return status;
406  }
407 
408  status = IntKernVirtMemRead(gAgentState.Trampoline, slackSize, origTrampoline, NULL);
409  if (!INT_SUCCESS(status))
410  {
411  ERROR("[ERROR] IntKernVirtMemRead failed for %llx, %d: 0x%08x\n", gAgentState.Trampoline, slackSize, status);
412 
413  // Remove the slack allocation.
414  IntSlackFree(gAgentState.Trampoline);
415 
416  gAgentState.Trampoline = 0;
417 
418  return status;
419  }
420 
421  gAgentState.TrampolineSize = slackSize;
422 
423  // Inject the trampoline and cloak it.
424  status = IntMemClkCloakRegion(gAgentState.Trampoline,
425  0,
426  slackSize,
428  origTrampoline,
429  trampoline,
430  NULL,
431  &gAgentState.TrampolineCloak);
432  if (!INT_SUCCESS(status))
433  {
434  ERROR("[ERROR] IntMemClkCloakRegion failed: 0x%08x\n", status);
435 
436  // Remove the slack allocation.
437  IntSlackFree(gAgentState.Trampoline);
438 
439  gAgentState.Trampoline = 0;
440 
441  return status;
442  }
443 
444  TRACE("[AGENT] Selected trampoline at 0x%016llx\n", gAgentState.Trampoline);
445 
446  return INT_STATUS_SUCCESS;
447 }
448 
449 
450 static INTSTATUS
452  _In_ PWIN_AGENT Agent
453  )
468 {
469  INTSTATUS status;
470  QWORD cr3;
471  DWORD imageSize, imageEp, ring;
472  PBYTE pImage;
473  BOOLEAN is64;
474 
475  imageSize = imageEp = ring = 0;
476  pImage = NULL;
477  cr3 = 0;
478 
480  {
482  }
483 
484  status = IntCr3Read(IG_CURRENT_VCPU, &cr3);
485  if (!INT_SUCCESS(status))
486  {
487  ERROR("[ERROR] IntCr3Read failed: 0x%08x\n", status);
488  return status;
489  }
490 
491  status = IntGetCurrentRing(IG_CURRENT_VCPU, &ring);
492  if (!INT_SUCCESS(status))
493  {
494  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
495  return status;
496  }
497 
498  TRACE("[AGENT] Initiating boot driver deployment...\n");
499 
500  is64 = gGuest.Guest64;
501 
502  status = IntLdrGetImageSizeAndEntryPoint(REM_DRV(is64), REM_SIZE(is64), &imageSize, &imageEp);
503  if (!INT_SUCCESS(status))
504  {
505  ERROR("[ERROR] IntLdrGetImageSizeAndEntryPoint failed: 0x%08x\n", status);
506  goto cleanup_and_exit;
507  }
508 
509  if (imageEp == 0)
510  {
511  ERROR("[ERROR] No entry point found!\n");
512  status = INT_STATUS_NOT_FOUND;
513  goto cleanup_and_exit;
514  }
515 
516  pImage = HpAllocWithTag(imageSize, IC_TAG_IMGE);
517  if (NULL == pImage)
518  {
520  goto cleanup_and_exit;
521  }
522 
523  TRACE("[AGENT] Loading boot driver image...\n");
524 
525  // NOTE: We will fix the imports inside the driver. The thing is, the EAT of certain modules may be swapped out;
526  // Injecting page faults in order to access it is very possible & doable, but it would induce a very high
527  // performance penalty; it is preferred, therefore, to fix the imports directly inside the driver.
528  status = IntLdrLoadPEImage(REM_DRV(is64),
529  REM_SIZE(is64),
530  Agent->DriverAddress,
531  pImage,
532  imageSize,
534  if (!INT_SUCCESS(status))
535  {
536  ERROR("[ERROR] IntLdrLoadPEImage failed: 0x%08x\n", status);
537  goto cleanup_and_exit;
538  }
539 
540  TRACE("[AGENT] Deploying boot driver image...\n");
541 
542  IntPauseVcpus();
543 
544  status = IntVirtMemSafeWrite(cr3, Agent->DriverAddress, imageSize, pImage, ring);
545  if (!INT_SUCCESS(status))
546  {
547  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
548  goto resume_and_exit;
549  }
550 
551 resume_and_exit:
552  IntResumeVcpus();
553 
554  // Preserved status from IntValidateRangeForWrite/IntKernVirtMemWrite.
555  if (!INT_SUCCESS(status))
556  {
557  goto cleanup_and_exit;
558  }
559 
560  TRACE("[AGENT] Boot driver deployment successful!\n");
561 
562  // over-write the status
563  status = INT_STATUS_SUCCESS;
564 
565 cleanup_and_exit:
566  if (NULL != pImage)
567  {
569  }
570 
571  return status;
572 }
573 
574 
575 static INTSTATUS
577  _Out_ QWORD *PropperSyscall
578  )
590 {
591  INTSTATUS status;
592  DWORD rva;
594 
595  // KiSystemServiceUser
596  WIN_UNEXPORTED_FUNCTION_PATTERN functionPatternx64 =
597  {
598  .SectionHint = {0},
599  .Signature = {
600  .Length = 33,
601  .SignatureId = 0,
602  .Offset = 0,
603  .Pattern = {
604  0xc6, 0x45, 0x100, 0x02, // mov byte ptr [rbp-55h],2
605  0x65, 0x48, 0x8b, 0x1c, 0x25, 0x88, 0x01, 0x00, 0x00, // mov rbx,qword ptr gs:[188h]
606  0x0f, 0x0d, 0x8b, 0x100, 0x100, 0x00, 0x00, // prefetchw [rbx+90h]
607  0x0f, 0xae, 0x5d, 0x100, // stmxcsr dword ptr [rbp-54h]
608  0x65, 0x0f, 0xae, 0x14, 0x25, 0x80, 0x01, 0x00, 0x00, // ldmxcsr dword ptr gs:[180h]
609  }
610  }
611  };
612 
613  // KiFastCallEntryCommon
614  WIN_UNEXPORTED_FUNCTION_PATTERN functionPatternx86 =
615  {
616  .SectionHint = {0},
617  .Signature = {
618  .Length = 34,
619  .SignatureId = 0,
620  .Offset = 0,
621  .Pattern = {
622  0x6a, 0x02, // push 2
623  0x83, 0xc2, 0x08, // add edx,8
624  0x9d, // popfd
625  0x80, 0x4c, 0x24, 0x01, 0x02, // or byte ptr [esp+1],2
626  0x6a, 0x1b, // push 1Bh
627  0xff, 0x35, 0x100, 0x100, 0x100, 0x100, // push dword ptr [nt!KeI386FastSystemCallReturn
628  // (81ebdfdc)]
629  0x6a, 0x00, // push 0
630  0x55, // push ebp
631  0x53, // push ebx
632  0x56, // push esi
633  0x57, // push edi
634  0x64, 0x8b, 0x1d, 0x1c, 0x00, 0x00, 0x00, // mov ebx,dword ptr fs:[1Ch]
635  0x6a, 0x3b, // push 3Bh
636  }
637  }
638  };
639 
640  // Already found the KiSystemServiceUser
641  if (0 != gWinGuest->PropperSyscallGva)
642  {
643  *PropperSyscall = gWinGuest->PropperSyscallGva;
644  return INT_STATUS_SUCCESS;
645  }
646 
647  if (gGuest.Guest64)
648  {
649  pPattern = &functionPatternx64;
650  }
651  else
652  {
653  pPattern = &functionPatternx86;
654  }
655 
656  // Find KiSystemServiceUser/KiFastCallEntryCommon. Since this is called only once and it is possible that we don't
657  // have the KernelBuffer saved yet (there might be BP agents injected at init in order to swap the kernel),
658  // we won't use the buffer in this case.
659  status = IntPeFindFunctionByPattern(gGuest.KernelVa, pPattern, TRUE, &rva);
660  if (!INT_SUCCESS(status))
661  {
662  ERROR("[ERROR] IntPeFindFunctionByPattern failed: 0x%08x\n", status);
663  return status;
664  }
665 
667 
668  *PropperSyscall = gWinGuest->PropperSyscallGva;
669 
670  return INT_STATUS_SUCCESS;
671 }
672 
673 
674 static INTSTATUS
676  _In_ DWORD SyscallNumber,
677  _Out_ QWORD *LinkageAddress
678  )
689 {
690  static WIN_UNEXPORTED_FUNCTION_PATTERN linkage64 =
691  {
692  .SectionHint = { 0 },
693  .Signature =
694  {
695  .Length = 30,
696  .SignatureId = 0,
697  .Offset = 0,
698  .Pattern =
699  {
700  0x48, 0x8b, 0xc4, // mov rax,rsp
701  0xfa, // cli
702  0x48, 0x83, 0xec, 0x10, // sub rsp,10h
703  0x50, // push rax
704  0x9c, // pushfq
705  0x6a, 0x10, // push 10h
706  0x48, 0x8d, 0x100, 0x100, 0x100, 0x100, 0x100, // lea rax,[nt!KiServiceLinkage (fffff800`026fbb50)]
707  0x50, // push rax
708  0xb8, 0x81, 0x01, 0x00, 0x00, // mov eax,181h
709  0xe9, 0x100, 0x100, 0x100, 0x100, // jmp nt!KiServiceInternal (fffff800`02707e00)
710 
711  }
712  }
713  };
714 
715  static WIN_UNEXPORTED_FUNCTION_PATTERN linkage32 =
716  {
717  .SectionHint = { 0 },
718  .Signature =
719  {
720  .Length = 20,
721  .SignatureId = 0,
722  .Offset = 0,
723  .Pattern =
724  {
725  0xb8, 0x1e, 0x00, 0x00, 0x00, // mov eax,1Eh
726  0x8d, 0x54, 0x24, 0x04, // lea edx,[esp+4]
727  0x9c, // pushfd
728  0x6a, 0x08, // push 8
729  0xe8, 0x100, 0x100, 0x100, 0x100, // call nt!KiSystemService (8197d85a)
730  0xc2, 0x100, 0x00, // ret 18h
731 
732  }
733  }
734  };
735 
736  WIN_UNEXPORTED_FUNCTION_PATTERN *const linkage = gGuest.Guest64 ? &linkage64 : &linkage32;
737  const DWORD sysNoOffset = gGuest.Guest64 ? 21 : 1;
738  DWORD rva;
739  INTSTATUS status;
740 
741  // Place the syscall number inside the pattern
742  for (size_t i = 0; i < sizeof(SyscallNumber); i++)
743  {
744  linkage->Signature.Pattern[i + sysNoOffset] = ((BYTE *)&SyscallNumber)[i];
745  }
746 
747  if (NULL != gWinGuest->KernelBuffer && 0 == gWinGuest->RemainingSections)
748  {
750  linkage, TRUE, &rva);
751  }
752  else
753  {
754  status = IntPeFindFunctionByPattern(gGuest.KernelVa, linkage, TRUE, &rva);
755  }
756 
757  if (!INT_SUCCESS(status))
758  {
759  ERROR("[ERROR] Failed to find syscall 0x%x kernel linkage: 0x%08x\n", SyscallNumber, status);
760  return status;
761  }
762 
763  TRACE("[INFO] Found syscall 0x%x linkage @ 0x%016llx\n", SyscallNumber, gGuest.KernelVa + rva);
764 
765  *LinkageAddress = gGuest.KernelVa + rva;
766 
767  return INT_STATUS_SUCCESS;
768 }
769 
770 
771 static INTSTATUS
773  _In_ BYTE MinLen,
774  _Out_ QWORD *InstructionVa,
775  _Out_ BYTE *InstructionLen,
776  _Out_writes_bytes_(ND_MAX_INSTRUCTION_LENGTH) BYTE *InstructionBytes
777  )
795 {
796  INTSTATUS status;
797  PBYTE pSyscallCode;
798  QWORD syscallGva;
799  size_t parsed;
800  BYTE cdef, ddef, stiCount, neededStiCount;
801  BOOLEAN bFound, bStiFound;
802  INSTRUX instrux;
803 
805 
806  if (NULL == InstructionVa)
807  {
809  }
810 
811  pSyscallCode = NULL;
812  parsed = 0;
813  bFound = bStiFound = FALSE;
814  stiCount = 0;
815  syscallGva = 0;
816 
817  pSyscallCode = HpAllocWithTag(PAGE_SIZE, IC_TAG_ALLOC);
818  if (NULL == pSyscallCode)
819  {
821  goto cleanup_and_exit;
822  }
823 
824  syscallGva = gWinGuest->SyscallAddress;
825 
826  if (gGuest.OSVersion <= 9200)
827  {
828  // Windows 7 and Windows 8 only have two STI instructions in the syscall code.
829  neededStiCount = 2;
830  }
831  else
832  {
833  // Windows 8.1 and Windows 10 have 3 STI instructions in the syscall code.
834  neededStiCount = 3;
835  }
836 
837  status = IntWinAgentFindPropperSyscall(&syscallGva);
838  if (!INT_SUCCESS(status))
839  {
840  ERROR("[ERROR] IntAgentFindKiSystemServiceUser failed: 0x%08x\n", status);
841  goto cleanup_and_exit;
842  }
843 
844  TRACE("[AGENT] Discovered the KiSystemServiceUser at 0x%016llx\n", syscallGva);
845 
846  // Read the SYSCALL/SYSENTRY code.
847  status = IntKernVirtMemRead(syscallGva, PAGE_SIZE, pSyscallCode, NULL);
848  if (!INT_SUCCESS(status))
849  {
850  ERROR("[ERROR] IntKernVirtMemRead failed for syscall 0x%016llx: 0x%08x\n", syscallGva, status);
851  goto cleanup_and_exit;
852  }
853 
854  // Parse the read code in order to find the first "STI" and then the first instruction after the "STI" that's
855  // at least 5 bytes long.
856  if (gGuest.Guest64)
857  {
858  cdef = ND_CODE_64;
859  ddef = ND_DATA_64;
860  }
861  else
862  {
863  cdef = ND_CODE_32;
864  ddef = ND_DATA_32;
865  }
866 
867  while (parsed + 16 < PAGE_SIZE)
868  {
869  NDSTATUS ndstatus;
870 
871  ndstatus = NdDecodeEx(&instrux, pSyscallCode + parsed, 0x1000 - parsed, cdef, ddef);
872  if (!ND_SUCCESS(ndstatus))
873  {
874  ERROR("[ERROR] NdDecodeEx failed at 0x%016llx: 0x%08x\n", syscallGva + parsed, ndstatus);
875  status = INT_STATUS_DISASM_ERROR;
876  break;
877  }
878 
879  if (!bStiFound)
880  {
881  if (ND_INS_STI == instrux.Instruction)
882  {
883  stiCount++;
884 
885  if ((stiCount == neededStiCount) || !gGuest.Guest64)
886  {
887  bStiFound = TRUE;
888  }
889  }
890  }
891  else if ((instrux.Length >= MinLen) && !ND_HAS_PREDICATE(&instrux)) // Avoid conditional instructions.
892  {
893  bFound = TRUE;
894  *InstructionVa = syscallGva + parsed;
895  *InstructionLen = instrux.Length;
896  memcpy(InstructionBytes, instrux.InstructionBytes, ND_MAX_INSTRUCTION_LENGTH);
897  break;
898  }
899 
900  parsed += instrux.Length;
901  }
902 
903  if (!bFound)
904  {
905  status = INT_STATUS_NOT_FOUND;
906  goto cleanup_and_exit;
907  }
908 
909 cleanup_and_exit:
910  if (NULL != pSyscallCode)
911  {
912  HpFreeAndNullWithTag(&pSyscallCode, IC_TAG_ALLOC);
913  }
914 
915  return status;
916 }
917 
918 
919 INTSTATUS
921  void
922  )
936 {
937  INTSTATUS status;
938  LIST_ENTRY *list;
939  PWIN_AGENT pAgent;
940  BYTE newCode[ND_MAX_INSTRUCTION_LENGTH] = {0};
941  PBYTE originalBootstrap;
942 
943  pAgent = NULL;
944  originalBootstrap = NULL;
945 
946  // If there are no pending agents or there are other agents bootstrapping, leave. We will inject our
947  // agent only after the other ones are started.
948  if ((0 == gAgentState.PendingAgentsCount) || (0 != gAgentState.BootstrapAgentsCount) ||
949  (0 != gAgentState.CompletingAgentsCount))
950  {
951  status = INT_STATUS_SUCCESS;
952  goto cleanup_and_exit;
953  }
954 
955  // Search a suitable pending agent.
956  list = gAgentState.PendingAgents.Flink;
957  while (list != &gAgentState.PendingAgents)
958  {
960 
961  list = list->Flink;
962 
963  if ((pAg->AgentType == AGENT_TYPE_FILE) ||
964  (pAg->AgentType == AGENT_TYPE_BINARY) ||
965  (pAg->AgentType == AGENT_TYPE_DRIVER) ||
966  (pAg->AgentType == AGENT_TYPE_BREAKPOINT) ||
967  (pAg->AgentType == AGENT_TYPE_VE_LOADER) ||
968  (pAg->AgentType == AGENT_TYPE_VE_UNLOADER) ||
969  (pAg->AgentType == AGENT_TYPE_PT_LOADER) ||
970  (pAg->AgentType == AGENT_TYPE_PT_UNLOADER) ||
971  ((pAg->AgentType == AGENT_TYPE_PROCESS) && (gAgentState.SafeToInjectProcess)))
972  {
973  pAgent = pAg;
974 
975  break;
976  }
977  }
978 
979  if (NULL == pAgent)
980  {
981  status = INT_STATUS_SUCCESS;
982  goto cleanup_and_exit;
983  }
984 
985  TRACE("[AGENT] Activating pending agent, %d, remaining %d\n",
986  gAgentState.SafeToInjectProcess, gAgentState.PendingAgentsCount - 1);
987 
988  // 1. Remove the agent from the pending-agents list and mark it as being the active agent.
989  RemoveEntryList(&pAgent->Link);
990 
991  gAgentState.PendingAgentsCount--;
992 
993  gAgentState.BootstrapAgentsCount++;
994 
995  gAgentState.CompletingAgentsCount++;
996 
997  // Mark the current active agent.
998  gAgentState.ActiveAgent = pAgent;
999 
1000 
1001  // Pause the VCPUs while we modify the guest memory.
1002  IntPauseVcpus();
1003 
1004  // Handle special breakpoint agent injection.
1005  if (pAgent->AgentType == AGENT_TYPE_BREAKPOINT)
1006  {
1007  goto skip_bsp_injection;
1008  }
1009 
1010  // 2. Resolve the jump-back address of this bootstrap code. We will jump inside the trampoline, to the termination
1011  // code, and that code will inform us that the agent finished execution and can be removed.
1012  if (gGuest.Guest64)
1013  {
1014  *((PQWORD)(pAgent->BootStrap + pAgent->OffsetJumpBack)) = gAgentState.Trampoline + gAgentState.OffsetStop;
1015  }
1016  else
1017  {
1018  *((PDWORD)(pAgent->BootStrap + pAgent->OffsetJumpBack)) = (DWORD)(gAgentState.Trampoline +
1019  gAgentState.OffsetStop);
1020  }
1021 
1022  TRACE("[AGENT] Agent jumpback address is 0x%016llx\n", gAgentState.Trampoline + gAgentState.OffsetStop);
1023 
1024  // 3. Get us a bootstrap address for the loader. That's where the bootstrap code will be injected.
1026  if (!INT_SUCCESS(status))
1027  {
1028  ERROR("[ERROR] IntAgentSelectBootstrapAddress failed: 0x%08x\n", status);
1029  goto unpause_and_exit;
1030  }
1031 
1032  TRACE("[AGENT] Selected bootstrap address at 0x%016llx\n", pAgent->BootstrapAddress);
1033 
1034  // Patch the relocation offset.
1035  if (!gGuest.Guest64)
1036  {
1037  *((DWORD *)&pAgent->BootStrap[OFFSET_WIN_X86_RELOC]) = (DWORD)pAgent->BootstrapAddress;
1038  }
1039 
1040  // 4. Deploy the loader inside the previously obtained bootstrap zone and cloak the region it will reside in.
1041  originalBootstrap = HpAllocWithTag(pAgent->BootstrapSize, IC_TAG_ALLOC);
1042  if (NULL == originalBootstrap)
1043  {
1045  goto unpause_and_exit;
1046  }
1047 
1048  status = IntKernVirtMemRead(pAgent->BootstrapAddress, pAgent->BootstrapSize, originalBootstrap, NULL);
1049  if (!INT_SUCCESS(status))
1050  {
1051  ERROR("[ERROR] IntKernVirtMemRead failed for size %d on address %llx: 0x%08x\n",
1052  pAgent->BootstrapSize, pAgent->BootstrapAddress, status);
1053  goto unpause_and_exit;
1054  }
1055 
1056  status = IntMemClkCloakRegion(pAgent->BootstrapAddress,
1057  0,
1058  pAgent->BootstrapSize,
1060  originalBootstrap,
1061  pAgent->BootStrap,
1062  NULL,
1063  &pAgent->BootCloakRegion);
1064  if (!INT_SUCCESS(status))
1065  {
1066  ERROR("[ERROR] IntMemClkCloakRegion failed: 0x%08x\n", status);
1067  goto unpause_and_exit;
1068  }
1069 
1070 skip_bsp_injection:
1071  // 5. Find a suitable instruction to overwrite. We parse the SYSCALL/SYSENTER code, and search for a "STI"
1072  // instruction. Once we find the "STI", we will seek an instruction that's at least 5 bytes in size, which
1073  // will host the CALL towards the trampoline.
1074 
1076  {
1077  // We know for sure if the AG_FLAG_SET_INSTRUCTION_ON_RIP is given we are on the NtSetSystemPowerState
1078  // hook handler. So, after Rip (which is an int3) we'll have a 5 bytes NOP instruction which we'll replace
1079  // with a call to our agent trampoline. In the future we can search for an instruction with length >=5 starting
1080  // from the current rip but for now I can't see a use case regarding this.
1081  pAgent->InstructionAddress = gVcpu->Regs.Rip + 1;
1082  pAgent->InstructionLen = 5;
1083 
1084  status = IntKernVirtMemRead(pAgent->InstructionAddress, pAgent->InstructionLen, pAgent->InstructionBytes, NULL);
1085  if (!INT_SUCCESS(status))
1086  {
1087  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1088  goto unpause_and_exit;
1089  }
1090  }
1091  else
1092  {
1093  status = IntWinAgentFindInstruction(pAgent->AgentType == AGENT_TYPE_BREAKPOINT ? 1 : 5,
1094  &pAgent->InstructionAddress,
1095  &pAgent->InstructionLen,
1096  pAgent->InstructionBytes);
1097  if (!INT_SUCCESS(status))
1098  {
1099  ERROR("[ERROR] IntAgentFindInstruction failed: 0x%08x\n", status);
1100  goto unpause_and_exit;
1101  }
1102  }
1103 
1104  TRACE("[AGENT] Found suitable instruction of len %d at 0x%016llx\n", pAgent->InstructionLen,
1105  pAgent->InstructionAddress);
1106 
1107  // Initialize the new code. Fill it out with NOPs, and then add a CALL towards our handler or an INT3.
1108  memset(newCode, 0x90, sizeof(newCode));
1109 
1110  if (pAgent->AgentType == AGENT_TYPE_BREAKPOINT)
1111  {
1112  newCode[0] = 0xCC;
1113  }
1114  else
1115  {
1116  newCode[0] = 0xE8;
1117  *(DWORD *)(newCode + 1) = (DWORD)(gAgentState.Trampoline - pAgent->InstructionAddress - 5);
1118  }
1119 
1120  pAgent->InstructionRestored = FALSE;
1121 
1122  // 7. Patch the instruction with a CALL to the VMCALL code and cloak the region for that instruction.
1124  {
1125  status = IntDetModifyPublicData(detTagPowerState, newCode, pAgent->InstructionLen, "5bytenop");
1126  if (!INT_SUCCESS(status))
1127  {
1128  ERROR("[ERROR] IntDetModifyPublicData failed: 0x%08x\n", status);
1129  goto unpause_and_exit;
1130  }
1131  }
1132  else
1133  {
1134  status = IntMemClkCloakRegion(pAgent->InstructionAddress,
1135  0,
1136  pAgent->InstructionLen,
1138  pAgent->InstructionBytes,
1139  newCode,
1140  NULL,
1141  &pAgent->InsCloakRegion);
1142  if (!INT_SUCCESS(status))
1143  {
1144  ERROR("[ERROR] Failed cloaking region 0x%016llx: 0x%08x\n", pAgent->InstructionAddress, status);
1145  goto unpause_and_exit;
1146  }
1147  }
1148 
1150 
1151  // Done! Everything went fine!
1152  TRACE("[AGENT] Region successfully hooked!\n");
1153 
1154  status = INT_STATUS_SUCCESS;
1155 
1156 unpause_and_exit:
1157  IntResumeVcpus();
1158 
1159 cleanup_and_exit:
1160  if (NULL != originalBootstrap)
1161  {
1162  HpFreeAndNullWithTag(&originalBootstrap, IC_TAG_ALLOC);
1163  }
1164 
1165  if (!INT_SUCCESS(status))
1166  {
1167  // If we get here, the activation was not successful. Remove the agent.
1168  INTSTATUS status2 = IntWinAgentRemove(pAgent);
1169  if (!INT_SUCCESS(status2))
1170  {
1171  ERROR("[ERROR] IntAgentRemove failed: 0x%08x\n", status2);
1172  }
1173 
1174  gAgentState.BootstrapAgentsCount--;
1175 
1176  gAgentState.CompletingAgentsCount--;
1177 
1178  gAgentState.ActiveAgent = NULL;
1179  }
1180 
1181  return status;
1182 }
1183 
1184 
1185 static INTSTATUS
1187  _In_ PWIN_AGENT Agent,
1188  _In_ PIG_ARCH_REGS Registers
1189  )
1206 {
1207  INTSTATUS status;
1208  QWORD regs[16] = {0}, sema = 1;
1209 
1210  // Fetch the registers + return address (note that RSP is not saved, hence 15 + 1 QWORDs) from the guest stack.
1211  status = IntKernVirtMemRead(Registers->Rsp, sizeof(regs), regs, NULL);
1212  if (!INT_SUCCESS(status))
1213  {
1214  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1215  return status;
1216  }
1217 
1218  // 2. Restore the registers inside the GPRs state
1219  Registers->R15 = regs[0];
1220  Registers->R14 = regs[1];
1221  Registers->R13 = regs[2];
1222  Registers->R12 = regs[3];
1223  Registers->R11 = regs[4];
1224  Registers->R10 = regs[5];
1225  Registers->R9 = regs[6];
1226  Registers->R8 = regs[7];
1227  Registers->Rdi = regs[8];
1228  Registers->Rsi = regs[9];
1229  Registers->Rbp = regs[10];
1230  Registers->Rbx = regs[11];
1231  Registers->Rdx = regs[12];
1232  Registers->Rcx = regs[13];
1233  Registers->Rax = regs[14];
1234 
1235  // 4. Restore the RIP
1236  Registers->Rip = regs[15];
1237 
1238  // 3. Restore the stack
1239  Registers->Rsp += 16 * 8;
1240 
1241  // Patch the registers!
1242  status = IntSetGprs(IG_CURRENT_VCPU, Registers);
1243  if (!INT_SUCCESS(status))
1244  {
1245  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1246  return status;
1247  }
1248 
1249  // Patch the semaphore field, used to spin. This is fine, we don't have to use IntVirtMemSafeWrite because
1250  // BootstrapAddress is allocated by Introcore, and is located in a protected memory region.
1251  status = IntKernVirtMemWrite(Agent->BootstrapAddress + OFFSET_WIN_X64_SEMAPHORE, 4, &sema);
1252  if (!INT_SUCCESS(status))
1253  {
1254  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
1255  return status;
1256  }
1257 
1258  return status;
1259 }
1260 
1261 
1262 static INTSTATUS
1264  _In_ PWIN_AGENT Agent,
1265  _In_ PIG_ARCH_REGS Registers
1266  )
1283 {
1284  INTSTATUS status;
1285  DWORD regs[9] = {0}, sema = 1;
1286 
1287  // Fetch the registers + return address (8 + 1 DWORDs) from the guest stack.
1288  status = IntKernVirtMemRead(Registers->Rsp, sizeof(regs), regs, NULL);
1289  if (!INT_SUCCESS(status))
1290  {
1291  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
1292  return status;
1293  }
1294 
1295  // 2. Restore the registers inside the GPRs state
1296  Registers->Rdi = regs[0];
1297  Registers->Rsi = regs[1];
1298  Registers->Rbp = regs[2];
1299  Registers->Rbx = regs[4];
1300  Registers->Rdx = regs[5];
1301  Registers->Rcx = regs[6];
1302  Registers->Rax = regs[7];
1303 
1304  // 4. Restore the RIP
1305  Registers->Rip = regs[8];
1306 
1307  // 3. Restore the stack
1308  Registers->Rsp += 9 * 4;
1309 
1310  // Patch the registers!
1311  status = IntSetGprs(IG_CURRENT_VCPU, Registers);
1312  if (!INT_SUCCESS(status))
1313  {
1314  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1315  return status;
1316  }
1317 
1318  // Patch the semaphore field, used to spin. This is fine, we don't have to use IntVirtMemSafeWrite because
1319  // BootstrapAddress is allocated by Introcore, and is located in a protected memory region.
1320  status = IntKernVirtMemWrite(Agent->BootstrapAddress + OFFSET_WIN_X86_SEMAPHORE, 4, &sema);
1321  if (!INT_SUCCESS(status))
1322  {
1323  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
1324  return status;
1325  }
1326 
1327  return status;
1328 }
1329 
1330 
1331 static INTSTATUS
1333  _In_ PWIN_AGENT Agent,
1334  _In_ PIG_ARCH_REGS Registers
1335  )
1351 {
1352  INTSTATUS status;
1353 
1354  if (NULL == Agent)
1355  {
1357  }
1358 
1359  if (NULL == Registers)
1360  {
1362  }
1363 
1364  if (Registers->Rip != Agent->InstructionAddress)
1365  {
1366  ERROR("[ERROR] RIP mismatch: 0x%016llx vs. 0x%016llx!\n", Registers->Rip, Agent->InstructionAddress);
1367  }
1368 
1369  // First VMCALL inside the trampoline hit. We have to store in RAX the address of the bootstrap, we have
1370  // to restore the old, patched instruction, and we have to patch the return address for the call in order to
1371  // return to the actual "interrupted" instruction.
1372  TRACE("[AGENT] BREAKPOINT hit, restoring original code and calling callback...\n");
1373 
1374  // Now, if we're started, we can leave. Otherwise, proceed and restore the instruction + remove the cloaking.
1375  if (Agent->Flags & AGENT_FLAG_STARTED)
1376  {
1377  return INT_STATUS_SUCCESS;
1378  }
1379 
1380  // Remove the cloak region.
1381  status = IntMemClkUncloakRegion(Agent->InsCloakRegion, MEMCLOAK_OPT_APPLY_PATCH);
1382  if (!INT_SUCCESS(status))
1383  {
1384  ERROR("[ERROR] IntMemClkUncloakRegion failed: 0x%08x\n", status);
1385  IntBugCheck();
1386  }
1387 
1388  Agent->InsCloakRegion = NULL;
1389 
1390  Agent->InstructionRestored = TRUE;
1391 
1392  Agent->Flags |= AGENT_FLAG_STARTED;
1393 
1394  status = Agent->InjectionCallback(0, Agent->AgentTag, Agent->Context);
1395  if (!INT_SUCCESS(status))
1396  {
1397  ERROR("[ERROR] Agent->InjectCallback failed: 0x%08x\n", status);
1398  return status;
1399  }
1400 
1401  Agent->Flags = AGENT_FLAG_ALL_DONE;
1402 
1403  status = IntWinAgentRemove(Agent);
1404  if (!INT_SUCCESS(status))
1405  {
1406  ERROR("[ERROR] IntWinAgentRemove failed: 0x%08x\n", status);
1407  }
1408 
1409  // Decrement the number of active agents.
1410  gAgentState.CompletingAgentsCount--;
1411 
1412  gAgentState.BootstrapAgentsCount--;
1413 
1414  // Make the current active agent NULL.
1415  gAgentState.ActiveAgent = NULL;
1416 
1417  return INT_STATUS_NO_DETOUR_EMU;
1418 }
1419 
1420 
1421 static INTSTATUS
1423  _In_opt_ WIN_AGENT *Agent,
1424  _In_ IG_ARCH_REGS *Registers
1425  )
1439 {
1440  INTSTATUS status;
1441 
1442  if (NULL == Registers)
1443  {
1445  }
1446 
1447  if (Registers->Rip == gAgentState.Trampoline + gAgentState.OffsetVmcall1)
1448  {
1449  DWORD offset = 0;
1450  QWORD retAddr = 0;
1451 
1452  // First HYPERCALL inside the trampoline hit. We have to store in RAX the address of the bootstrap, we have
1453  // to restore the old, patched instruction, and we have to patch the return address for the call in order to
1454  // return to the actual "interrupted" instruction.
1455  // Note that this may be hit after the agent terminated - a thread may have been de-scheduled right before
1456  // the INT3, and it may have been re-scheduled after the agent has completed.
1457  offset = gGuest.WordSize;
1458 
1459  // Fetch the return address.
1460  status = IntKernVirtMemRead(Registers->Rsp + offset, gGuest.WordSize, &retAddr, NULL);
1461  if (!INT_SUCCESS(status))
1462  {
1463  ERROR("[ERROR] IntKernVirtMemRead failed for VA 0x%016llx (offset %x): 0x%08x\n",
1464  Registers->Rsp, offset, status);
1465  IntBugCheck();
1466  }
1467 
1468  retAddr -= 5;
1469 
1470  status = IntVirtMemSafeWrite(gGuest.Mm.SystemCr3, Registers->Rsp + offset,
1471  gGuest.WordSize, &retAddr, IG_CS_RING_0);
1472  if (!INT_SUCCESS(status))
1473  {
1474  ERROR("[ERROR] IntKernVirtMemWrite failed for VA 0x%016llx: 0x%08x\n", Registers->Rsp + offset, status);
1475  IntBugCheck();
1476  }
1477 
1478  Registers->Rax = (NULL == Agent) ? 0 : ((Agent->Flags & AGENT_FLAG_STARTED) ? 0 : Agent->BootstrapAddress);
1479 
1480  // Patch the registers.
1481  status = IntSetGprs(IG_CURRENT_VCPU, Registers);
1482  if (!INT_SUCCESS(status))
1483  {
1484  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
1485  IntBugCheck();
1486  }
1487 
1488  // Now, if we're started, we can leave. Otherwise, proceed and restore the instruction + remove the cloaking.
1489  // Also, if the agent is NULL, leave now - it may have completed by now.
1490  if ((NULL == Agent) || (Agent->Flags & AGENT_FLAG_STARTED))
1491  {
1492  return INT_STATUS_SUCCESS;
1493  }
1494 
1495  TRACE("[AGENT] HYPERCALL1 hit, storing the agent bootstrap: 0x%016llx\n", Agent->BootstrapAddress);
1496 
1497  // Remove the cloak region.
1498  if (!!(Agent->Options & AG_OPT_INJECT_ON_RIP_POWSTATE_CHANGE))
1499  {
1500  BYTE fiveByteNop[] = {0x66, 0x66, 0x66, 0x66, 0x90};
1501 
1502  status = IntDetModifyPublicData(detTagPowerState, fiveByteNop, sizeof(fiveByteNop), "5bytenop");
1503  if (!INT_SUCCESS(status))
1504  {
1505  ERROR("[ERROR] IntDetModifyPublicData failed: 0x%08x\n", status);
1506  IntBugCheck();
1507  }
1508  }
1509  else
1510  {
1511  status = IntMemClkUncloakRegion(Agent->InsCloakRegion, MEMCLOAK_OPT_APPLY_PATCH);
1512  if (!INT_SUCCESS(status))
1513  {
1514  ERROR("[ERROR] IntMemClkUncloakRegion failed: 0x%08x\n", status);
1515  IntBugCheck();
1516  }
1517  }
1518 
1519  Agent->InsCloakRegion = NULL;
1520 
1521  Agent->InstructionRestored = TRUE;
1522 
1523  Agent->Flags |= AGENT_FLAG_STARTED;
1524 
1525  return status;
1526  }
1527  else if (Registers->Rip == gAgentState.Trampoline + gAgentState.OffsetVmcall2)
1528  {
1529  if (NULL == Agent)
1530  {
1532  }
1533 
1534  // Second VMCALL hit, we can free the agent!
1535  TRACE("[AGENT] HYPERCALL2 hit, freeing the bootstrap at 0x%016llx\n", Agent->BootstrapAddress);
1536 
1537  if (AGENT_FLAG_ALL_DONE != (Agent->Flags & AGENT_FLAG_ALL_DONE))
1538  {
1539  ERROR("[ERROR] Agent termination requested, but the agent is not done yet = %x!\n", Agent->Flags);
1540  status = INT_STATUS_SUCCESS;
1541  }
1542  else
1543  {
1544  status = IntWinAgentRemove(Agent);
1545  if (!INT_SUCCESS(status))
1546  {
1547  ERROR("[ERROR] IntAgentRemove failed: 0x%08x\n", status);
1548  }
1549 
1550  // Decrement the number of active agents.
1551  gAgentState.CompletingAgentsCount--;
1552 
1553  // Make the current active agent NULL.
1554  gAgentState.ActiveAgent = NULL;
1555  }
1556 
1557  return status;
1558  }
1559 
1560  return INT_STATUS_NOT_FOUND;
1561 }
1562 
1563 
1564 static INTSTATUS
1566  _In_ WIN_AGENT *Agent,
1567  _In_ IG_ARCH_REGS *Registers
1568  )
1578 {
1579  INTSTATUS status = INT_STATUS_SUCCESS;
1580 
1581  if (gGuest.Guest64)
1582  {
1583  status = IntWinAgentRestoreState64(Agent, Registers);
1584  if (!INT_SUCCESS(status))
1585  {
1586  ERROR("[ERROR] IntWinAgentRestoreState64 failed: 0x%08x\n", status);
1587  IntBugCheck();
1588  }
1589  }
1590  else
1591  {
1592  status = IntWinAgentRestoreState32(Agent, Registers);
1593  if (!INT_SUCCESS(status))
1594  {
1595  ERROR("[ERROR] IntWinAgentRestoreState32 failed: 0x%08x\n", status);
1596  IntBugCheck();
1597  }
1598  }
1599 
1600  status = INT_STATUS_NO_DETOUR_EMU;
1601 
1602  Agent->Flags |= AGENT_FLAG_ACTIVE;
1603 
1604  // Update the bootstrap agents count. This one bootstrapped, we can decrement the counter.
1605  gAgentState.BootstrapAgentsCount--;
1606 
1607  return status;
1608 }
1609 
1610 
1611 static INTSTATUS
1613  _In_ WIN_AGENT *Agent
1614  )
1622 {
1623  INTSTATUS status;
1624 
1625  status = IntWinAgentRemove(Agent);
1626  if (!INT_SUCCESS(status))
1627  {
1628  ERROR("[ERROR] IntWinAgentRemove failed: 0x%08x\n", status);
1629  return status;
1630  }
1631 
1632  gAgentState.CompletingAgentsCount--;
1633 
1634  gAgentState.ActiveAgent = NULL;
1635 
1636  return INT_STATUS_SUCCESS;
1637 }
1638 
1639 
1640 static INTSTATUS
1642  _In_ WIN_AGENT *Agent,
1643  _In_ IG_ARCH_REGS *Registers
1644  )
1653 {
1654  INTSTATUS status;
1655 
1656  status = IntWinAgentReleaseBootstrap(Agent, Registers);
1657  if (!INT_SUCCESS(status))
1658  {
1659  ERROR("[ERROR] IntWinAgentReleaseBootstrap failed: 0x%08x\n", status);
1660  return status;
1661  }
1662 
1663  status = IntWinAgentRemoveAgentAndResetState(Agent);
1664  if (!INT_SUCCESS(status))
1665  {
1666  ERROR("[ERROR] IntWinAgentRemoveAgentAndResetState failed: 0x%08x\n", status);
1667  return status;
1668  }
1669 
1670  return INT_STATUS_SUCCESS;
1671 }
1672 
1673 
1674 static INTSTATUS
1676  _In_ PWIN_AGENT Agent,
1677  _In_ PIG_ARCH_REGS Registers
1678  )
1701 {
1702  INTSTATUS status;
1703  QWORD argument;
1704 
1705  if (NULL == Agent)
1706  {
1708  }
1709 
1710  if (NULL == Registers)
1711  {
1713  }
1714 
1715  status = INT_STATUS_SUCCESS;
1716 
1717  // Second stage loader.
1718 
1719  // HYPERCALL from the bootstrap stub. The argument is in RCX.
1720  argument = Registers->Rcx;
1721 
1722  // We got our agent!
1723  if (Registers->Rdx == Agent->Token1)
1724  {
1725  // Agent has just been allocated.
1726 
1727  TRACE("[AGENT] Bootstrap reports that the agent has been allocated at 0x%016llx\n", argument);
1728 
1729  if (argument == 0 && !INT_SUCCESS((INTSTATUS)Registers->Rsi))
1730  {
1731  ERROR("[ERROR] Bootstrap reports agent allocation failed with status: 0x%08x\n", (DWORD)Registers->Rsi);
1732 
1733  status = IntWinAgentReleaseBootstrapAndRemoveAgent(Agent, Registers);
1734  if (!INT_SUCCESS(status))
1735  {
1736  ERROR("[ERROR] IntWinAgentReleaseBootstrapAndRemoveAgent failed: 0x%08x\n", status);
1737  }
1738 
1739  return INT_STATUS_NO_DETOUR_EMU;
1740  }
1741 
1742  Agent->DriverAddress = argument;
1743 
1744  status = IntWinAgentDeployWinDriver(Agent);
1745  if (!INT_SUCCESS(status))
1746  {
1747  ERROR("[ERROR] IntWinAgentInjectWinDriver failed: 0x%08x\n", status);
1748  IntBugCheck();
1749  }
1750 
1751  // Invoke the injection callback.
1752  status = Agent->InjectionCallback(Agent->DriverAddress, Agent->AgentTag, Agent->Context);
1753  if (!INT_SUCCESS(status))
1754  {
1755  ERROR("[ERROR] Injection failed: 0x%08x\n", status);
1756  }
1757 
1758  Agent->Flags |= AGENT_FLAG_ALLOCATED;
1759  }
1760  else if (Registers->Rdx == Agent->Token2)
1761  {
1762  // Agent has just been started
1763  TRACE("[AGENT] Bootstrap reports that the agent has been started with result 0x%016llx\n", argument);
1764 
1765  status = IntWinAgentReleaseBootstrap(Agent, Registers);
1766  if (!INT_SUCCESS(status))
1767  {
1768  ERROR("[ERROR] IntWinAgentReleaseBootstrap failed: 0x%08x\n", status);
1769  return status;
1770  }
1771 
1772  if (!INT_SUCCESS(argument))
1773  {
1774  ERROR("[ERROR] Thread creation status: 0x%08x, will remove agent\n", (DWORD)argument);
1775 
1776  status = IntWinAgentRemoveAgentAndResetState(Agent);
1777  if (!INT_SUCCESS(status))
1778  {
1779  ERROR("[ERROR] IntWinAgentRemoveAgentAndResetState failed: 0x%08x\n", status);
1780  }
1781 
1782  return INT_STATUS_NO_DETOUR_EMU;
1783  }
1784  }
1785  else if (Registers->Rdx == Agent->Token3)
1786  {
1787  TRACE("[AGENT] Bootstrap reports completion with status = 0x%08x!\n", (DWORD)argument);
1788 
1789  Agent->Flags |= AGENT_FLAG_COMPLETED;
1790  }
1791  else
1792  {
1793  return INT_STATUS_NOT_FOUND;
1794  }
1795 
1796  if (AGENT_FLAG_ALL_DONE == (Agent->Flags & AGENT_FLAG_ALL_DONE))
1797  {
1798  INTSTATUS status2;
1799 
1800  TRACE("[AGENT] Agent bootstrap completed execution, will remove it soon.\n");
1801 
1802  status2 = Agent->CompletionCallback(Agent->DriverAddress, Agent->ErrorCode, Agent->AgentTag, Agent->Context);
1803  if (!INT_SUCCESS(status2))
1804  {
1805  ERROR("[ERROR] CompletionCallback failed: 0x%08x\n", status2);
1806  }
1807 
1808  Agent->DriverAddress = 0;
1809  }
1810 
1811  return status;
1812 }
1813 
1814 
1815 static INTSTATUS
1817  _In_ PWIN_AGENT Agent,
1818  _In_ PIG_ARCH_REGS Registers
1819  )
1845 {
1846  INTSTATUS status;
1847  QWORD arg1, arg2, op, res;
1848 
1849  if (NULL == Agent)
1850  {
1852  }
1853 
1854  if (NULL == Registers)
1855  {
1857  }
1858 
1859  res = 0;
1860 
1861  // The driver calls us. We'll make two safety checks:
1862  // 1. The caller is in kernel.
1863  // 2. The memory pages where we'll deploy the agent must be writable
1864  if (gGuest.Guest64)
1865  {
1866  arg1 = Registers->Rcx;
1867  arg2 = Registers->Rbx;
1868  }
1869  else
1870  {
1871  arg1 = Registers->Rsi;
1872  arg2 = Registers->Rdi;
1873  }
1874 
1875  op = Registers->Rdx;
1876 
1877  // If we are pending unload, ignore any agent request; we can simply return 0, which means error, and the agent
1878  // will bail out, and we'll be able to unload safely.
1879  if (gGuest.UninitPrepared && (AGENT_HCALL_ERROR != op) && (Agent->AgentType != AGENT_TYPE_PT_UNLOADER) &&
1880  (Agent->AgentType != AGENT_TYPE_VE_UNLOADER))
1881  {
1882  LOG("[AGENT] Uninit is prepared, will return error to the in-guest agent.\n");
1883  IntWinAgentRemoveEntryByAgid(Agent->Agid, NULL);
1884  res = (QWORD) - 1;
1885  goto bail_out;
1886  }
1887 
1888  if (AGENT_HCALL_FETCH_CMD == op)
1889  {
1890  AGENT_COMMAND cmd = {0};
1891  DWORD pidToInject = Agent->Pid;
1892 
1893  // This mirrors the logic from remdrv.sys' HandleProcess which will search for winlogon only when the PID
1894  // returned from this hypercall is 0
1895  if (0 == pidToInject)
1896  {
1897  PWIN_PROCESS_OBJECT pWinLogon = IntWinProcFindObjectByName("winlogon.exe", TRUE);
1898 
1899  if (NULL != pWinLogon)
1900  {
1901  pidToInject = pWinLogon->Pid;
1902  }
1903  }
1904 
1905  // The first argument is the buffer with the command.
1906  // The second argument is the buffer size.
1907  if (arg2 != sizeof(AGENT_COMMAND))
1908  {
1909  ERROR("[ERROR] Agent command structure mismatch: %lld vs %zu!\n", arg2, sizeof(AGENT_COMMAND));
1910  return INT_STATUS_NOT_SUPPORTED;
1911  }
1912 
1913  // Fetch the command.
1914  status = IntKernVirtMemRead(arg1, (DWORD)arg2, &cmd, NULL);
1915  if (!INT_SUCCESS(status))
1916  {
1917  ERROR("[ERROR] IntKernVirtMemRead failed for %llx: 0x%08x\n", arg1, status);
1918  return status;
1919  }
1920 
1921  if (cmd.Version != AGENT_COMMAND_VERSION)
1922  {
1923  ERROR("[ERROR] Agent command versions mismatch: %d vs %d\n", cmd.Version, AGENT_COMMAND_VERSION);
1924  return INT_STATUS_NOT_SUPPORTED;
1925  }
1926 
1928  cmd.Pid = pidToInject;
1929  cmd.Type = Agent->AgentType;
1930  cmd.Synched = FALSE;
1931  cmd.Agid = Agent->Agid;
1932  cmd.Size = Agent->AgentSize;
1933  cmd.Flags = 0;
1934  cmd.Pointer = 0;
1935  memcpy(cmd.Name, Agent->Name, sizeof(cmd.Name));
1936  memcpy(cmd.Args, Agent->Args, sizeof(cmd.Args));
1937 
1938  if (AGENT_TYPE_VE_UNLOADER == Agent->AgentType)
1939  {
1941  }
1942  else if (AGENT_TYPE_PT_UNLOADER == Agent->AgentType)
1943  {
1945  }
1946 
1947  IntPauseVcpus();
1948 
1949  status = IntVirtMemSafeWrite(Registers->Cr3, arg1, (DWORD)arg2, &cmd, IG_CS_RING_0);
1950  if (!INT_SUCCESS(status))
1951  {
1952  ERROR("[ERROR] IntVirtMemSafeWrite failed: 0x%08x\n", status);
1953  }
1954 
1955  IntResumeVcpus();
1956 
1957  res = INT_SUCCESS(status) ? 0 : (QWORD) - 1;
1958  }
1959  else if (AGENT_HCALL_FETCH_CHUNK == op)
1960  {
1961  DWORD actualCopy;
1962 
1963  IntPauseVcpus();
1964 
1965  actualCopy = (DWORD)MIN(arg2, Agent->AgentSize - Agent->AgentPosition);
1966 
1967  if (actualCopy > 0 && Agent->AgentPosition < Agent->AgentSize)
1968  {
1969  status = IntVirtMemSafeWrite(Registers->Cr3,
1970  arg1,
1971  actualCopy,
1972  &Agent->AgentContent[Agent->AgentPosition],
1973  IG_CS_RING_0);
1974  if (!INT_SUCCESS(status))
1975  {
1976  ERROR("[ERROR] IntVirtMemSafeWrite failed at GVA 0x%016llx, size %x: 0x%08x\n",
1977  arg1, actualCopy, status);
1978 
1979  res = 0;
1980  }
1981  else
1982  {
1983  res = actualCopy;
1984 
1985  Agent->AgentPosition += (DWORD)res;
1986  }
1987  }
1988  else
1989  {
1990  res = 0;
1991  }
1992 
1993  IntResumeVcpus();
1994  }
1995  else if (AGENT_HCALL_MOD_BASE == op)
1996  {
1997  char modName[256];
1998  PWCHAR wModuleName;
1999  DWORD len, i;
2000  QWORD modBase;
2001 
2002  wModuleName = NULL;
2003 
2004  memset(modName, 0, sizeof(modName));
2005 
2006  len = (DWORD)MIN(arg2, 255u);
2007 
2008  status = IntKernVirtMemRead(arg1, len, modName, NULL);
2009  if (!INT_SUCCESS(status))
2010  {
2011  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
2012  modBase = 0;
2013  goto _mod_fail_and_exit;
2014  }
2015 
2016  // len + 2 OK: len is not longer than 255.
2017  wModuleName = HpAllocWithTag((len + 1ull) * 2, IC_TAG_DRNU);
2018  if (NULL == wModuleName)
2019  {
2020  modBase = 0;
2022  goto _mod_fail_and_exit;
2023  }
2024 
2025  i = 0;
2026 
2027  while (i < len + 1)
2028  {
2029  wModuleName[i] = modName[i];
2030 
2031  i++;
2032  }
2033 
2034  if (0 == strcmp(modName, "ntoskrnl.exe"))
2035  {
2037  if (NULL == pDriver)
2038  {
2039  ERROR("[ERROR] IntDriverFindByLoadOrder failed for %s: 0x%08x\n", modName, status);
2040  modBase = 0;
2041  goto _mod_fail_and_exit;
2042  }
2043 
2044  modBase = pDriver->BaseVa;
2045  }
2046  else if (0 == strcmp(modName, "hal.dll"))
2047  {
2049  if (NULL == pDriver)
2050  {
2051  ERROR("[ERROR] IntDriverFindByLoadOrder failed for %s: 0x%08x\n", modName, status);
2052  modBase = 0;
2053  goto _mod_fail_and_exit;
2054  }
2055 
2056  modBase = pDriver->BaseVa;
2057  }
2058  else
2059  {
2060  KERNEL_DRIVER *pDriver = IntDriverFindByName(wModuleName);
2061  if (NULL == pDriver)
2062  {
2063  ERROR("[ERROR] IntDriierverFindByName failed for %s: 0x%08x\n", modName, status);
2064  modBase = 0;
2065  goto _mod_fail_and_exit;
2066  }
2067 
2068  modBase = pDriver->BaseVa;
2069  }
2070 
2071 
2072 _mod_fail_and_exit:
2073 
2074  if (NULL != wModuleName)
2075  {
2076  HpFreeAndNullWithTag(&wModuleName, IC_TAG_DRNU);
2077  }
2078 
2079  // Store the module base in Rax.
2080  res = modBase;
2081  }
2082  // op == 4 -> Request agent pool address.
2083  else if (AGENT_HCALL_OWN_BASE == op)
2084  {
2085  res = Agent->DriverAddress;
2086  }
2087  // op == 5 -> Request a generic data region to be delivered inside a pre-allocated memory region.
2088  else if (AGENT_HCALL_VE == op || AGENT_HCALL_PT == op)
2089  {
2090  if (NULL != Agent->DeliverCallback)
2091  {
2092  res = Agent->DeliverCallback(arg1, (DWORD)arg2, Agent->Context);
2093  }
2094  }
2095  else if (AGENT_HCALL_VCPUID == op)
2096  {
2097  res = gVcpu->Index;
2098  }
2099  else if (AGENT_HCALL_SYS_LNK == op)
2100  {
2101  QWORD linkage = 0;
2102  DWORD sysNo;
2103 
2104  if (arg1 >= winKmFieldSyscallNumbersEnd)
2105  {
2106  ERROR("[ERROR] Requested syscall linkage not supported by introcore: 0x%016llx\n", arg1);
2107  goto _sys_lnk_fail_and_exit;
2108  }
2109 
2111  status = IntWinAgentFindSyscallLinkage(sysNo, &linkage);
2112  if (!INT_SUCCESS(status))
2113  {
2114  ERROR("[ERROR] IntWinAgentFindSyscallLinkage failed for 0x%x: 0x%08x\n", sysNo, status);
2115  linkage = 0;
2116  }
2117 
2118 _sys_lnk_fail_and_exit:
2119 
2120  res = linkage;
2121  }
2122  else if (AGENT_HCALL_ERROR == op)
2123  {
2124  LOG("[AGENT] Agent reports error code: 0x%08x\n", (DWORD)arg2);
2125  Agent->ErrorCode = (DWORD)arg2;
2126  res = 0;
2127 
2128  if (Agent->ErrorCode != 0)
2129  {
2130  PEVENT_AGENT_EVENT event = &gAlert.Agent;
2131 
2132  event->Event = agentError;
2133  event->AgentTag = Agent->AgentTag;
2134  event->ErrorCode = Agent->ErrorCode;
2135 
2136  status = IntNotifyIntroEvent(introEventAgentEvent, event, sizeof(*event));
2137  if (!INT_SUCCESS(status))
2138  {
2139  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%x\n", status);
2140  }
2141 
2142  // An error occurred, the process will never be created - remove it from the list of pending processes.
2143  IntWinAgentRemoveEntryByAgid(Agent->Agid, NULL);
2144  }
2145  }
2146 
2147 bail_out:
2148  // NOTE: The result is returned in EDX/RDX due to the fact that Xen hypercall interface always returns 0 in EAX/RAX.
2149  Registers->Rdx = res;
2150 
2151  status = IntSetGprs(IG_CURRENT_VCPU, Registers);
2152  if (!INT_SUCCESS(status))
2153  {
2154  ERROR("[ERROR] IntSetGprs failed: 0x%08x\n", status);
2155  }
2156 
2157  return INT_STATUS_SUCCESS;
2158 }
2159 
2160 
2161 static INTSTATUS
2163  _In_opt_ void *Reserved,
2164  _In_ PIG_ARCH_REGS Registers
2165  )
2184 {
2185  INTSTATUS status;
2186  QWORD arg1, arg2, op;
2187 
2188  UNREFERENCED_PARAMETER(Reserved);
2189 
2190  if (NULL == Registers)
2191  {
2193  }
2194 
2195  // The driver calls us. We'll make two safety checks:
2196  // 1. The caller is in kernel.
2197  // 2. The memory pages where we'll deploy the agent must be writable
2198  if (gGuest.Guest64)
2199  {
2200  arg1 = Registers->Rcx;
2201  arg2 = Registers->Rbx;
2202  }
2203  else
2204  {
2205  arg1 = (DWORD)Registers->Rsi;
2206  arg2 = (DWORD)Registers->Rdi;
2207  }
2208 
2209  op = Registers->Rdx;
2210 
2211  // for now, we will keep different HCALLs for different visibility event jobs, but maybe we should have one
2212  // HCALL and multiple job types?
2213  if (AGENT_HCALL_INTERNAL == op)
2214  {
2215  // This is reserved for reporting error codes from within the winlogon.exe process start stub.
2216  PEVENT_AGENT_EVENT event = &gAlert.Agent;
2217 
2218  IntWinAgentRemoveEntryByAgid((DWORD)arg1, &event->AgentTag);
2219 
2220  event->Event = agentError;
2221  event->ErrorCode = (DWORD)arg2;
2222 
2223  LOG("[AGENT] User-mode stub reports error for agent with tag %d: 0x%08x\n", event->AgentTag, event->ErrorCode);
2224 
2225  status = IntNotifyIntroEvent(introEventAgentEvent, event, sizeof(*event));
2226  if (!INT_SUCCESS(status))
2227  {
2228  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%x\n", status);
2229  }
2230  }
2231  else
2232  {
2233  PWIN_PROCESS_OBJECT pProc = IntWinProcFindObjectByCr3(Registers->Cr3);
2234 
2235  // bail out if this VMCALL is not from an agent or it is from a previous agent
2236  if (NULL == pProc)
2237  {
2238  WARNING("[WARNING] VMCALL received from unknown process, will ignore. Cr3 = 0x%016llx\n", Registers->Cr3);
2239  return INT_STATUS_SUCCESS;
2240  }
2241  else if (!pProc->IsAgent || pProc->IsPreviousAgent)
2242  {
2243  TRACE("[AGENT] VMCALL with op = %lld from `%s` (PID = %d) which is not an agent (previous = %d), "
2244  "will ignore\n", op, pProc->Name, pProc->Pid, pProc->IsPreviousAgent);
2245  return INT_STATUS_SUCCESS;
2246  }
2247 
2248  if (AGENT_HCALL_REM_TOOL == op)
2249  {
2250  status = IntAgentHandleRemediationVmcall(NULL, Registers);
2251  if (!INT_SUCCESS(status))
2252  {
2253  ERROR("[ERROR] IntAgentHandleRemediationVmcall failed: 0x%08x\n", status);
2254  return status;
2255  }
2256  }
2257  else if (AGENT_HCALL_GATHER_TOOL == op)
2258  {
2259  status = IntAgentHandleLogGatherVmcall(NULL, Registers);
2260  if (!INT_SUCCESS(status))
2261  {
2262  ERROR("[ERROR] IntAgentHandleLogGatherVmcall failed: 0x%08x\n", status);
2263  return status;
2264  }
2265  }
2266  }
2267 
2268  return INT_STATUS_SUCCESS;
2269 }
2270 
2271 
2272 INTSTATUS
2274  _In_ QWORD Rip,
2275  _In_ DWORD CpuNumber
2276  )
2297 {
2298  INTSTATUS status, status2;
2299  PWIN_AGENT pAg;
2300  PIG_ARCH_REGS regs;
2301  DWORD ring;
2302 
2304 
2305  ring = 0;
2306 
2307  regs = &gVcpu->Regs;
2308 
2309  status = IntGetCurrentRing(CpuNumber, &ring);
2310  if (!INT_SUCCESS(status))
2311  {
2312  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
2313  return status;
2314  }
2315 
2316  // No point in handling ring 3 breakpoints. We never establish breakpoints in user-mode, so this must be reinjected.
2317  if (0 != ring)
2318  {
2319  return INT_STATUS_NOT_FOUND;
2320  }
2321 
2322  // Make sure we have an active agent.
2323  pAg = gAgentState.ActiveAgent;
2324 
2325  if ((NULL == pAg) && (regs->Rip == gAgentState.Trampoline + gAgentState.OffsetVmcall1))
2326  {
2327  // This can happen when:
2328  // 1. there are more than 1 VCPUs
2329  // 2. the trigger SYSCALL is executed on at least 2 VCPUs
2330  // 3. one of the VCPUs gets to inject the agent
2331  // 4. the other VCPU is delayed significantly and executes the INT3 after the agent has been fully injected
2332  status = IntWinAgentHandleLoader1Hypercall(NULL, regs);
2333  if (!INT_SUCCESS(status))
2334  {
2335  ERROR("[ERROR] IntWinAgentHandleLoader1Hypercall failed: 0x%08x\n", status);
2336  }
2337 
2338  goto cleanup_and_exit;
2339  }
2340 
2341  if (pAg)
2342  {
2343  // Trampoline breakpoint.
2344  if ((regs->Rip == gAgentState.Trampoline + gAgentState.OffsetVmcall1) ||
2345  (regs->Rip == gAgentState.Trampoline + gAgentState.OffsetVmcall2))
2346  {
2347  status = IntWinAgentHandleLoader1Hypercall(pAg, regs);
2348  if (!INT_SUCCESS(status))
2349  {
2350  ERROR("[ERROR] IntWinAgentHandleLoader1Hypercall failed: 0x%08x\n", status);
2351  }
2352  }
2353  // Bootstrap breakpoint.
2354  else if ((regs->Rip >= pAg->BootstrapAddress) && (regs->Rip < pAg->BootstrapAddress + pAg->BootstrapSize))
2355  {
2356  status = IntWinAgentHandleLoader2Hypercall(pAg, regs);
2357  if (!INT_SUCCESS(status))
2358  {
2359  ERROR("[ERROR] IntWinAgentHandleLoader2Hypercall failed: 0x%08x\n", status);
2360  }
2361  }
2362  // Direct breakpoint agent.
2363  else if ((regs->Rip == pAg->InstructionAddress) && (pAg->AgentType == AGENT_TYPE_BREAKPOINT))
2364  {
2365  status = IntWinAgentHandleBreakpointAgent(pAg, regs);
2366  if (!INT_SUCCESS(status))
2367  {
2368  ERROR("[ERROR] IntWinAgentHandleBreakpointAgent failed: 0x%08x\n", status);
2369  }
2370  }
2371  // Unknown.
2372  else
2373  {
2374  status = INT_STATUS_NOT_FOUND;
2375  goto cleanup_and_exit;
2376  }
2377  }
2378  else
2379  {
2380  status = INT_STATUS_NOT_FOUND;
2381  goto cleanup_and_exit;
2382  }
2383 
2384 cleanup_and_exit:
2385  // One agent finished execution, schedule another, if there are any.
2386  status2 = IntWinAgentActivatePendingAgent();
2387  if (!INT_SUCCESS(status2))
2388  {
2389  ERROR("[ERROR] IntAgentActivatePendingAgent failed: 0x%08x\n", status2);
2390  }
2391 
2392  return status;
2393 }
2394 
2395 
2396 INTSTATUS
2398  _In_ QWORD Rip
2399  )
2411 {
2412  INTSTATUS status;
2413  PIG_ARCH_REGS regs;
2414  DWORD ring = 0;
2415 
2416  regs = &gVcpu->Regs;
2417 
2418  status = IntGetCurrentRing(gVcpu->Index, &ring);
2419  if (!INT_SUCCESS(status))
2420  {
2421  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
2422  return status;
2423  }
2424 
2425  TRACE("VMCALL -> rip: 0x%016llx, cr3: 0x%016llx, rax: 0x%016llx, rcx: 0x%016llx, rdx: 0x%016llx, rbx: 0x%016llx\n",
2426  regs->Rip, regs->Cr3, regs->Rax, regs->Rcx, regs->Rdx, regs->Rbx);
2427 
2428  if (0 == ring)
2429  {
2430  PWIN_AGENT pAg;
2431 
2432  // ring0 hypercall; this happens in three cases:
2433  // 1. stage 1 loader
2434  // 2. stage 2 loader
2435  // 3. agent driver
2436 
2437  // Make sure we have an active agent.
2438  if (NULL == gAgentState.ActiveAgent)
2439  {
2440  ERROR("[ERROR] VMCALL with no active agent from RIP 0x%016llx!\n", regs->Rip);
2441  goto cleanup_and_exit;
2442  }
2443 
2444  pAg = gAgentState.ActiveAgent;
2445 
2446  if ((pAg->DriverAddress <= Rip) && (pAg->DriverAddress + pAg->DriverSize > Rip))
2447  {
2448  status = IntWinAgentHandleDriverVmcall(pAg, regs);
2449  if (!INT_SUCCESS(status))
2450  {
2451  ERROR("[ERROR] IntWinAgentHandleDriverVmcall failed: 0x%08x\n", status);
2452  }
2453  }
2454  }
2455  else
2456  {
2457  // ring3 hypercall; these are initiated by the agents. Note that these can be issued at any time, even
2458  // after an introcore unload/reload. They must not fail.
2459  status = IntWinAgentHandleAppVmcall(NULL, regs);
2460  if (!INT_SUCCESS(status))
2461  {
2462  ERROR("[ERROR] IntWinAgentHandleAppVmcall failed: 0x%08x\n", status);
2463  }
2464  }
2465 
2466 cleanup_and_exit:
2467 
2468  // One agent finished execution, schedule another, if there are any.
2470  if (!INT_SUCCESS(status))
2471  {
2472  ERROR("[ERROR] IntWinAgentActivatePendingAgent failed: 0x%08x\n", status);
2473  }
2474 
2475  // We must return success, otherwise the HV might bug-check.
2476  return INT_STATUS_SUCCESS;
2477 }
2478 
2479 
2480 INTSTATUS
2482  _In_ DWORD Size,
2483  _Out_ QWORD *Address
2484  )
2498 {
2499  INTSTATUS status;
2500  KERNEL_DRIVER *pDriver;
2501 
2502  if (NULL == Address)
2503  {
2505  }
2506 
2507  // If we've been loaded from the OS, we'll use the first page of the kernel - the headers.
2508  // This is dirty and patchy, but it is just for testing purposes.
2509 
2510  // First, try the kernel.
2511  TRACE("[AGENT] Trying to find slack inside the kernel...\n");
2512 
2513  status = IntSlackAlloc(gGuest.KernelVa, FALSE, Size, Address, 0);
2514  if (INT_SUCCESS(status))
2515  {
2516  // We've found slack space, we're done here.
2518  goto cleanup_and_exit;
2519  }
2520 
2521  // We couldn't find slack inside the kernel, we'll try inside hal or other core module, that can't unload.
2522  for (DWORD i = 1; i < 5; i++)
2523  {
2524  pDriver = IntDriverFindByLoadOrder(i);
2525  if (NULL == pDriver)
2526  {
2527  continue;
2528  }
2529 
2530  TRACE("[AGENT] Trying to find slack inside '%s'...\n", utf16_for_log(pDriver->Name));
2531 
2532  status = IntSlackAlloc(pDriver->BaseVa, FALSE, Size, Address, 0);
2533  if (INT_SUCCESS(status))
2534  {
2535  // We've found slack space, we're done here.
2536  goto cleanup_and_exit;
2537  }
2538  }
2539 
2540 cleanup_and_exit:
2541  ;
2542 
2543  return INT_STATUS_SUCCESS;
2544 }
2545 
2546 
2547 INTSTATUS
2549  _In_ QWORD Address
2550  )
2558 {
2560 
2561  return IntSlackFree(Address);
2562 }
2563 
2564 
2565 static void
2567  _Out_opt_ QWORD *Token1,
2568  _Out_opt_ QWORD *Token2,
2569  _Out_opt_ QWORD *Token3
2570  )
2581 {
2582  QWORD tk;
2583 
2584  if (NULL != Token1)
2585  {
2586  tk = __rdtsc();
2587 
2588  *Token1 = gGuest.Guest64 ? tk : (tk & 0xFFFFFFFF);
2589  }
2590 
2591  if (NULL != Token2)
2592  {
2593  tk = __rdtsc();
2594 
2595  *Token2 = gGuest.Guest64 ? tk : (tk & 0xFFFFFFFF);
2596  }
2597 
2598  if (NULL != Token3)
2599  {
2600  tk = __rdtsc();
2601 
2602  *Token3 = gGuest.Guest64 ? tk : (tk & 0xFFFFFFFF);
2603  }
2604 }
2605 
2606 
2607 INTSTATUS
2609  _In_ PFUNC_AgentInjection InjectionCallback,
2610  _In_ PFUNC_AgentCompletion CompletionCallback,
2611  _In_opt_ PFUNC_AgentDeliver DeliverCallback,
2612  _In_opt_ void *Context,
2613  _In_ PBYTE AgentContent,
2614  _In_ DWORD AgentSize,
2615  _In_ BOOLEAN AgentInternal,
2616  _In_ DWORD AgentTag,
2617  _In_ AGENT_TYPE AgentType,
2618  _In_opt_z_ const CHAR *Name,
2619  _In_ DWORD Options,
2620  _In_opt_ const CHAR *Args,
2621  _In_opt_ DWORD Pid,
2622  _Outptr_opt_ PWIN_AGENT *Agent
2623  )
2659 {
2660  PWIN_AGENT pAg;
2661  PAGENT_NAME pAgName;
2662  INTSTATUS status;
2663  QWORD token1, token2, token3;
2664  PBYTE pBs;
2665 
2666  UNREFERENCED_PARAMETER(Agent);
2667 
2668  token1 = 0;
2669  token2 = 0;
2670  token3 = 0;
2671  pAgName = NULL;
2672  pAg = NULL;
2673 
2674  if (NULL == InjectionCallback)
2675  {
2677  }
2678 
2679  if (NULL == CompletionCallback)
2680  {
2682  }
2683 
2685  {
2687  }
2688 
2689  if (AGENT_TYPE_PROCESS == AgentType)
2690  {
2691  if (NULL == Name)
2692  {
2694  }
2695 
2696  if (strlen(Name) >= IG_MAX_AGENT_NAME_LENGTH)
2697  {
2699  }
2700 
2701  if ((NULL != Args) && (strlen(Args) >= IG_MAX_COMMAND_LINE_LENGTH))
2702  {
2704  }
2705  }
2706 
2707  if (AGENT_TYPE_FILE == AgentType)
2708  {
2709  if (NULL == Name)
2710  {
2712  }
2713  }
2714 
2715  // Make sure that another agent with this name isn't injected
2716  if (NULL != Name)
2717  {
2718  LIST_ENTRY *list = gAgentState.AgentNames.Flink;
2719  while (list != &gAgentState.AgentNames)
2720  {
2721  pAgName = CONTAINING_RECORD(list, AGENT_NAME, Link);
2722  list = list->Flink;
2723 
2724  if (0 == strcasecmp(pAgName->ImageName, Name))
2725  {
2726  ERROR("[ERROR] An agent with the name '%s' is already injected!\n", Name);
2727 
2729 
2730  goto cleanup_and_exit;
2731  }
2732  }
2733 
2734  pAgName = NULL;
2735  }
2736 
2737  IntWinAgentSelectTokens(&token1, &token2, &token3);
2738 
2739  TRACE("[AGENT] Selected tokens: 0x%016llx 0x%016llx 0x%016llx\n", token1, token2, token3);
2740 
2741  pAg = HpAllocWithTag(sizeof(*pAg), IC_TAG_AGNE);
2742  if (NULL == pAg)
2743  {
2745  goto cleanup_and_exit;
2746  }
2747 
2748  pAg->AgentType = AgentType;
2749  pAg->HcallType = AGENT_HCALL_INT3;
2750  pAg->InjectionCallback = InjectionCallback;
2751  pAg->CompletionCallback = CompletionCallback;
2752  pAg->DeliverCallback = DeliverCallback;
2753  pAg->Context = Context;
2754  pAg->AgentTag = AgentTag;
2755  pAg->DriverAddress = 0;
2756  pAg->Token1 = token1;
2757  pAg->Token2 = token2;
2758  pAg->Token3 = token3;
2759  pAg->InsCloakRegion = NULL;
2760  pAg->BootCloakRegion = NULL;
2761  pAg->InstructionRestored = TRUE;
2762  pAg->Pid = Pid;
2763  pAg->ArgsLen = 0;
2764  pAg->AgentContent = AgentContent;
2765  pAg->AgentSize = AgentSize;
2766  pAg->AgentInternal = AgentInternal;
2767  pAg->AgentPosition = 0;
2768  pAg->Agid = gAgentState.Counter++;
2769  pAg->Options = Options;
2770 
2771  if (NULL != Args)
2772  {
2773  pAg->ArgsLen = strlen(Args);
2774  if (pAg->ArgsLen >= sizeof(pAg->Args))
2775  {
2777  goto cleanup_and_exit;
2778  }
2779 
2780  if (pAg->ArgsLen > 0)
2781  {
2782  strlcpy(pAg->Args, Args, sizeof(pAg->Args));
2783  }
2784  }
2785 
2786  if (NULL != Name)
2787  {
2788  strlcpy(pAg->Name, Name, sizeof(pAg->Name));
2789  }
2790 
2793  &pAg->DriverSize,
2794  &pAg->DriverEntryPoint);
2795  if (!INT_SUCCESS(status))
2796  {
2797  ERROR("[ERROR] IntLdrGetImageSizeAndEntryPoint failed: 0x%08x\n", status);
2798  goto cleanup_and_exit;
2799  }
2800 
2801  if (gGuest.Guest64)
2802  {
2804 
2806  }
2807  else
2808  {
2810 
2812  }
2813 
2814  pBs = pAg->BootStrap;
2815 
2816  // Fix bootstrap agent internal variables
2817  if (gGuest.Guest64)
2818  {
2822  *((PDWORD)(pBs + OFFSET_WIN_X64_AGENT_SIZE)) = pAg->DriverSize;
2823  *((PDWORD)(pBs + OFFSET_WIN_X64_AGENT_EP)) = pAg->DriverEntryPoint;
2824  // It seems that on Windows 10 RS6 return NULL if we try to ExAllocatePoolWithTag and the tag is in the
2825  // range used by our internals tags ([0, 100]).
2826  // Anyways, the documentation states that each tag character must have a value between [0x20, 0x7E].
2827  *((PDWORD)(pBs + OFFSET_WIN_X64_AGENT_TAG)) = 'Agnt'; // AgentTag;
2828 
2829  *((PQWORD)(pBs + OFFSET_WIN_X64_TOKEN1)) = token1;
2830  *((PQWORD)(pBs + OFFSET_WIN_X64_TOKEN2)) = token2;
2831  *((PQWORD)(pBs + OFFSET_WIN_X64_TOKEN3)) = token3;
2832 
2833  *((PQWORD)(pBs + OFFSET_WIN_X64_JUMPBACK)) = 0;
2834 
2836  }
2837  else
2838  {
2842  *((PDWORD)(pBs + OFFSET_WIN_X86_AGENT_SIZE)) = pAg->DriverSize;
2843  *((PDWORD)(pBs + OFFSET_WIN_X86_AGENT_EP)) = pAg->DriverEntryPoint;
2844  // It seems that on Windows 10 RS6 return NULL if we try to ExAllocatePoolWithTag and the tag is in the
2845  // range used by our internals tags ([0, 100]).
2846  // Anyways, the documentation states that each tag character must have a value between [0x20, 0x7E].
2847  *((PDWORD)(pBs + OFFSET_WIN_X86_AGENT_TAG)) = 'Agnt'; // AgentTag;
2848 
2849  *((PDWORD)(pBs + OFFSET_WIN_X86_TOKEN1)) = (DWORD)token1;
2850  *((PDWORD)(pBs + OFFSET_WIN_X86_TOKEN2)) = (DWORD)token2;
2851  *((PDWORD)(pBs + OFFSET_WIN_X86_TOKEN3)) = (DWORD)token3;
2852 
2853  *((PDWORD)(pBs + OFFSET_WIN_X86_JUMPBACK)) = 0;
2854 
2856  }
2857 
2858  if (AGENT_TYPE_PROCESS == AgentType)
2859  {
2860  // Now allocate and insert the agent-name structure, so we can send agent process creation/termination events.
2861  pAgName = HpAllocWithTag(sizeof(*pAgName), IC_TAG_AGNN);
2862  if (NULL == pAgName)
2863  {
2865  goto cleanup_and_exit;
2866  }
2867 
2868  strlcpy(pAgName->ImageName, Name, sizeof(pAgName->ImageName));
2869 
2870  pAgName->NameLen = strlen(pAgName->ImageName);
2871 
2872  strlower_utf8(pAgName->ImageName, pAgName->NameLen);
2873 
2874  pAgName->Tag = AgentTag;
2875  pAgName->Agid = pAg->Agid;
2876 
2877  // Not an typo... We only increment to 1 when the agent starts!
2878  pAgName->RefCount = 0;
2879 
2880  InsertTailList(&gAgentState.AgentNames, &pAgName->Link);
2881  }
2882 
2883  InsertTailList(&gAgentState.PendingAgents, &pAg->Link);
2884 
2885  gAgentState.PendingAgentsCount++;
2886 
2887  TRACE("[AGENT] Agent allocated and initialized!\n");
2888 
2889  // For now, we're done. The rest of the dirty work will be done by the execution handler.
2890  status = INT_STATUS_SUCCESS;
2891 
2892 cleanup_and_exit:
2893  if (!INT_SUCCESS(status))
2894  {
2895  TRACE("[INFO] Can't inject agent: 0x%08x\n", status);
2896 
2897  if (NULL != pAg)
2898  {
2899  // Note: we don't free/unmap the agent here; if this function returns an error, the caller must unmap the
2900  // agent content.
2902  }
2903  }
2904 
2905  if (INT_STATUS_SUCCESS == status)
2906  {
2907  // The agent has been created, but now we need to see if we can schedule it. If there aren't any other agents
2908  // active, then we will activate this one.
2910  if (!INT_SUCCESS(status))
2911  {
2912  ERROR("[ERROR] IntWinAgentActivatePendingAgent failed: 0x%08x\n", status);
2913  }
2914  }
2915 
2916  return status;
2917 }
2918 
2919 
2920 INTSTATUS
2922  _In_ PFUNC_AgentInjection InjectionCallback,
2923  _In_opt_ void *Context,
2924  _Outptr_opt_ PWIN_AGENT *Agent
2925  )
2941 {
2942  PWIN_AGENT pAg;
2943  INTSTATUS status;
2944 
2945  UNREFERENCED_PARAMETER(Agent);
2946 
2947  if (NULL == InjectionCallback)
2948  {
2950  }
2951 
2953  {
2955  }
2956 
2957  pAg = HpAllocWithTag(sizeof(*pAg), IC_TAG_AGNE);
2958  if (NULL == pAg)
2959  {
2961  goto cleanup_and_exit;
2962  }
2963 
2965  pAg->HcallType = AGENT_HCALL_INT3;
2966  pAg->InjectionCallback = InjectionCallback;
2967  pAg->Context = Context;
2968  pAg->InsCloakRegion = NULL;
2969  pAg->BootCloakRegion = NULL;
2970  pAg->InstructionRestored = TRUE;
2971  pAg->ArgsLen = 0;
2972  pAg->Agid = gAgentState.Counter++;
2973 
2974  InsertTailList(&gAgentState.PendingAgents, &pAg->Link);
2975 
2976  gAgentState.PendingAgentsCount++;
2977 
2978  TRACE("[AGENT] Agent allocated and initialized!\n");
2979 
2980  // For now, we're done. The rest of the dirty work will be done by the execution handler.
2981  status = INT_STATUS_SUCCESS;
2982 
2983 cleanup_and_exit:
2984  if (!INT_SUCCESS(status))
2985  {
2986  TRACE("[INFO] Can't inject agent: 0x%08x\n", status);
2987 
2988  if (NULL != pAg)
2989  {
2991  }
2992  }
2993 
2994  if (INT_STATUS_SUCCESS == status)
2995  {
2996  // The agent has been created, but now we need to see if we can schedule it. If there aren't any other agents
2997  // active, then we will activate this one.
2999  if (!INT_SUCCESS(status))
3000  {
3001  ERROR("[ERROR] IntAgentActivatePendingAgent failed: 0x%08x\n", status);
3002  }
3003  }
3004 
3005  return status;
3006 }
3007 
3008 
3009 INTSTATUS
3011  void
3012  )
3018 {
3019  // Flag that agent injection is now safe.
3020  gAgentState.SafeToInjectProcess = TRUE;
3021 
3022  // Wake-up any pending agent.
3024 }
3025 
3026 
3027 void
3029  _In_ CHAR *ImageName,
3030  _Out_ BOOLEAN *IsAgent,
3031  _Out_ DWORD *Tag
3032  )
3043 {
3044  LIST_ENTRY *list;
3045 
3046  if (NULL == ImageName)
3047  {
3048  return;
3049  }
3050 
3051  if (NULL == IsAgent)
3052  {
3053  return;
3054  }
3055 
3056  *IsAgent = FALSE;
3057  *Tag = 0;
3058 
3059  if (IsListEmpty(&gAgentState.AgentNames))
3060  {
3061  return;
3062  }
3063 
3064  list = gAgentState.AgentNames.Flink;
3065  while (list != &gAgentState.AgentNames)
3066  {
3067  PAGENT_NAME pAgName = CONTAINING_RECORD(list, AGENT_NAME, Link);
3068  list = list->Flink;
3069 
3070  if (0 == strcasecmp(ImageName, pAgName->ImageName))
3071  {
3072  pAgName->RefCount++;
3073 
3074  *IsAgent = TRUE;
3075  *Tag = pAgName->Tag;
3076 
3077  return;
3078  }
3079  }
3080 }
3081 
3082 
3083 void
3085  _In_ CHAR *ImageName,
3086  _Out_opt_ BOOLEAN *IsAgent,
3087  _Out_opt_ DWORD *Tag,
3088  _Out_opt_ BOOLEAN *Removed
3089  )
3101 {
3102  LIST_ENTRY *list;
3103 
3104  if (IsAgent)
3105  {
3106  *IsAgent = FALSE;
3107  }
3108 
3109  if (Tag)
3110  {
3111  *Tag = 0;
3112  }
3113 
3114  if (Removed)
3115  {
3116  *Removed = FALSE;
3117  }
3118 
3119  if (IsListEmpty(&gAgentState.AgentNames))
3120  {
3121  return;
3122  }
3123 
3124  list = gAgentState.AgentNames.Flink;
3125  while (list != &gAgentState.AgentNames)
3126  {
3127  PAGENT_NAME pAgName = CONTAINING_RECORD(list, AGENT_NAME, Link);
3128 
3129  list = list->Flink;
3130 
3131  if (0 == strcasecmp(ImageName, pAgName->ImageName))
3132  {
3133  if (IsAgent)
3134  {
3135  *IsAgent = TRUE;
3136  }
3137 
3138  if (Tag)
3139  {
3140  *Tag = pAgName->Tag;
3141  }
3142 
3143  if (pAgName->RefCount > 0)
3144  {
3145  --pAgName->RefCount;
3146  }
3147  else
3148  {
3149  WARNING("[WARNING] Agent %s already done by our logic!\n", pAgName->ImageName);
3150  }
3151 
3152  if (pAgName->RefCount == 0)
3153  {
3154  RemoveEntryList(&pAgName->Link);
3155 
3156  HpFreeAndNullWithTag(&pAgName, IC_TAG_AGNN);
3157 
3158  if (Removed)
3159  {
3160  *Removed = TRUE;
3161  }
3162  }
3163 
3164  return;
3165  }
3166  }
3167 }
3168 
3169 
3170 void
3172  _In_ DWORD Counter,
3174  )
3181 {
3182  LIST_ENTRY *list;
3183 
3184  if (Tag)
3185  {
3186  *Tag = 0;
3187  }
3188 
3189  if (IsListEmpty(&gAgentState.AgentNames))
3190  {
3191  return;
3192  }
3193 
3194  list = gAgentState.AgentNames.Flink;
3195  while (list != &gAgentState.AgentNames)
3196  {
3197  PAGENT_NAME pAgName = CONTAINING_RECORD(list, AGENT_NAME, Link);
3198 
3199  list = list->Flink;
3200 
3201  if (Counter == pAgName->Agid)
3202  {
3203  if (Tag)
3204  {
3205  *Tag = pAgName->Tag;
3206  }
3207 
3208  RemoveEntryList(&pAgName->Link);
3209 
3210  HpFreeAndNullWithTag(&pAgName, IC_TAG_AGNN);
3211 
3212  return;
3213  }
3214  }
3215 }
3216 
3217 
3218 BOOLEAN
3220  _In_ QWORD Ptr,
3221  _In_ THS_PTR_TYPE Type
3222  )
3231 {
3232  if ((Ptr >= gAgentState.Trampoline) && (Ptr < gAgentState.Trampoline + gAgentState.TrampolineSize))
3233  {
3234  WARNING("[WARNING] Found %s 0x%016llx in agent trampoline 0x%016llx, 0x%x\n",
3235  Type == ptrLiveRip ? "live RIP" : "stack value",
3236  Ptr, gAgentState.Trampoline, gAgentState.TrampolineSize);
3237  return TRUE;
3238  }
3239 
3240  return FALSE;
3241 }
3242 
3243 
3247  )
3257 {
3258  // Normally, we wait only for the current agent to finish. However, if #VE is on, we need
3259  // to wait & inject the #VE unloader first.
3260 
3261  if (gAgentState.ActiveAgent)
3262  {
3263  if (NULL != Tag)
3264  {
3265  PWIN_AGENT pAg = gAgentState.ActiveAgent;
3266  *Tag = pAg->AgentTag;
3267  }
3268  return agActive;
3269  }
3270 
3271  if (gAgentState.PendingAgentsCount)
3272  {
3273  if (NULL != Tag)
3274  {
3276  *Tag = pAg->AgentTag;
3277  }
3278  return agWaiting;
3279  }
3280 
3281  if (NULL != Tag)
3282  {
3283  *Tag = 0;
3284  }
3285 
3286  return agNone;
3287 }
3288 
3289 
3290 void
3292  void
3293  )
3301 {
3302  LIST_ENTRY *list;
3303 
3304  // Remove the pending agents.
3305  if (0 != gAgentState.PendingAgentsCount)
3306  {
3307  list = gAgentState.PendingAgents.Flink;
3308 
3309  while (list != &gAgentState.PendingAgents)
3310  {
3312 
3313  list = list->Flink;
3314 
3315  // NOTE: The #VE unloader needs to be injected anyway, so don't remove that one.
3318  {
3319  continue;
3320  }
3321 
3322  RemoveEntryList(&pAg->Link);
3323 
3324  IntWinAgentFree(pAg, 0);
3325 
3326  gAgentState.PendingAgentsCount--;
3327  }
3328  }
3329 }
3330 
3331 
3332 void
3334  void
3335  )
3339 {
3340  gAgentState.SafeToInjectProcess = FALSE;
3341  gAgentState.ActiveAgent = NULL;
3342 
3343  InitializeListHead(&gAgentState.PendingAgents);
3344 
3345  InitializeListHead(&gAgentState.AgentNames);
3346 
3347  gAgentState.Initialized = TRUE;
3348 }
3349 
3350 
3351 INTSTATUS
3353  void
3354  )
3361 {
3362  INTSTATUS status;
3363  LIST_ENTRY *list;
3364 
3365  if (!gAgentState.Initialized)
3366  {
3368  }
3369 
3370  // Uninit the common part.
3371  list = gAgentState.AgentNames.Flink;
3372  while (list != &gAgentState.AgentNames)
3373  {
3374  PAGENT_NAME pAgName = CONTAINING_RECORD(list, AGENT_NAME, Link);
3375 
3376  list = list->Flink;
3377 
3378  RemoveEntryList(&pAgName->Link);
3379 
3380  HpFreeAndNullWithTag(&pAgName, IC_TAG_AGNN);
3381  }
3382 
3383  if (gAgentState.ActiveAgent != NULL)
3384  {
3385  status = IntWinAgentRemove(gAgentState.ActiveAgent);
3386  if (!INT_SUCCESS(status))
3387  {
3388  ERROR("[ERROR] IntAgentRemove failed: 0x%08x\n", status);
3389  }
3390 
3391  gAgentState.ActiveAgent = NULL;
3392  }
3393 
3394  // The trampoline is initialized, remove it.
3395  if (0 != gAgentState.Trampoline)
3396  {
3397  if (NULL != gAgentState.TrampolineCloak)
3398  {
3400  if (!INT_SUCCESS(status))
3401  {
3402  ERROR("[ERROR] IntMemClkUncloakRegion failed: 0x%08x\n", status);
3403  }
3404 
3405  gAgentState.TrampolineCloak = NULL;
3406  }
3407  else
3408  {
3409  ERROR("[ERROR] The trampoline is initialized, but no cloak region was found!\n");
3411  }
3412  }
3413 
3414  gAgentState.Initialized = FALSE;
3415 
3416  memzero(&gAgentState, sizeof(gAgentState));
3417 
3418  return INT_STATUS_SUCCESS;
3419 }
KERNEL_DRIVER * IntDriverFindByLoadOrder(DWORD LoadOrder)
Searches a driver by its module load order.
Definition: drivers.c:235
uint16_t * PWCHAR
Definition: intro_types.h:63
#define _In_opt_
Definition: intro_sal.h:16
static INTSTATUS IntWinAgentReleaseBootstrap(WIN_AGENT *Agent, IG_ARCH_REGS *Registers)
Releases the bootstrap allocated inside the guest.
Definition: winagent.c:1565
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
#define OFFSET_WIN_X86_AGENT_SIZE
Definition: winstubs.h:127
static INTSTATUS IntWinAgentDeployWinDriver(PWIN_AGENT Agent)
Inject the Windows boot driver.
Definition: winagent.c:451
DWORD CompletingAgentsCount
Number of agents that are yet to complete execution.
Definition: winagent.c:188
INTSTATUS IntWinAgentInject(PFUNC_AgentInjection InjectionCallback, PFUNC_AgentCompletion CompletionCallback, PFUNC_AgentDeliver DeliverCallback, void *Context, PBYTE AgentContent, DWORD AgentSize, BOOLEAN AgentInternal, DWORD AgentTag, AGENT_TYPE AgentType, const CHAR *Name, DWORD Options, const CHAR *Args, DWORD Pid, PWIN_AGENT *Agent)
Schedule an agent injection inside the guest.
Definition: winagent.c:2608
#define OFFSET_WIN_X64_FREE
Definition: winstubs.h:85
struct _AGENT_STATE AGENT_STATE
#define AGENT_HCALL_ERROR
Generic error signaling hypercall.
Definition: aghcall.h:36
QWORD PropperSyscallGva
Guest virtual address of the KiSystemServiceUser function.
Definition: winguest.h:821
static INTSTATUS IntWinAgentHandleAppVmcall(void *Reserved, PIG_ARCH_REGS Registers)
Handles a VMCALL issued by an application that has been injected inside the guest.
Definition: winagent.c:2162
BOOLEAN InstructionRestored
True if the detours instruction has been restored.
Definition: winagent.h:131
uint8_t BYTE
Definition: intro_types.h:47
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
static INTSTATUS IntWinAgentHandleLoader1Hypercall(WIN_AGENT *Agent, IG_ARCH_REGS *Registers)
Handle a hyper call initiated by the trampoline code.
Definition: winagent.c:1422
A single breakpoint will be injected.
Definition: aghcall.h:51
No active/pending agents.
Definition: agent.h:15
struct _WIN_OPAQUE_FIELDS::@212 Km
Kernel mode information.
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
DWORD Index
The VCPU number.
Definition: guests.h:172
#define _In_
Definition: intro_sal.h:21
QWORD SyscallAddress
Guest virtual address of the SYSCALL/SYSENTER handler.
Definition: winguest.h:815
DWORD AgentTag
Agent tag.
Definition: winagent.h:119
#define LDR_FLAG_FIX_RELOCATIONS
If flag is set, the relocations will be applied.
Definition: loader.h:11
unsigned int Agid
Internal use; IT&#39;S NOT the agent tag.
Definition: aghcall.h:76
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
static INTSTATUS IntWinAgentHandleDriverVmcall(PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
This function handles VMCALLs issued by the boot driver.
Definition: winagent.c:1816
static INTSTATUS IntWinAgentRestoreState32(PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
Restore the general purpose registers state.
Definition: winagent.c:1263
INTSTATUS IntWinAgentHandleInt3(QWORD Rip, DWORD CpuNumber)
Handle a breakpoint that was initiated inside the guest.
Definition: winagent.c:2273
uint16_t WORD
Definition: intro_types.h:48
The VE agent unloader.
Definition: aghcall.h:53
#define INT_STATUS_DISASM_ERROR
Indicates a decoder error.
Definition: introstatus.h:442
WORD Pattern[SIG_MAX_PATTERN]
Definition: patsig.h:32
The end of the fields.
Definition: winguest.h:634
We have at least pending agent waiting to be injected inside the guest.
Definition: agent.h:17
void * ActiveAgent
There can be only one active agent at any given moment. This is the one.
Definition: winagent.c:185
unsigned int Size
The size of the agent.
Definition: aghcall.h:73
#define _Out_writes_bytes_(expr)
Definition: intro_sal.h:38
static INTSTATUS IntWinAgentFindInstruction(BYTE MinLen, QWORD *InstructionVa, BYTE *InstructionLen, BYTE *InstructionBytes)
Searches for a suitable instruction to replace with a CALL to the trampoline code.
Definition: winagent.c:772
DWORD Options
Agent options.
Definition: winagent.h:155
#define OFFSET_WIN_X64_AGENT_TAG
Definition: winstubs.h:88
DWORD Counter
Incremented on each agent injection, used to generate unique agent IDs.
Definition: winagent.c:181
INTSTATUS IntPeFindFunctionByPattern(QWORD ImageBase, WIN_UNEXPORTED_FUNCTION_PATTERN *Pattern, BOOLEAN IgnoreSectionHint, DWORD *Rva)
Find a function using a pattern.
Definition: winpe.c:3150
unsigned int Type
The agent type. One of AGENT_TYPE.
Definition: aghcall.h:70
static void IntWinAgentSelectTokens(QWORD *Token1, QWORD *Token2, QWORD *Token3)
Randomly select 3 tokens to be used by the bootstrap code when issuing hyper calls.
Definition: winagent.c:2566
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
#define OFFSET_WIN_X86_FREE
Definition: winstubs.h:125
#define IC_ANY_VAS
Definition: icache.h:86
#define AGENT_HCALL_INTERNAL
Reserved for internal use.
Definition: intro_types.h:2123
AGENT_EVENT_TYPE Event
The type of the agent.
Definition: intro_types.h:2347
#define OFFSET_WIN_X64_SEMAPHORE
Definition: winstubs.h:90
BYTE BootStrap[MAX_BOOTSTRAP_SIZE]
The bootstrap code.
Definition: winagent.h:153
QWORD DriverAddress
Address of the boot driver.
Definition: winagent.h:115
QWORD BaseVa
The guest virtual address of the kernel module that owns this driver object.
Definition: drivers.h:41
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
QWORD ExFreePoolWithTag
Guest virtual address of the ExFreePoolWithTag kernel function.
Definition: winguest.h:814
static BOOLEAN IsListEmpty(const LIST_ENTRY *ListHead)
Definition: introlists.h:78
#define OFFSET_WIN_X86_TOKEN1
Definition: winstubs.h:131
BYTE gWindowsBootstrapAgentx64[0x180]
Definition: winstubs.h:56
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
LIST_ENTRY Link
List entry element.
Definition: winagent.c:155
BOOLEAN IntWinAgentIsRipInsideCurrentAgent(QWORD Rip)
Return true if the given RIP points inside the currently active boot driver.
Definition: winagent.c:197
#define OFFSET_WIN_X86_TOKEN3
Definition: winstubs.h:133
#define IG_MAX_AGENT_NAME_LENGTH
Definition: glueiface.h:1712
DWORD DriverEntryPoint
Entry point of the boot driver.
Definition: winagent.h:117
INTSTATUS IntWinAgentInjectBreakpoint(PFUNC_AgentInjection InjectionCallback, void *Context, PWIN_AGENT *Agent)
Injects a breakpoint agent inside the guest.
Definition: winagent.c:2921
static INTSTATUS IntWinAgentRemoveAgentAndResetState(WIN_AGENT *Agent)
Removes the indicated agent.
Definition: winagent.c:1612
QWORD BootstrapAddress
Address where the bootstrap was allocated.
Definition: winagent.h:111
Event structure for agent injection and termination.
Definition: intro_types.h:2345
static INTSTATUS IntWinAgentFree(PWIN_AGENT Agent, QWORD DataInfo)
Frees an agent.
Definition: winagent.c:238
static INTSTATUS IntWinAgentHandleLoader2Hypercall(PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
Handles VMCALLs issued by the bootstrap code.
Definition: winagent.c:1675
BYTE InstructionLen
Detoured instruction length.
Definition: winagent.h:130
#define OFFSET_WIN_X64_TOKEN3
Definition: winstubs.h:93
PFUNC_AgentCompletion CompletionCallback
Completion callback.
Definition: winagent.h:107
BYTE gTrampolineAgentx64[54]
Definition: trampoline64.h:1
struct _AGENT_NAME AGENT_NAME
void IntWinAgentDisablePendingAgents(void)
Disables all pending agents.
Definition: winagent.c:3291
static INTSTATUS IntWinAgentHandleBreakpointAgent(PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
Handle an INT3 that was initiated by a breakpoint agent.
Definition: winagent.c:1332
#define ERROR(fmt,...)
Definition: glue.h:62
#define _Outptr_opt_
Definition: intro_sal.h:39
INTSTATUS IntPeFindFunctionByPatternInBuffer(BYTE *Buffer, DWORD BufferSize, WIN_UNEXPORTED_FUNCTION_PATTERN *Pattern, BOOLEAN IgnoreSectionHint, DWORD *Rva)
Find a function using a pattern.
Definition: winpe.c:3044
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
INTSTATUS IntAgentHandleRemediationVmcall(void *Reserved, PIG_ARCH_REGS Registers)
Handle a VMCALL issued by a remediation agent.
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define AGENT_COMMAND_VERSION
Agent command structure version. Increment this whenever modifying the AGENT_COMMAND structure...
Definition: aghcall.h:60
#define TRAMP_X86_STOP
Definition: winstubs.h:46
DWORD OSVersion
Os version.
Definition: guests.h:281
#define OFFSET_WIN_X64_TOKEN2
Definition: winstubs.h:92
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
BOOLEAN BootstrapAgentAllocated
True if the slack space for the bootstrap agent has been allocated.
Definition: guests.h:332
The PT filter loader.
Definition: aghcall.h:54
BOOLEAN Initialized
True if the agents state has been initialized.
Definition: winagent.c:172
QWORD Token2
Token used by the bootstrap code.
Definition: winagent.h:123
QWORD ExAllocatePoolWithTag
Guest virtual address of the ExAllocatePoolWithTag kernel function.
Definition: winguest.h:813
#define IMAGE_BASE_NAME_LEN
The maximum length of a process name.
Definition: winguest.h:15
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
#define REM_DRV(arch64)
Definition: winagent.c:137
#define INT_STATUS_OPERATION_NOT_IMPLEMENTED
Definition: introstatus.h:125
#define MIN(a, b)
Definition: introdefs.h:146
Will write the contents of the patched data inside the guest.
Definition: memcloak.h:54
PBYTE AgentContent
Agent contents. Can be a file, process, driver, etc.
Definition: winagent.h:135
#define OFFSET_WIN_X86_SEMAPHORE
Definition: winstubs.h:130
INTSTATUS IntSlackAlloc(QWORD ModuleBase, BOOLEAN Pageable, DWORD Size, QWORD *Buffer, QWORD SecHint)
Allocate slack inside the guest.
Definition: slack.c:437
#define AGENT_HCALL_FETCH_CHUNK
Used to get the remediation agent data.
Definition: aghcall.h:26
Describes a pattern for a kernel function that is not exported.
Definition: winguest.h:85
#define TRAMPOLINE_MAX_SIZE
Definition: winstubs.h:50
#define OFFSET_WIN_X86_THREAD
Definition: winstubs.h:126
#define OFFSET_WIN_X64_AGENT_SIZE
Definition: winstubs.h:87
INTSTATUS IntWinAgentEnableInjection(void)
enables agent injections.
Definition: winagent.c:3010
#define LOG(fmt,...)
Definition: glue.h:61
void IntWinAgentCheckIfProcessAgentAndDecrement(CHAR *ImageName, BOOLEAN *IsAgent, DWORD *Tag, BOOLEAN *Removed)
Checks if a process is an agent or not, and decrements the ref count of that name.
Definition: winagent.c:3084
Describes a kernel driver.
Definition: drivers.h:30
uint32_t * PDWORD
Definition: intro_types.h:49
KERNEL_DRIVER * IntDriverFindByName(const void *Name)
Searches for a driver by its name.
Definition: drivers.c:266
INTSTATUS IntAgentHandleLogGatherVmcall(void *Reserved, PIG_ARCH_REGS Registers)
Handle a VMCALL issued by a log gather agent.
Definition: hnd_loggather.c:11
DWORD Agid
Agent ID. Unique for each injected agent.
Definition: winagent.h:141
QWORD PsCreateSystemThread
Guest virtual address of the PsCreateSystemThread kernel function.
Definition: winguest.h:812
#define AG_OPT_INJECT_ON_RIP_POWSTATE_CHANGE
Definition: winagent.h:17
struct _AGENT_NAME * PAGENT_NAME
Allows the code inside the region to modify the region.
Definition: memcloak.h:53
#define MAX_BOOTSTRAP_SIZE
Maximum size of the bootstrap code.
Definition: winagent.h:12
A driver will be injected and started inside the kernel. NOT USED!
Definition: aghcall.h:50
#define INT_STATUS_INVALID_PARAMETER_10
Definition: introstatus.h:89
CHAR Name[IG_MAX_AGENT_NAME_LENGTH]
Agent name.
Definition: winagent.h:148
#define OFFSET_WIN_X86_TOKEN2
Definition: winstubs.h:132
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
BYTE gTrampolineAgentx86[52]
Definition: trampoline32.h:1
void * TrampolineCloak
Cloak handle used to hide the trampoline inside the guest.
Definition: winagent.c:174
INTSTATUS IntWinAgentUnInit(void)
Uninit the agents state.
Definition: winagent.c:3352
#define AGENT_HCALL_GATHER_TOOL
Log gathering tool.
Definition: intro_types.h:2119
DWORD PendingAgentsCount
Number of agents waiting to be activated.
Definition: winagent.c:186
DWORD BootstrapAgentsCount
Number of agents bootstrapping.
Definition: winagent.c:187
#define AGENT_HCALL_PT
Used to get the PT cache agent.
Definition: aghcall.h:30
#define _Out_opt_
Definition: intro_sal.h:30
#define OFFSET_WIN_X86_JUMPBACK
Definition: winstubs.h:134
INTSTATUS IntWinAgentReleaseBootstrapAddress(QWORD Address)
Releases the slack space allocated for the bootstrap code.
Definition: winagent.c:2548
#define TRAMP_X64_STOP
Definition: winstubs.h:27
CHAR Args[IG_MAX_COMMAND_LINE_LENGTH]
Agent arguments.
Definition: winagent.h:146
#define INT_STATUS_ALREADY_INITIALIZED
Definition: introstatus.h:263
#define OFFSET_WIN_X86_AGENT_EP
Definition: winstubs.h:129
#define IC_TAG_AGNE
Agent entry.
Definition: memtags.h:47
#define IG_CURRENT_VCPU
For APIs that take a VCPU number as a parameter, this can be used to specify that the current VCPU sh...
Definition: glueiface.h:324
uint8_t * PBYTE
Definition: intro_types.h:47
DWORD IsPreviousAgent
TRUE if this is an agent injected in a previous session.
Definition: winprocess.h:164
#define OFFSET_WIN_X64_ALLOC
Definition: winstubs.h:84
INTSTATUS IntDetModifyPublicData(DETOUR_TAG Tag, void const *Data, DWORD DataSize, char const *PublicDataName)
Modifies public parts of a detour handler.
Definition: detours.c:1751
__noreturn void IntBugCheck(void)
Definition: glue.c:917
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
void * Context
Optional context. Passed along to the 3 callbacks above.
Definition: winagent.h:108
BOOLEAN SafeToInjectProcess
Will be true the moment it&#39;s safe to inject agents (the OS has booted).
Definition: winagent.c:189
unsigned long long Pointer
A pointer to the agent contents in guest memory.
Definition: aghcall.h:75
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
#define AGENT_HCALL_OWN_BASE
Used to get the base of the agent module.
Definition: aghcall.h:28
#define memzero(a, s)
Definition: introcrt.h:35
INTSTATUS(* PFUNC_AgentCompletion)(QWORD GuestVirtualAddress, DWORD ErrorCode, DWORD AgentTag, void *Context)
Completion callback.
Definition: winagent.h:56
static INTSTATUS IntWinAgentFindPropperSyscall(QWORD *PropperSyscall)
Find the main SYSCALL handler.
Definition: winagent.c:576
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
QWORD Trampoline
The address of the trampoline code (slacked inside the kernel).
Definition: winagent.c:173
unsigned long long QWORD
Definition: intro_types.h:53
CHAR Name[IMAGE_BASE_NAME_LEN]
Process base name.
Definition: winprocess.h:108
#define IG_MAX_COMMAND_LINE_LENGTH
Definition: glueiface.h:1711
#define TRAMP_X64_VMCALL1
Definition: winstubs.h:28
SIZE_T NameLen
Name length.
Definition: winagent.c:157
#define OFFSET_WIN_X64_AGENT_EP
Definition: winstubs.h:89
DWORD BootstrapSize
The size of the bootstrap.
Definition: winagent.h:112
QWORD IntPtiGetAgentAddress(void)
Get the guest virtual address where the PT filter resides.
Definition: ptfilter.c:1974
Informational event sent when the remediation tool is injected or terminated. See EVENT_AGENT_EVENT...
Definition: intro_types.h:104
#define TRUE
Definition: intro_types.h:30
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
PWIN_PROCESS_OBJECT IntWinProcFindObjectByName(CHAR const *Name, BOOLEAN MustBeSystem)
Finds a process by name.
Definition: winprocesshp.c:157
INTSTATUS(* PFUNC_AgentInjection)(QWORD GuestVirtualAddress, DWORD AgentTag, void *Context)
Injection callback.
Definition: winagent.h:33
#define AGENT_HCALL_SYS_LNK
Used to get a kernel syscall linkage address.
Definition: aghcall.h:32
AGENT_HCALL HcallType
Hyper call type.
Definition: winagent.h:103
static INTSTATUS IntWinAgentReleaseBootstrapAndRemoveAgent(WIN_AGENT *Agent, IG_ARCH_REGS *Registers)
Releases the bootstrap address and removes the agent.
Definition: winagent.c:1641
#define TRAMP_X86_VMCALL2
Definition: winstubs.h:48
The VE agent loader.
Definition: aghcall.h:52
CHAR SectionHint[8]
Optional section name hint.
Definition: winguest.h:90
WIN_OPAQUE_FIELDS OsSpecificFields
OS-dependent and specific information (variables, offsets, etc).
Definition: winguest.h:857
#define TRACE(fmt,...)
Definition: glue.h:58
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
The PT filter unloader.
Definition: aghcall.h:55
PWIN_PROCESS_OBJECT IntWinProcFindObjectByCr3(QWORD Cr3)
Finds a process by its kernel CR3.
Definition: winprocesshp.c:195
SIZE_T ArgsLen
Length of the arguments.
Definition: winagent.h:145
#define AGENT_HCALL_FETCH_CMD
Used to get the command structure for the agent.
Definition: aghcall.h:25
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
#define AGENT_HCALL_VE
Used to get a generically piece of data inside a pre-allocated region.
Definition: aghcall.h:29
void * Name
The name of the driver.
Definition: drivers.h:54
PFUNC_AgentDeliver DeliverCallback
Delivery callback.
Definition: winagent.h:106
DWORD Tag
Agent tag.
Definition: winagent.c:158
QWORD Token3
Token used by the bootstrap code.
Definition: winagent.h:124
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
INTSTATUS IntIcFlushAddress(PINS_CACHE Cache, QWORD Gva, QWORD Cr3)
Flush entries cached from a given address.
Definition: icache.c:597
INTSTATUS IntWinAgentActivatePendingAgent(void)
Activates a pending agent that waits to be injected.
Definition: winagent.c:920
#define AGENT_HCALL_REM_TOOL
Used by the remediation tool.
Definition: intro_types.h:2117
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.
LIST_ENTRY AgentNames
List of agent names.
Definition: winagent.c:184
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
#define INT_STATUS_NO_DETOUR_EMU
Signals that no emulation is needed for this event.
Definition: introstatus.h:378
#define OFFSET_WIN_X64_JUMPBACK
Definition: winstubs.h:94
void IntWinAgentCheckIfProcessAgentAndIncrement(CHAR *ImageName, BOOLEAN *IsAgent, DWORD *Tag)
Checks if a process is an agent or not, and increments the ref count of that name.
Definition: winagent.c:3028
size_t strlcpy(char *dst, const char *src, size_t dest_size)
Definition: introcrt.c:1093
#define AGENT_FLAG_ALL_DONE
Definition: winagent.c:144
#define INT_STATUS_INVALID_PARAMETER_12
Definition: introstatus.h:95
BOOLEAN UninitPrepared
Definition: guests.h:320
#define WARNING(fmt,...)
Definition: glue.h:60
DWORD Pid
Process ID (the one used by Windows).
Definition: winprocess.h:100
#define _In_opt_z_
Definition: intro_sal.h:18
#define AGENT_HCALL_VCPUID
Used to get the ID of the current VCPU.
Definition: aghcall.h:31
#define AGENT_FLAG_ACTIVE
Definition: winagent.c:142
#define TRAMP_X86_VMCALL1
Definition: winstubs.h:47
struct _AGENT_STATE * PAGENT_STATE
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
Hyper call using INT3.
Definition: winagent.h:90
#define PAGE_SIZE
Definition: common.h:70
INTSTATUS IntVirtMemSafeWrite(QWORD Cr3, QWORD VirtualAddress, DWORD Size, void *Buffer, DWORD Ring)
Safely modify guest memory.
Definition: kernvm.c:498
enum _AGENT_TYPE AGENT_TYPE
INTSTATUS IntReleaseBuffer(void *Buffer, DWORD Size)
Definition: glue.c:1083
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define IC_TAG_IMGE
PE image buffer.
Definition: memtags.h:51
INTSTATUS IntSlackFree(QWORD Buffer)
Free slack space.
Definition: slack.c:499
DWORD AgentPosition
Current pointer inside the agent, used to track which chunk must be injected inside the guest...
Definition: winagent.h:138
LIST_ENTRY PendingAgents
List of agents waiting to be injected.
Definition: winagent.c:183
DWORD SyscallNumbers[winKmFieldSyscallNumbersEnd]
Syscall numbers needed by agents. Indexed with values from WIN_KM_FIELD_SYSCALL_NUMBERS.
Definition: winguest.h:705
void * InstructionCache
The currently used instructions cache.
Definition: guests.h:404
EVENT_AGENT_EVENT Agent
Definition: alerts.h:29
WORD OffsetJumpBack
Offset of the trampoline code which jumps back to the detoured instruction.
Definition: winagent.h:151
#define OFFSET_WIN_X86_ALLOC
Definition: winstubs.h:124
uint32_t DWORD
Definition: intro_types.h:49
DWORD KernelBufferSize
The size of the KernelBuffer.
Definition: winguest.h:852
CHAR ImageName[IMAGE_BASE_NAME_LEN]
Image base name.
Definition: winagent.c:156
#define OFFSET_WIN_X86_RELOC
Definition: winstubs.h:123
unsigned int Flags
Note used.
Definition: aghcall.h:74
BOOLEAN AgentInternal
True if the agent is internal to Introcore.
Definition: winagent.h:139
static INTSTATUS IntWinAgentFindSyscallLinkage(DWORD SyscallNumber, QWORD *LinkageAddress)
Find the address of the kernel linkage of a syscall.
Definition: winagent.c:675
INTSTATUS IntWinAgentInjectTrampoline(void)
Inject the agent trampoline inside the guest.
Definition: winagent.c:364
A binary blob of code will be injected and started in the kernel. NOT USED!
Definition: aghcall.h:49
static uint64_t __rdtsc(void)
Definition: intrinsics.h:306
WORD OffsetVmcall2
Offset to the second hyper call.
Definition: winagent.c:179
char Args[AGENT_MAX_COMMAND_LINE_LENGTH]
Command line arguments used by the injected process. It is limited to AGENT_MAX_COMMAND_LINE_LENGTH b...
Definition: aghcall.h:82
INTSTATUS IntSetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Sets the values of the guest GPRs.
Definition: introcpu.c:905
BYTE InstructionBytes[16]
Detoured instruction bytes.
Definition: winagent.h:129
#define IntDbgEnterDebugger()
Definition: introcore.h:381
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
INTSTATUS IntLdrLoadPEImage(PBYTE RawPe, DWORD RawPeSize, QWORD GuestVirtualAddress, PBYTE LoadedPe, DWORD VirtualPeSize, DWORD Flags)
Load the provided PE image at the provided guest virtual address, and return it in LoadedPe...
Definition: loader.c:670
AGENT_TYPE AgentType
Agent type.
Definition: winagent.h:102
unsigned int Version
Structure version. Check out AGENT_COMMAND_VERSION.
Definition: aghcall.h:69
PATTERN_SIGNATURE Signature
The pattern signature.
Definition: winguest.h:92
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
INTSTATUS IntLdrGetImageSizeAndEntryPoint(PBYTE RawPe, DWORD RawSize, DWORD *VirtualSize, DWORD *EntryPoint)
Returns the entry point and the virtual size for the provided module.
Definition: loader.c:11
THS_PTR_TYPE
The type of pointer to be checked.
BOOLEAN IntWinAgentIsPtrInTrampoline(QWORD Ptr, THS_PTR_TYPE Type)
Check if the provided address points inside the agent trampoline.
Definition: winagent.c:3219
#define IC_TAG_ALLOC
Memory allocation.
Definition: memtags.h:28
#define AGENT_FLAG_ALLOCATED
Definition: winagent.c:141
char Name[AGENT_MAX_AGENT_NAME_LENGTH]
The agent name. This will be the file name or the process name.
Definition: aghcall.h:79
DWORD Pid
PID of the process that will be the parent of the injected process.
Definition: winagent.h:144
AG_WAITSTATE IntWinAgentGetState(DWORD *Tag)
Gets the global agents state.
Definition: winagent.c:3245
#define OFFSET_WIN_X64_TOKEN1
Definition: winstubs.h:91
DWORD RemainingSections
The number of kernel sections not yet read into KernelBuffer.
Definition: winguest.h:854
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
The agent or the process stub reports an error.
Definition: intro_types.h:2104
LIST_ENTRY Link
List entry element.
Definition: winagent.h:100
#define AGENT_FLAG_COMPLETED
Definition: winagent.c:143
INTSTATUS IntCr3Read(DWORD CpuNumber, QWORD *Cr3Value)
Reads the value of the guest CR3.
Definition: introcpu.c:415
BYTE gWindowsBootstrapAgentx86[0x120]
Definition: winstubs.h:101
#define IC_TAG_AGNN
Agent name.
Definition: memtags.h:50
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
DWORD AgentSize
Definition: winagent.h:136
BYTE * KernelBuffer
A buffer containing the entire kernel image.
Definition: winguest.h:851
QWORD(* PFUNC_AgentDeliver)(QWORD GuestVirtualAddress, DWORD MaxSize, void *Context)
Called for VE and PT initialization.
Definition: winagent.h:77
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
WORD OffsetStop
Offset to the code chunk that stops the thread (_stop label).
Definition: winagent.c:177
void IntWinAgentRemoveEntryByAgid(DWORD Counter, DWORD *Tag)
Removes an agent name from the list of names, using the ID.
Definition: winagent.c:3171
void * BootCloakRegion
Cloak handle used to hide the bootstrap code.
Definition: winagent.h:127
#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
INTSTATUS IntWinAgentSelectBootstrapAddress(DWORD Size, QWORD *Address)
Finds an in-guest adders that can be used to host the bootstrap code.
Definition: winagent.c:2481
The Virtualization exception driver.
Definition: glueiface.h:353
DWORD IsAgent
TRUE if this is an injected agent.
Definition: winprocess.h:131
DWORD RefCount
Number of times this name has been used by agents.
Definition: winagent.c:160
Process agent. A process will be injected & started inside the guest.
Definition: aghcall.h:47
unsigned int Pid
The process PID from which to start a process agent.
Definition: aghcall.h:71
#define OFFSET_WIN_X64_THREAD
Definition: winstubs.h:86
__fn_naked void trampoline(void)
The trampoline of the agent.
Definition: deploy.c:171
#define AGENT_HCALL_MOD_BASE
Used to get the base of the module indicated by edi/rcx.
Definition: aghcall.h:27
enum _AG_WAITSTATE AG_WAITSTATE
Holds register state.
Definition: glueiface.h:30
#define IC_TAG_DRNU
Guest loaded module name buffer (Unicode)
Definition: memtags.h:11
INTSTATUS IntGetCurrentRing(DWORD CpuNumber, DWORD *Ring)
Read the current protection level.
Definition: introcpu.c:959
#define REM_SIZE(arch64)
Definition: winagent.c:138
static INTSTATUS IntWinAgentRemove(PWIN_AGENT Agent)
Removes the given agent.
Definition: winagent.c:272
#define UNREFERENCED_LOCAL_VARIABLE(V)
Definition: introdefs.h:30
WORD OffsetVmcall1
Offset to the first hyper call.
Definition: winagent.c:178
unsigned int Synched
Always FALSE for now. Will not wait for the process agent to finish.
Definition: aghcall.h:72
char CHAR
Definition: intro_types.h:56
BYTE gTrampolineZero[4096]
Just a page filled with zeros.
Definition: winagent.c:164
QWORD IntVeGetDriverAddress(void)
Gets the guest virtual address of the VE agent.
Definition: vecore.c:2200
unsigned long long * PQWORD
Definition: intro_types.h:53
static AGENT_STATE gAgentState
Definition: winagent.c:192
We have an active agent, currently injected inside the guest.
Definition: agent.h:16
QWORD Token1
Token used by the bootstrap code.
Definition: winagent.h:122
static INTSTATUS IntWinAgentRestoreState64(PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
Restore the general purpose registers state.
Definition: winagent.c:1186
DWORD Agid
Agent ID.
Definition: winagent.c:159
void * InsCloakRegion
Cloak handle used to hide the detoured instruction.
Definition: winagent.h:126
QWORD InstructionAddress
Address of the detoured instruction.
Definition: winagent.h:132
The page table filtering agent.
Definition: glueiface.h:356
INTSTATUS IntWinAgentHandleVmcall(QWORD Rip)
Handle a VMCALL that was executed inside the guest.
Definition: winagent.c:2397
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
#define OFFSET_WIN_X86_AGENT_TAG
Definition: winstubs.h:128
DWORD DriverSize
Size of the boot driver.
Definition: winagent.h:116
DWORD TrampolineSize
Size of the trampoline code.
Definition: winagent.c:175
void IntWinAgentInit(void)
Initialize the agents state.
Definition: winagent.c:3333
int strlower_utf8(char *buf, size_t len)
Definition: introcrt.c:57
File agent. A file will be dropped inside the guest.
Definition: aghcall.h:46
#define AGENT_FLAG_STARTED
Definition: winagent.c:140
size_t SIZE_T
Definition: intro_types.h:60
PFUNC_AgentInjection InjectionCallback
Injection callback.
Definition: winagent.h:105
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:83
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
#define TRAMP_X64_VMCALL2
Definition: winstubs.h:29
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68