Bitdefender Hypervisor Memory Introspection
winthread.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "winthread.h"
6 #include "alerts.h"
7 #include "crc32.h"
8 #include "guests.h"
9 #include "winpe.h"
10 #include "winprocesshp.h"
11 
23 
24 
27  _In_ DWORD CpuNumber,
28  _Out_ QWORD *EthreadAddress
29  )
42 {
44 
45  if (NULL == EthreadAddress)
46  {
48  }
49 
50  if (CpuNumber == IG_CURRENT_VCPU)
51  {
52  CpuNumber = IntGetCurrentCpu();
53  }
54 
55  if (CpuNumber >= gGuest.CpuCount)
56  {
58  }
59 
60  *EthreadAddress = 0;
61 
62  if (0 == gGuest.VcpuArray[CpuNumber].PcrGla)
63  {
64  status = IntFindKernelPcr(CpuNumber, &gGuest.VcpuArray[CpuNumber].PcrGla);
65  if (!INT_SUCCESS(status))
66  {
67  ERROR("[ERROR] IntFindKernelPcr failed: 0x%08x\n", status);
68  return status;
69  }
70  }
71 
73  {
74  TRACE("[CPU %d] Could not find a kernel KPCR, will not cache anything: 0x%016llx\n",
75  CpuNumber, gGuest.VcpuArray[CpuNumber].PcrGla);
76  gGuest.VcpuArray[CpuNumber].PcrGla = 0;
77 
78  return INT_STATUS_NOT_FOUND;
79  }
80 
81  status = IntKernVirtMemRead(gGuest.VcpuArray[CpuNumber].PcrGla + WIN_KM_FIELD(Pcr, CurrentThread),
82  gGuest.WordSize, EthreadAddress, NULL);
83 
84  return status;
85 }
86 
87 
92 #define THREADS_MAX_COUNT 65536
93 
94 
97  _In_ QWORD Eprocess,
99  _In_ QWORD Aux
100  )
112 {
113  INTSTATUS status;
114  QWORD currentThread = 0, count;
115 
116  if (NULL == Callback)
117  {
119  }
120 
121  count = 0;
122 
123  status = IntKernVirtMemFetchWordSize(Eprocess + WIN_KM_FIELD(Process, ThreadListHead), &currentThread);
124  if (!INT_SUCCESS(status))
125  {
126  ERROR("[ERROR] IntKernVirtMemFetchQword failed for VA 0x%016llx: 0x%08x\n", Eprocess, status);
127  return status;
128  }
129 
130  // parse the threads and show their addresses for now
131  while ((currentThread != Eprocess + WIN_KM_FIELD(Process, ThreadListHead)) &&
132  (count++ < THREADS_MAX_COUNT))
133  {
134  QWORD ethreadAddress;
135 
136  ethreadAddress = currentThread - WIN_KM_FIELD(Thread, ThreadListEntry);
137 
138  status = Callback(ethreadAddress, Aux);
139  if (INT_STATUS_BREAK_ITERATION == status)
140  {
141  status = INT_STATUS_SUCCESS;
142  goto _cleanup_and_exit;
143  }
144  else if (!INT_SUCCESS(status))
145  {
146  ERROR("[ERROR] Callback failed for thread 0x%016llx, proc 0x%016llx: 0x%08x\n",
147  ethreadAddress, Eprocess, status);
148  }
149 
150  status = IntKernVirtMemFetchWordSize(currentThread, &currentThread);
151  if (!INT_SUCCESS(status))
152  {
153  ERROR("[ERROR] Failed getting the next thread: 0x%08x\n", status);
154  break;
155  }
156  }
157 
158 _cleanup_and_exit:
159 
160  if (count >= THREADS_MAX_COUNT)
161  {
162  status = INT_STATUS_NOT_SUPPORTED;
163  }
164 
165  return status;
166 }
167 
168 
169 INTSTATUS
171  _In_ IG_CS_RING CurrentRing,
172  _In_ IG_CS_TYPE CsType,
173  _Out_ QWORD *Tib
174  )
188 {
189  INTSTATUS status;
190 
191  if (NULL == Tib)
192  {
194  }
195 
196  *Tib = 0;
197 
198  if (IG_CS_TYPE_64B == CsType && !gGuest.Guest64)
199  {
201  }
202 
203  if (IG_CS_RING_3 == CurrentRing)
204  {
205  // User mode context, simply read FS/GS
206  if (IG_CS_TYPE_64B == CsType)
207  {
208  status = IntGsRead(IG_CURRENT_VCPU, Tib);
209  }
210  else
211  {
212  status = IntFsRead(IG_CURRENT_VCPU, Tib);
213  }
214  if (!INT_SUCCESS(status))
215  {
216  ERROR("[ERROR] IntGs/FsRead failed: 0x%08x\n", status);
217  return status;
218  }
219  }
220  else
221  {
222  // Kernel mode context
223  if (IG_CS_TYPE_32B == CsType && gGuest.Guest64)
224  {
225  // 32-bit thread on 64-bit system, simply read FS
226  status = IntFsRead(IG_CURRENT_VCPU, Tib);
227  if (!INT_SUCCESS(status))
228  {
229  ERROR("[ERROR] IntGsRead failed: 0x%08x\n", status);
230  return status;
231  }
232  }
233  else
234  {
235  // 32-bit thread on 32-bit system, or 64-bit thread on 64-bit system, we need to read it from the KTHREAD
236  QWORD currentEthread = 0;
237  status = IntWinThrGetCurrentThread(gVcpu->Index, &currentEthread);
238  if (!INT_SUCCESS(status))
239  {
240  ERROR("[ERROR] IntWinThrGetCurrentThread failed: 0x%08x\n", status);
241  return status;
242  }
243 
244  status = IntKernVirtMemRead(currentEthread + WIN_KM_FIELD(Thread, Teb), gGuest.WordSize, Tib, NULL);
245  if (!INT_SUCCESS(status))
246  {
247  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%016llx: 0x%08x\n",
248  currentEthread + WIN_KM_FIELD(Thread, Teb), status);
249  return status;
250  }
251  }
252  }
253 
254  return INT_STATUS_SUCCESS;
255 }
256 
257 
258 INTSTATUS
260  _In_ QWORD Tib,
261  _In_ IG_CS_TYPE CsType,
262  _In_ QWORD Cr3,
263  _Out_ QWORD *StackBase,
264  _Out_ QWORD *StackLimit
265  )
277 {
278  const DWORD size = IG_CS_TYPE_64B == CsType ? 8 : 4;
279  INTSTATUS status;
280  QWORD buffer[2] = { 0 };
281 
282  if (NULL == StackBase)
283  {
285  }
286 
287  if (NULL == StackLimit)
288  {
290  }
291 
292  // Read the stack base & limit. They are at offset 4 on 32 bit on 8 on 64 bit.
293  status = IntVirtMemRead(Tib + size, size * 2, Cr3, buffer, NULL);
294  if ((INT_STATUS_PAGE_NOT_PRESENT == status) ||
296  {
297  return status;
298  }
299  else if (!INT_SUCCESS(status))
300  {
301  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
302  return status;
303  }
304 
305  if (4 == size)
306  {
307  *StackBase = ((DWORD *)&buffer)[0];
308  *StackLimit = ((DWORD *)&buffer)[1];
309  }
310  else
311  {
312  *StackBase = buffer[0];
313  *StackLimit = buffer[1];
314  }
315 
316  return INT_STATUS_SUCCESS;
317 }
318 
319 
320 INTSTATUS
322  _Out_ QWORD *TibBase,
323  _Out_ QWORD *StackBase,
324  _Out_ QWORD *StackLimit
325  )
336 {
337  INTSTATUS status;
338  QWORD tibBase, buffer[2] = { 0 };
339  DWORD csType;
340  IG_ARCH_REGS regs;
341  BYTE size;
342  DWORD ring;
343 
344  if (NULL == TibBase)
345  {
347  }
348 
349  if (NULL == StackBase)
350  {
352  }
353 
354  if (NULL == StackLimit)
355  {
357  }
358 
359  tibBase = 0;
360  csType = 0;
361 
362  status = IntGetGprs(IG_CURRENT_VCPU, &regs);
363  if (!INT_SUCCESS(status))
364  {
365  ERROR("[ERROR] IntGetGprs failed: 0x%08x\n", status);
366  return status;
367  }
368 
369  // Get the CS type for this CPU (we need it in order to see if it's in 16 bit, 32 bit or 64 bit).
370  status = IntGetCurrentMode(gVcpu->Index, &csType);
371  if (!INT_SUCCESS(status))
372  {
373  ERROR("[ERROR] IntGetCurrentMode failed: 0x%08x\n", status);
374  return status;
375  }
376 
377  status = IntGetCurrentRing(gVcpu->Index, &ring);
378  if (!INT_SUCCESS(status))
379  {
380  ERROR("[ERROR] IntGetCurrentRing failed: 0x%08x\n", status);
381  return status;
382  }
383 
384  size = csType == IG_CS_TYPE_64B ? 8 : 4;
385 
386  status = IntWinThrGetCurrentTib(ring, csType, &tibBase);
387  if (!INT_SUCCESS(status))
388  {
389  ERROR("[ERROR] IntWinThrGetCurrentTib failed: 0x%08x\n", status);
390  return status;
391  }
392 
393  *TibBase = tibBase;
394 
395  if (0 == tibBase)
396  {
397  ERROR("[ERROR] TIB base is 0!\n");
398  return INT_STATUS_NOT_FOUND;
399  }
400 
401  // Read the stack base & limit. They are at offset 4 on 32 bit on 8 on 64 bit.
402  status = IntVirtMemRead(tibBase + size, size * 2, regs.Cr3, buffer, NULL);
403  if ((INT_STATUS_PAGE_NOT_PRESENT == status) || (INT_STATUS_NO_MAPPING_STRUCTURES == status))
404  {
405  return status;
406  }
407  else if (!INT_SUCCESS(status))
408  {
409  ERROR("[ERROR] IntVirtMemRead failed: 0x%08x\n", status);
410  return status;
411  }
412 
413  if (4 == size)
414  {
415  *StackBase = ((DWORD *)&buffer)[0];
416  *StackLimit = ((DWORD *)&buffer)[1];
417  }
418  else
419  {
420  *StackBase = buffer[0];
421  *StackLimit = buffer[1];
422  }
423 
424  return INT_STATUS_SUCCESS;
425 }
426 
427 
428 INTSTATUS
430  _In_ void *Detour
431  )
447 {
448  INTSTATUS status;
449  PIG_ARCH_REGS regs;
450  QWORD kthreadOriginator, eprocessOriginator;
451  QWORD kthreadVictim, eprocessVictim;
452  QWORD dstAddress, rip;
453  QWORD args[2];
454  PWIN_PROCESS_OBJECT pProcOrig, pProcVictim;
455  EXCEPTION_UM_ORIGINATOR originator = { 0 };
456  EXCEPTION_VICTIM_ZONE victim = { 0 };
457  INTRO_ACTION action;
458  INTRO_ACTION_REASON reason;
459  union
460  {
461  CONTEXT64 threadContext64;
462  CONTEXT32 threadContext32;
463  } threadContext = { 0 };
464  BOOLEAN bIsDumpValid = TRUE;
465 
466  UNREFERENCED_PARAMETER(Detour);
467 
468  eprocessOriginator = eprocessVictim = kthreadVictim = dstAddress = rip = 0;
469  pProcOrig = pProcVictim = NULL;
470  action = introGuestAllowed;
471  reason = introReasonUnknown;
472  regs = &gVcpu->Regs;
473 
474  kthreadOriginator = regs->Rax;
475 
476  status = IntDetGetArguments(Detour, 2, args);
477  if (!INT_SUCCESS(status))
478  {
479  ERROR("[ERROR] IntDetGetArguments failed: 0x%08x\n", status);
480  reason = introReasonInternalError;
481  goto cleanup_and_exit;
482  }
483 
484  kthreadVictim = args[0];
485  dstAddress = args[1];
486 
487  eprocessOriginator = kthreadOriginator + WIN_KM_FIELD(Thread, Process);
488 
489  status = IntKernVirtMemRead(eprocessOriginator, gGuest.WordSize, &eprocessOriginator, NULL);
490  if (!INT_SUCCESS(status))
491  {
492  LOG("[ERROR] IntKernVirtMemRead failed for %llx: 0x%08x\n", eprocessOriginator, status);
493  reason = introReasonInternalError;
494  goto cleanup_and_exit;
495  }
496 
497  pProcOrig = IntWinProcFindObjectByEprocess(eprocessOriginator);
498  if (pProcOrig == NULL)
499  {
500  ERROR("[ERROR] Failed to find originator with eprocess: %llx\n", eprocessOriginator);
501  reason = introReasonInternalError;
502  goto cleanup_and_exit;
503  }
504 
505  eprocessVictim = kthreadVictim + WIN_KM_FIELD(Thread, Process);
506 
507  status = IntKernVirtMemRead(eprocessVictim, gGuest.WordSize, &eprocessVictim, NULL);
508  if (!INT_SUCCESS(status))
509  {
510  ERROR("[ERROR] IntKernVirtMemRead failed for %llx: 0x%08x\n", eprocessVictim, status);
511  reason = introReasonInternalError;
512  goto cleanup_and_exit;
513  }
514 
515  pProcVictim = IntWinProcFindObjectByEprocess(eprocessVictim);
516  if (pProcVictim == NULL)
517  {
518  ERROR("[ERROR] Failed to find victim with eprocess: %llx\n", eprocessVictim);
519  reason = introReasonInternalError;
520  goto cleanup_and_exit;
521  }
522 
523  if (pProcOrig->EprocessAddress == pProcVictim->EprocessAddress)
524  {
525  TRACE("[THREAD HIJACK] Hijack detected in same process, will allow...\n");
526  reason = introReasonAllowed;
527  goto cleanup_and_exit;
528  }
529 
530  if (!pProcVictim->Protected || !pProcVictim->ProtThreadCtx)
531  {
532  reason = introReasonAllowed;
533  goto cleanup_and_exit;
534  }
535 
536  if (gGuest.Guest64 && !pProcOrig->Wow64Process)
537  {
538  memzero(&threadContext.threadContext64, sizeof(CONTEXT64));
539 
540  status = IntVirtMemRead(dstAddress, sizeof(CONTEXT64), pProcOrig->Cr3, &threadContext.threadContext64, NULL);
541  if (!INT_SUCCESS(status))
542  {
543  ERROR("[ERROR] IntVirtMemRead failed for %llx: 0x%08x\n", dstAddress, status);
544  bIsDumpValid = FALSE;
545  }
546 
547  rip = threadContext.threadContext64.Rip;
548  }
549  else
550  {
551  memzero(&threadContext.threadContext32, sizeof(CONTEXT32));
552 
553  status = IntVirtMemRead(dstAddress, sizeof(CONTEXT32), pProcOrig->Cr3, &threadContext.threadContext32, NULL);
554  if (!INT_SUCCESS(status))
555  {
556  ERROR("[ERROR] IntVirtMemRead failed for %llx: 0x%08x\n", dstAddress, status);
557  bIsDumpValid = FALSE;
558  }
559 
560  rip = threadContext.threadContext32.Eip;
561  }
562 
564 
565  memzero(&originator, sizeof(originator));
566  memzero(&victim, sizeof(victim));
567 
568  status = IntExceptUserGetOriginator(pProcOrig, FALSE, 0, NULL, &originator);
569  if (!INT_SUCCESS(status))
570  {
571  ERROR("[ERROR] IntExceptUserGetOriginator failed: 0x%08x\n", status);
572  reason = introReasonInternalError;
573  action = introGuestNotAllowed;
574  goto send_notification;
575  }
576 
577  // We put length 1 because of the following reason: on SetContextThread injections the length of the "injected"
578  // buffer should be 0 but 0 won't match on delta exports checks (it will check RVA + length - 1 > delta), and it
579  // will not match as it is a comparison of 2 DWORDS, so we put 1 as a "dummy" access size so that we avoid this
580  // problem.
581  status = IntExceptGetVictimProcess(pProcVictim, rip, 1, ZONE_PROC_THREAD_CTX | ZONE_WRITE, &victim);
582  if (!INT_SUCCESS(status))
583  {
584  ERROR("[ERROR] IntExceptGetModifiedProcess failed: 0x%08x\n", status);
585  reason = introReasonInternalError;
586  action = introGuestNotAllowed;
587  goto send_notification;
588  }
589 
590  IntExcept(&victim, &originator, exceptionTypeUm, &action, &reason, introEventInjectionViolation);
591 
592 send_notification:
594 
595  if (IntPolicyProcTakeAction(PROC_OPT_PROT_SET_THREAD_CTX, pProcVictim, &action, &reason))
596  {
598 
599  memzero(pInjEvent, sizeof(*pInjEvent));
600 
601  LOG("[THREAD HIJACK] Thread Hijack detected from KTHREAD: %llx, Process `%s` (pid = %d) into KTHREAD: "
602  "%llx from Process `%s` (pid = %d)\n",
603  kthreadOriginator, pProcOrig->Name, pProcOrig->Pid, kthreadVictim, pProcVictim->Name, pProcVictim->Pid);
604 
605  if (gGuest.Guest64 && !pProcOrig->Wow64Process)
606  {
607  if (!bIsDumpValid)
608  {
609  pInjEvent->DumpValid = FALSE;
610  }
611  else
612  {
613  LOG("Dumping CONTEXT registers %llx...", dstAddress);
614  LOG("ContextFlags: %d\n", threadContext.threadContext64.ContextFlags);
615  LOG("Rax: 0x%08llx Rbx: 0x%08llx Rcx: 0x%08llx Rdx: 0x%08llx Rsp: 0x%08llx Rbp: 0x%08llx Rsi: 0x%08llx"
616  "Rdi: 0x%08llx R8: 0x%08llx R9: 0x%08llx R10: 0x%08llx R11: 0x%08llx R12: 0x%08llx R13: 0x%08llx "
617  "R14: 0x%08llx "
618  "R15: 0x%08llx Rip: 0x%08llx\n", threadContext.threadContext64.Rax,
619  threadContext.threadContext64.Rbx, threadContext.threadContext64.Rcx,
620  threadContext.threadContext64.Rdx, threadContext.threadContext64.Rsp,
621  threadContext.threadContext64.Rbp, threadContext.threadContext64.Rsi,
622  threadContext.threadContext64.Rdi, threadContext.threadContext64.R8,
623  threadContext.threadContext64.R9, threadContext.threadContext64.R10,
624  threadContext.threadContext64.R11, threadContext.threadContext64.R12,
625  threadContext.threadContext64.R13, threadContext.threadContext64.R14,
626  threadContext.threadContext64.R15, threadContext.threadContext64.Rip);
627 
628  memcpy(&pInjEvent->RawDump,
629  &threadContext.threadContext64,
630  MIN((OFFSET_OF(CONTEXT64, Rip) + sizeof(QWORD)), sizeof(pInjEvent->RawDump)));
631 
632  pInjEvent->DumpValid = TRUE;
633 
634  pInjEvent->CopySize = MIN((OFFSET_OF(CONTEXT64, Rip) + sizeof(QWORD)), sizeof(pInjEvent->RawDump));
635 
636  IntDumpBuffer(pInjEvent->RawDump,
637  0,
638  (OFFSET_OF(CONTEXT64, Rip) - OFFSET_OF(CONTEXT64, Rax) + sizeof(QWORD)),
639  16,
640  sizeof(BYTE),
641  0,
642  0);
643  }
644  }
645  else
646  {
647  if (!bIsDumpValid)
648  {
649  pInjEvent->DumpValid = FALSE;
650  }
651  else
652  {
653  LOG("Dumping CONTEXT registers %llx...", dstAddress);
654  LOG("ContextFlags: %d\n", threadContext.threadContext32.ContextFlags);
655  LOG("Eax: 0x%08x Ebx: 0x%08x Ecx: 0x%08x Edx: 0x%08x Esp: 0x%08x Ebp: 0x%08x Esi: 0x%08x Edi: 0x%08x "
656  "Eip: 0x%08x\n", threadContext.threadContext32.Eax, threadContext.threadContext32.Ebx,
657  threadContext.threadContext32.Ecx, threadContext.threadContext32.Edx,
658  threadContext.threadContext32.Esp, threadContext.threadContext32.Ebp,
659  threadContext.threadContext32.Esi, threadContext.threadContext32.Edi,
660  threadContext.threadContext32.Eip);
661 
662  memcpy(&pInjEvent->RawDump,
663  &threadContext.threadContext32,
664  MIN((OFFSET_OF(CONTEXT32, ExtendedRegisters) - OFFSET_OF(CONTEXT32, ContextFlags)),
665  sizeof(pInjEvent->RawDump)));
666 
667  pInjEvent->DumpValid = TRUE;
668 
669  pInjEvent->CopySize = MIN((OFFSET_OF(CONTEXT32, ExtendedRegisters) -
670  OFFSET_OF(CONTEXT32, ContextFlags)), sizeof(pInjEvent->RawDump));
671 
672  IntDumpBuffer(pInjEvent->RawDump,
673  0,
674  (OFFSET_OF(CONTEXT32, ExtendedRegisters) - OFFSET_OF(CONTEXT32, ContextFlags)),
675  16,
676  sizeof(BYTE),
677  0,
678  0);
679  }
680  }
681 
682  pInjEvent->Header.Action = action;
683  pInjEvent->Header.Reason = reason;
684  pInjEvent->Header.MitreID = idProcInject;
685 
687  IntAlertFillWinProcess(pProcOrig, &pInjEvent->Originator.Process);
688  IntAlertFillWinProcess(pProcVictim, &pInjEvent->Victim.Process);
690 
691  if (victim.Object.Library.Module)
692  {
694  }
695 
696  if (victim.Object.Library.Export != NULL)
697  {
698  WIN_PROCESS_MODULE *pModule = victim.Object.Library.Module;
699  WINUM_CACHE_EXPORT *pExport = victim.Object.Library.Export;
700 
701  for (DWORD export = 0; export < pExport->NumberOfOffsets; export++)
702  {
703  strlcpy(pInjEvent->Export.Name[export], pExport->Names[export],
704  MIN(ALERT_MAX_FUNCTION_NAME_LEN, pExport->NameLens[export] + 1));
705  pInjEvent->Export.Hash[export] = Crc32Compute(pExport->Names[export],
706  pExport->NameLens[export], INITIAL_CRC_VALUE);
707  }
708 
709  strlcpy(pInjEvent->FunctionName, pExport->Names[0],
710  MIN(ALERT_MAX_FUNCTION_NAME_LEN, pExport->NameLens[0] + 1));
711  pInjEvent->FunctionNameHash = Crc32Compute(pExport->Names[0],
712  pExport->NameLens[0], INITIAL_CRC_VALUE);
713 
714  if (pModule != NULL)
715  {
716  DWORD writeRva = (DWORD)(dstAddress - pModule->VirtualBase);
717  pInjEvent->Export.Delta = writeRva - victim.Object.Library.Export->Rva;
718  }
719  }
720 
721  pInjEvent->Header.Flags = IntAlertProcGetFlags(PROC_OPT_PROT_SET_THREAD_CTX, pProcVictim, reason, 0);
722 
723  // Set the internal information
724  pInjEvent->DestinationVirtualAddress = kthreadVictim;
725  pInjEvent->SourceVirtualAddress = kthreadOriginator;
726 
728 
729  IntAlertFillVersionInfo(&pInjEvent->Header);
730 
731  status = IntNotifyIntroEvent(introEventInjectionViolation, pInjEvent, sizeof(*pInjEvent));
732  if (!INT_SUCCESS(status))
733  {
734  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
735  }
736 
737  status = INT_STATUS_SUCCESS;
738  }
739 
740 cleanup_and_exit:
741  if (NULL != pProcVictim)
742  {
744  }
745 
746  status = IntDetSetReturnValue(Detour, regs,
748  if (!INT_SUCCESS(status))
749  {
750  ERROR("[ERROR] IntDetSetReturnValue failed: 0x%08x\n", status);
751  }
752 
753  return status;
754 }
755 
756 
757 INTSTATUS
759  _In_ void *Detour
760  )
777 {
778  INTSTATUS status;
779  INTRO_ACTION action;
780  QWORD ethreadOriginator;
781  QWORD eprocessOriginator;
782  QWORD victimThread;
783  QWORD eprocessVictim;
784  INTRO_ACTION_REASON reason;
785  EXCEPTION_UM_ORIGINATOR originator;
786  EXCEPTION_VICTIM_ZONE victim;
787  QWORD rip;
788  QWORD functionAddr, functionParameter;
789  QWORD args[4];
790  WIN_PROCESS_OBJECT *pOrigProc, *pVictimProc;
791  WIN_PROCESS_MODULE *pMod;
792  WINUM_CACHE_EXPORT *currentExport;
793 
794  action = introGuestAllowed;
795  reason = introReasonAllowed;
796  pVictimProc = pOrigProc = NULL;
797  pMod = NULL;
798  currentExport = NULL;
799  rip = functionAddr = functionParameter = 0;
800 
801  status = IntDetGetArguments(Detour, 4, args);
802  if (!INT_SUCCESS(status))
803  {
804  ERROR("[ERROR] IntDetGetArguments failed: 0x%08x\n", status);
805  goto cleanup_and_exit;
806  }
807 
808  victimThread = args[0];
809  functionAddr = args[1];
810  functionParameter = args[2];
811  ethreadOriginator = args[3];
812 
813  eprocessOriginator = ethreadOriginator + WIN_KM_FIELD(Thread, Process);
814 
815  status = IntKernVirtMemRead(eprocessOriginator, gGuest.WordSize, &eprocessOriginator, NULL);
816  if (!INT_SUCCESS(status))
817  {
818  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
819  goto cleanup_and_exit;
820  }
821 
822  pOrigProc = IntWinProcFindObjectByEprocess(eprocessOriginator);
823  if (pOrigProc == NULL)
824  {
825  LOG("IntWinProcFindObjectByEprocess failed for originator! \n");
826  status = INT_STATUS_NOT_FOUND;
827  goto cleanup_and_exit;
828  }
829 
830  eprocessVictim = victimThread + WIN_KM_FIELD(Thread, Process);
831 
832  status = IntKernVirtMemRead(eprocessVictim, gGuest.WordSize, &eprocessVictim, NULL);
833  if (!INT_SUCCESS(status))
834  {
835  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
836  goto cleanup_and_exit;
837  }
838 
839  pVictimProc = IntWinProcFindObjectByEprocess(eprocessVictim);
840  if (pVictimProc == NULL)
841  {
842  LOG("IntWinProcFindObjectByEprocess failed for victim! \n");
843  status = INT_STATUS_NOT_FOUND;
844  goto cleanup_and_exit;
845  }
846 
847  if (!pVictimProc->Protected || !pVictimProc->ProtQueueApc)
848  {
849  goto cleanup_and_exit;
850  }
851 
852  if (eprocessVictim == eprocessOriginator)
853  {
854  goto cleanup_and_exit;
855  }
856 
858 
859  memzero(&originator, sizeof(originator));
860  memzero(&victim, sizeof(victim));
861 
862  status = IntExceptUserGetOriginator(pOrigProc, FALSE, 0, NULL, &originator);
863  if (!INT_SUCCESS(status))
864  {
865  ERROR("[ERROR] IntExceptUserGetOriginator failed: 0x%08x\n", status);
866  reason = introReasonInternalError;
867  goto send_notification;
868  }
869 
870  // wow64!NtQueueApcThread will pass to ntdll!NtQueueApcThread the "target function" parameter into EDX
871  // (which in kernel mode will be moved into r8) as (-Target) << 2.
872  // non-wow64 processes should call RtlQueueApcWow64Thread for a wow64 target. This will also do the
873  // targetFunction << 2 * (-1) before actually doing the syscall.
874  if (pOrigProc->Wow64Process || pVictimProc->Wow64Process)
875  {
876  rip = (functionAddr * (-1)) >> 2;
877  }
878  else
879  {
880  rip = functionAddr;
881  }
882 
883  // Calling QueueUserAPC (documented function which will let you queue an APC on a function with 1 parameter)
884  // will cause the following mechanism:
885  // 1. Call ntdll!NtQueueApcThread with the following params: param1 (function address): RtlDispatchApc,
886  // param2 (first function param): real function address
887  // param3 the parameter passed to the real function from the QueueUserAPC call
888  // 2. The kernel will then give control to RtlDispatchApc by delivering an APC
889  // 3. RtlDispatchApc takes at least 2 parameters: the function to call and the parameter passed to the function
890  // to call
891  // 4. RtlDispatchApc will call the function given by first parameter with the second parameter given as a parameter
892  // to the function
893  // So, the real function will actually be the next parameter passed to NtQueueApcThreadEx
894  pMod = IntWinUmModFindByAddress(pVictimProc, rip);
895  if (pMod == NULL)
896  {
897  goto not_rtl_dispatch;
898  }
899 
900  currentExport = IntWinUmModCacheExportFind(pMod, (DWORD)(rip - pMod->VirtualBase), 0);
901  if (currentExport == NULL)
902  {
903  goto not_rtl_dispatch;
904  }
905 
906  // Search for export RtlDispatchApc. Note that there might be multiple export names on one export
907  // rva, therefore we must verify each name for the current cached export.
908  for (DWORD i = 0; i < currentExport->NumberOfOffsets; i++)
909  {
910  if (0 == strncasecmp(currentExport->Names[i], "RtlDispatchApc", currentExport->NameLens[i]))
911  {
912  // This means that the function parameter is the actual RIP which the function RtlDispatchApc
913  // will dispatch to (most probably a call from kernel32!QueueUserAPC).
914  rip = functionParameter;
915  break;
916  }
917  }
918 
919 not_rtl_dispatch:
920  // We put length 1 because of the following reason: on APC injections the length of the "injected" buffer should be
921  // 0, but 0 won't match on delta exports checks (it will check RVA + length - 1 > delta), and it will not match as
922  // it is a comparison of 2 DWORDS, so we put 1 as a "dummy" access size so that we avoid this problem.
923  status = IntExceptGetVictimProcess(pVictimProc, rip, 1, ZONE_PROC_THREAD_APC | ZONE_WRITE, &victim);
924  if (!INT_SUCCESS(status))
925  {
926  ERROR("[ERROR] IntExceptGetModifiedProcess failed: 0x%08x\n", status);
927  reason = introReasonInternalError;
928  goto send_notification;
929  }
930 
931  IntExcept(&victim, &originator, exceptionTypeUm, &action, &reason, introEventInjectionViolation);
932 
933 send_notification:
935 
936  if (IntPolicyProcTakeAction(PROC_OPT_PROT_QUEUE_APC, pVictimProc, &action, &reason))
937  {
939 
940  memzero(pInjEvent, sizeof(*pInjEvent));
941 
942  LOG("[APC HIJACKING] From process '%s' into process '%s' (%llx [%llx] -> %llx [%llx]) to rip %llx\n",
943  pOrigProc->Name, pVictimProc->Name, eprocessOriginator, ethreadOriginator,
944  eprocessVictim, victimThread, rip);
945 
946  pInjEvent->Header.Action = action;
947  pInjEvent->Header.Reason = reason;
948  pInjEvent->Header.MitreID = idProcInject;
949 
950  status = IntVirtMemRead(rip, sizeof(pInjEvent->RawDump), pVictimProc->Cr3, pInjEvent->RawDump, NULL);
951  if (!INT_SUCCESS(status))
952  {
953  WARNING("[WARNING] IntVirtMemRead failed: 0x%08x\n", status);
954  pInjEvent->DumpValid = FALSE;
955  }
956  else
957  {
958  pInjEvent->DumpValid = TRUE;
959  pInjEvent->CopySize = sizeof(pInjEvent->RawDump);
960 
961  IntDumpBuffer(pInjEvent->RawDump, 0, sizeof(pInjEvent->RawDump), 16, sizeof(BYTE), 0, 0);
962  }
963 
965  IntAlertFillWinProcess(pOrigProc, &pInjEvent->Originator.Process);
966  IntAlertFillWinProcess(pVictimProc, &pInjEvent->Victim.Process);
968 
969  if (victim.Object.Library.Module)
970  {
972  }
973 
974  if (victim.Object.Library.Export != NULL)
975  {
976  WIN_PROCESS_MODULE *pModule = victim.Object.Library.Module;
977  WINUM_CACHE_EXPORT *pExport = victim.Object.Library.Export;
978 
979  for (DWORD export = 0; export < pExport->NumberOfOffsets; export++)
980  {
981  strlcpy(pInjEvent->Export.Name[export], pExport->Names[export],
982  MIN(ALERT_MAX_FUNCTION_NAME_LEN, pExport->NameLens[export] + 1));
983  pInjEvent->Export.Hash[export] = Crc32Compute(pExport->Names[export],
984  pExport->NameLens[export], INITIAL_CRC_VALUE);
985  }
986 
987  strlcpy(pInjEvent->FunctionName, pExport->Names[0],
988  MIN(ALERT_MAX_FUNCTION_NAME_LEN, pExport->NameLens[0] + 1));
989  pInjEvent->FunctionNameHash = Crc32Compute(pExport->Names[0],
990  pExport->NameLens[0], INITIAL_CRC_VALUE);
991 
992  if (pModule != NULL)
993  {
994  DWORD writeRva = (DWORD)(rip - pModule->VirtualBase);
995  pInjEvent->Export.Delta = writeRva - victim.Object.Library.Export->Rva;
996  }
997  }
998 
999  pInjEvent->Header.Flags = IntAlertProcGetFlags(PROC_OPT_PROT_QUEUE_APC, pVictimProc, reason, 0);
1000 
1001  // Set the internal information
1002  pInjEvent->DestinationVirtualAddress = rip;
1003  pInjEvent->SourceVirtualAddress = victimThread;
1004 
1006 
1007  IntAlertFillVersionInfo(&pInjEvent->Header);
1008 
1009  status = IntNotifyIntroEvent(introEventInjectionViolation, pInjEvent, sizeof(*pInjEvent));
1010  if (!INT_SUCCESS(status))
1011  {
1012  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
1013  }
1014  }
1015 
1016 cleanup_and_exit:
1017 
1018  if (NULL != pVictimProc)
1019  {
1021  }
1022 
1023  status = IntDetSetReturnValue(Detour, &gVcpu->Regs,
1025  if (!INT_SUCCESS(status))
1026  {
1027  ERROR("[ERROR] IntDetSetReturnValue failed: 0x%08x\n", status);
1028  }
1029 
1030  return status;
1031 
1032 }
1033 
1034 
1035 INTSTATUS
1037  _In_ QWORD FunctionAddress,
1038  _Inout_ void *Handler,
1039  _In_ void *Descriptor
1040  )
1055 {
1056 
1057  PAPI_HOOK_HANDLER pHandler;
1058 
1059  UNREFERENCED_PARAMETER(FunctionAddress);
1060  UNREFERENCED_PARAMETER(Descriptor);
1061 
1062  pHandler = (PAPI_HOOK_HANDLER)Handler;
1063 
1064  if (!gGuest.Guest64)
1065  {
1066 
1067  *(DWORD *)(pHandler->Code + 0x8) = WIN_KM_FIELD(Thread, AttachedProcess);
1068 
1069  *(DWORD *)(pHandler->Code + 0x12) = WIN_KM_FIELD(Thread, Process);
1070 
1071  *(DWORD *)(pHandler->Code + 0x18) = WIN_KM_FIELD(Process, Spare);
1072 
1073  if (gGuest.OSVersion <= 9200 || gGuest.OSVersion == 10240 ||
1074  (gGuest.OSVersion >= 14393 && gGuest.OSVersion <= 18362))
1075  {
1076  // Patch the ret (it is retn 0x14 on these OSes)
1077  pHandler->Code[0x41] = 0x14;
1078  }
1079  else
1080  {
1081  // At offset 2 we have `mov ecx,DWORD PTR [esp+0xc]` instruction, but on 8.1 and
1082  // 10586 and seemingly on 20H1 the injected thread is in ECX
1083  // so we will patch the instruction with 4 nops
1084  *(DWORD *)(pHandler->Code + 0x2) = 0x90909090;
1085 
1086  // We have `cmp eax,DWORD PTR [esp+0xc]`, here we patch the 0xc, as the thread is at
1087  // [esp+0x0] because we have pushed ECX on the stack
1088  pHandler->Code[0x2f] = 0x00;
1089 
1090  // Finally patch the ret (it is retn 0x0C on these OSes)
1091  pHandler->Code[0x41] = 0x0C;
1092  }
1093  }
1094  else
1095  {
1096  *(DWORD *)(pHandler->Code + 0x4) = WIN_KM_FIELD(Thread, AttachedProcess);
1097 
1098  *(DWORD *)(pHandler->Code + 0x10) = WIN_KM_FIELD(Thread, Process);
1099 
1100  *(DWORD *)(pHandler->Code + 0x17) = WIN_KM_FIELD(Process, Spare);
1101  }
1102 
1103  return INT_STATUS_SUCCESS;
1104 }
1105 
1106 
1107 INTSTATUS
1109  _In_ QWORD FunctionAddress,
1110  _Inout_ void *Handler,
1111  _In_ void *Descriptor
1112  )
1128 {
1129  INTSTATUS status;
1130  PAPI_HOOK_HANDLER pHandler;
1131  QWORD *threadType, *referenceObj, *derefObj;
1132  DWORD *attachedProc, *process, *spare;
1133  DWORD *threadType32, *referenceObj32, *derefObj32;
1134  PCHAR exports[3] = { "PsThreadType", "ObReferenceObjectByHandle", "ObDereferenceObject" };
1135  QWORD gvas[3] = { 0, 0, 0 };
1136  DWORD offsetCallObReferenceObject, offsetCallObDereference,
1137  offsetPsThreadType, offsetAttachedProcess, offsetProcess, offsetSpare;
1138 
1139  UNREFERENCED_PARAMETER(FunctionAddress);
1140  UNREFERENCED_PARAMETER(Descriptor);
1141 
1142  threadType = referenceObj = derefObj = NULL;
1143  threadType32 = referenceObj32 = derefObj32 = NULL;
1144  attachedProc = process = spare = NULL;
1145 
1146  pHandler = (PAPI_HOOK_HANDLER)Handler;
1147 
1148  if (gGuest.Guest64)
1149  {
1150  offsetCallObReferenceObject = 0x3b;
1151  offsetCallObDereference = 0xa0;
1152  offsetPsThreadType = 0x11;
1153  offsetAttachedProcess = 0x5f;
1154  offsetProcess = 0x6b;
1155  offsetSpare = 0x72;
1156 
1157  threadType = (QWORD *)&pHandler->Code[offsetPsThreadType];
1158  referenceObj = (QWORD *)&pHandler->Code[offsetCallObReferenceObject];
1159  derefObj = (QWORD *)&pHandler->Code[offsetCallObDereference];
1160  attachedProc = (DWORD *)&pHandler->Code[offsetAttachedProcess];
1161  process = (DWORD *)&pHandler->Code[offsetProcess];
1162  spare = (DWORD *)&pHandler->Code[offsetSpare];
1163  }
1164  else
1165  {
1166  offsetPsThreadType = 0x1d;
1167  offsetCallObReferenceObject = 0x25;
1168  offsetCallObDereference = 0x67;
1169  offsetAttachedProcess = 0x35;
1170  offsetProcess = 0x3f;
1171  offsetSpare = 0x45;
1172 
1173  threadType32 = (DWORD *)&pHandler->Code[offsetPsThreadType];
1174  referenceObj32 = (DWORD *)&pHandler->Code[offsetCallObReferenceObject];
1175  derefObj32 = (DWORD *)&pHandler->Code[offsetCallObDereference];
1176  attachedProc = (DWORD *)&pHandler->Code[offsetAttachedProcess];
1177  process = (DWORD *)&pHandler->Code[offsetProcess];
1178  spare = (DWORD *)&pHandler->Code[offsetSpare];
1179  }
1180 
1181  for (DWORD i = 0; i < 3; i++)
1182  {
1183  status = IntPeFindKernelExport(exports[i], &gvas[i]);
1184  if (!INT_SUCCESS(status))
1185  {
1186  ERROR("[ERROR] Cannot find export %s for patching APC thread handler!\n", exports[i]);
1187  return status;
1188  }
1189  else
1190  {
1191  TRACE("[INFO] Export %s found at gva %016llx\n", exports[i], gvas[i]);
1192  }
1193  }
1194 
1195  if (gGuest.Guest64)
1196  {
1197  threadType[0] = gvas[0];
1198  referenceObj[0] = gvas[1];
1199  derefObj[0] = gvas[2];
1200  attachedProc[0] = WIN_KM_FIELD(Thread, AttachedProcess);
1201  process[0] = WIN_KM_FIELD(Thread, Process);
1202  spare[0] = WIN_KM_FIELD(Process, Spare);
1203  }
1204  else
1205  {
1206  threadType32[0] = (DWORD)gvas[0];
1207  referenceObj32[0] = (DWORD)gvas[1];
1208  derefObj32[0] = (DWORD)gvas[2];
1209  attachedProc[0] = WIN_KM_FIELD(Thread, AttachedProcess);
1210  process[0] = WIN_KM_FIELD(Thread, Process);
1211  spare[0] = WIN_KM_FIELD(Process, Spare);
1212  }
1213 
1214  TRACE("[INFO] Successfully patched NtQueueApcThreadEx handler!\n");
1215 
1216  return INT_STATUS_SUCCESS;
1217 }
void * Module
The internal structure of a module.
Definition: exceptions.h:836
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
struct _EVENT_MEMCOPY_VIOLATION::@297 Originator
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
char Name[ALERT_MAX_FUNCTIONS][ALERT_MAX_FUNCTION_NAME_LEN]
Array of all the extracted function names.
Definition: intro_types.h:1513
INTRO_MODULE Module
The module which was written or read.
Definition: intro_types.h:1451
An internal error occurred (no memory, pages not present, etc.).
Definition: intro_types.h:195
struct _API_HOOK_HANDLER * PAPI_HOOK_HANDLER
uint8_t BYTE
Definition: intro_types.h:47
#define OFFSET_OF(Type, Member)
Definition: introlists.h:33
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
#define PROC_OPT_PROT_QUEUE_APC
Blocks APC queuing inside the target process (Windows only).
Definition: intro_types.h:358
DWORD Index
The VCPU number.
Definition: guests.h:172
DWORD Crc32Compute(const void *Buffer, size_t Size, DWORD InitialCrc)
Computes the CRC for a byte array.
Definition: crc32.c:103
#define _In_
Definition: intro_sal.h:21
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1199
INTSTATUS IntPeFindKernelExport(const char *Name, QWORD *ExportGva)
Find an export inside the NT kernel image.
Definition: winpe.c:1748
INTSTATUS IntWinThrPatchThreadHijackHandler(QWORD FunctionAddress, void *Handler, void *Descriptor)
This functions is responsible for patching the detour that handles the "PspSetContextThreadInternal"...
Definition: winthread.c:1036
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTRO_PROCESS Process
The process that attempted the access.
Definition: intro_types.h:1440
INTSTATUS IntFsRead(DWORD CpuNumber, QWORD *FsValue)
Reads the IA32_FS_BASE guest MSR.
Definition: introcpu.c:252
Measures user mode exceptions checks.
Definition: stats.h:50
This represents an attempt of modifying the context of another thread.
Definition: intro_types.h:1417
CHAR FunctionName[ALERT_MAX_FUNCTION_NAME_LEN]
The name of the accessed function, if any.
Definition: intro_types.h:1499
#define WIN_STATUS_SUCCESS
Equivalent to NTSTATUS STATUS_SUCCESS.
Definition: winprocess.h:24
#define STATS_EXIT(id)
Definition: stats.h:160
INTSTATUS IntGetGprs(DWORD CpuNumber, PIG_ARCH_REGS Regs)
Get the current guest GPR state.
Definition: introcpu.c:827
void IntAlertFillWinProcess(const WIN_PROCESS_OBJECT *Process, INTRO_PROCESS *EventProcess)
Saves information about a windows process inside an alert.
Definition: alerts.c:689
User-mode exception.
Definition: exceptions.h:60
Described a detour handler.
Definition: detours.h:283
DWORD IntGetCurrentCpu(void)
Returns the current CPU number.
Definition: introcpu.c:802
DWORD NumberOfOffsets
Number of symbols pointing to the exported RVA.
Definition: winumcache.h:27
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
INTSTATUS IntDetSetReturnValue(DETOUR const *Detour, IG_ARCH_REGS *Registers, QWORD ReturnValue)
Sets the return value for a hooked guest function.
Definition: detours.c:1528
Process Injection.
Definition: intro_types.h:1146
#define PROC_OPT_PROT_SET_THREAD_CTX
Blocks thread hijacking attempts inside the target process (Windows only).
Definition: intro_types.h:354
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1198
INTSTATUS IntWinThrHandleQueueApc(void *Detour)
Handles a NtQueueApcThreadEx call - blocking process injections.Asynchronous Procedure Call (APC) inj...
Definition: winthread.c:758
The action was not allowed because there was no reason to allow it.
Definition: intro_types.h:183
DWORD Delta
The offset inside the affected function at which the access was made.
Definition: intro_types.h:1505
#define ERROR(fmt,...)
Definition: glue.h:62
Describes a user-mode originator.
Definition: exceptions.h:994
INTSTATUS IntKernVirtMemFetchWordSize(QWORD GuestVirtualAddress, void *Data)
Reads a guest pointer from the guest kernel memory.
Definition: introcore.c:847
int INTSTATUS
The status data type.
Definition: introstatus.h:24
DWORD OSVersion
Os version.
Definition: guests.h:281
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
#define ALERT_MAX_FUNCTION_NAME_LEN
The maximum size of a function name inside an alert structure.
Definition: intro_types.h:703
PVCPU_STATE VcpuArray
Array of the VCPUs assigned to this guest. The index in this array matches the VCPU number...
Definition: guests.h:372
Sent for code/data injection alerts. See EVENT_MEMCOPY_VIOLATION.
Definition: intro_types.h:96
WINUM_CACHE_EXPORT * IntWinUmModCacheExportFind(WIN_PROCESS_MODULE *Module, DWORD Rva, DWORD ErrorRange)
Tries to find an export in the range [Rva, Rva + ErrorRange].
Definition: winumcache.c:262
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
QWORD VirtualBase
Guest virtual address of the loaded module.
Definition: winummodule.h:34
IG_CS_RING
The current protection level.
Definition: glueiface.h:195
#define MIN(a, b)
Definition: introdefs.h:146
DWORD Protected
TRUE if this is a protected process. If this is FALSE, most of the above fields aren&#39;t used at all...
Definition: winprocess.h:130
#define LOG(fmt,...)
Definition: glue.h:61
32-bit selector.
Definition: glueiface.h:187
#define INT_STATUS_BREAK_ITERATION
Can be used by iteration callbacks to break the iteration early.
Definition: introstatus.h:374
void IntAlertFillVersionInfo(INTRO_VIOLATION_HEADER *Header)
Fills version information for an alert.
Definition: alerts.c:327
DWORD Wow64Process
TRUE if this is a 32 bit process on a 64 bit OS.
Definition: winprocess.h:125
QWORD IntAlertProcGetFlags(QWORD ProtectionFlag, const void *Process, INTRO_ACTION_REASON Reason, QWORD AdditionalFlags)
Returns the flags for an alert.
Definition: alerts.c:425
INTSTATUS IntWinThrHandleThreadHijack(void *Detour)
Handles a SetContextThread call - blocking thread hijacking.Thread hijacking (amongst others) is an a...
Definition: winthread.c:429
IG_CS_TYPE
The type of the code segment.
Definition: glueiface.h:183
Context Frame for 32-bit guests.
Definition: wddefs.h:1675
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
EXCEPTION_VICTIM_OBJECT Object
The modified object.
Definition: exceptions.h:895
PWIN_PROCESS_MODULE IntWinUmModFindByAddress(PWIN_PROCESS_OBJECT Process, QWORD Gva)
Searches for a user-mode module which contains the indicated guest virtual address.
Definition: winummodule.c:2304
Exposes the functions used to provide Windows Threads related support.
QWORD Cr3
Process PDBR. Includes PCID.
Definition: winprocess.h:98
INTSTATUS IntGetCurrentMode(DWORD CpuNumber, DWORD *Mode)
Read the current CS type.
Definition: introcpu.c:977
MEMCOPY_VIOLATION_TYPE ViolationType
The type of the access.
Definition: intro_types.h:1480
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
#define _Inout_
Definition: intro_sal.h:20
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1436
EVENT_MEMCOPY_VIOLATION Injection
Definition: alerts.h:21
#define INITIAL_CRC_VALUE
Definition: introdefs.h:221
INTSTATUS IntFindKernelPcr(DWORD CpuNumber, QWORD *Pcr)
Finds the address of the Windows kernel _KPCR.
Definition: introcpu.c:1116
BOOLEAN IntPolicyProcTakeAction(QWORD Flag, void const *Process, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason)
Returns the action that should be taken for a process protection option.
Definition: introcore.c:2732
DWORD CopySize
The size of the access.
Definition: intro_types.h:1477
#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
#define STATS_ENTER(id)
Definition: stats.h:153
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1196
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
#define THREADS_MAX_COUNT
The maximum number of threads for one single process (if something happens, IntWinThrIterateThreads w...
Definition: winthread.c:92
#define memzero(a, s)
Definition: introcrt.h:35
This represents an attempt to queue an APC into the victim process.
Definition: intro_types.h:1420
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
#define ZONE_PROC_THREAD_APC
Used for the APC thread hijacking technique.
Definition: exceptions.h:729
unsigned long long QWORD
Definition: intro_types.h:53
CHAR Name[IMAGE_BASE_NAME_LEN]
Process base name.
Definition: winprocess.h:108
INTSTATUS IntWinThrGetCurrentThread(DWORD CpuNumber, QWORD *EthreadAddress)
Get the ETHREAD structure address of the thread currently running on the given CPU.
Definition: winthread.c:26
BYTE Code[DETOUR_MAX_HANDLER_SIZE]
The code of the detour handler. Only CodeLength bytes are valid.
Definition: detours.h:298
#define TRUE
Definition: intro_types.h:30
INTSTATUS(* PFUNC_IterateListCallback)(QWORD Node, QWORD Aux)
Definition: introtypes.h:71
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
DWORD Hash[ALERT_MAX_FUNCTIONS]
Array of all the extracted function hashes.Export.Hash[i] is the hash for Export.Name[i].
Definition: intro_types.h:1516
#define IS_KERNEL_POINTER_WIN(is64, p)
Checks if a guest virtual address resides inside the Windows kernel address space.
Definition: wddefs.h:76
TIMER_FRIENDLY void IntDumpBuffer(const void *Buffer, QWORD Gva, DWORD Length, DWORD RowLength, DWORD ElementLength, BOOLEAN LogHeader, BOOLEAN DumpAscii)
This function dumps a given buffer in a user friendly format.
Definition: dumper.c:48
#define TRACE(fmt,...)
Definition: glue.h:58
INTSTATUS IntGsRead(DWORD CpuNumber, QWORD *GsValue)
Reads the IA32_GS_BASE guest MSR.
Definition: introcpu.c:289
#define INT_STATUS_INVALID_PARAMETER_5
Definition: introstatus.h:74
INTSTATUS IntWinThrGetCurrentStackBaseAndLimit(QWORD *TibBase, QWORD *StackBase, QWORD *StackLimit)
Obtains the stack base, stack limit and TIB address of the current thread.
Definition: winthread.c:321
Memory access violations that cross a process boundary.
Definition: intro_types.h:1434
BOOLEAN DumpValid
True if the contents of RawDump are valid, False if not.
Definition: intro_types.h:1483
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:367
size_t strlcpy(char *dst, const char *src, size_t dest_size)
Definition: introcrt.c:1093
char * PCHAR
Definition: intro_types.h:56
#define WARNING(fmt,...)
Definition: glue.h:60
INTSTATUS IntWinThrIterateThreads(QWORD Eprocess, PFUNC_IterateListCallback Callback, QWORD Aux)
Iterate all the threads of the given process and invoke the callback for each one of them...
Definition: winthread.c:96
DWORD Pid
Process ID (the one used by Windows).
Definition: winprocess.h:100
INTSTATUS IntExceptUserGetOriginator(void *Process, BOOLEAN ModuleWrite, QWORD Address, INSTRUX *Instrux, EXCEPTION_UM_ORIGINATOR *Originator)
This function is used to get the information about the user-mode originator.
DWORD ProtThreadCtx
Protect the thread context (protection against thread hijacking).
Definition: winprocess.h:211
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:279
Describes the modified zone.
Definition: exceptions.h:893
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
DWORD Rva
The RVA of this export.
Definition: winumcache.h:23
#define WIN_KM_FIELD(Structure, Field)
Macro used to access kernel mode fields inside the WIN_OPAQUE_FIELDS structure.
Definition: winguest.h:740
uint32_t DWORD
Definition: intro_types.h:49
INTSTATUS IntDetGetArguments(void const *Detour, DWORD Argc, QWORD *Argv)
Reads multiple arguments from a detour.
Definition: detours.c:2296
enum _INTRO_ACTION INTRO_ACTION
Event actions.
BOOLEAN IntPolicyProcForceBetaIfNeeded(QWORD Flag, void *Process, INTRO_ACTION *Action)
Checks if a forced action should be taken even if the process log-only mode is active.
Definition: introcore.c:2773
#define WIN_STATUS_ACCESS_DENIED
Equivalent to NTSTATUS STATUS_ACCESS_DENIED.
Definition: winprocess.h:23
DWORD NameLens[MAX_OFFSETS_PER_NAME]
Length of each name pointing to this RVA.
Definition: winumcache.h:25
QWORD Cr3
The value of the guest CR3 register when the event was generated.
Definition: intro_types.h:970
QWORD EprocessAddress
This will be the address of the ActiveProcess field.
Definition: winprocess.h:90
struct _EVENT_MEMCOPY_VIOLATION::@299 Export
All the names used to export the modified function.
DWORD FunctionNameHash
The hash of the FunctionName. It is the same as Export.Hash[0].
Definition: intro_types.h:1502
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
void IntAlertFillWinProcessByCr3(QWORD ProcessCr3, INTRO_PROCESS *EventProcess)
Saves information about a Windows process inside an alert. The process is searched by its kernel CR3...
Definition: alerts.c:756
INTSTATUS IntWinThrPrepareApcHandler(QWORD FunctionAddress, void *Handler, void *Descriptor)
This functions is responsible for patching the detour that handles the "NtQueueApcThreadEx".This function is called before the hook is placed into memory in order to "patch" the addresses of guest functions or guest file offsets that are used by the hook handler. Specifically, this patches the addresses of PsThreadType, ObReferenceObjectByHandle, ObDereferenceObject and the offsets of the AttachedProcess and Process fields of _KTHREAD and the Spare field of _KPROCESS, but also patches the "retn" instruction accordingly.
Definition: winthread.c:1108
INTSTATUS IntVirtMemRead(QWORD Gva, DWORD Length, QWORD Cr3, void *Buffer, DWORD *RetLength)
Reads data from a guest virtual memory range.
Definition: introcore.c:627
BYTE RawDump[ALERT_MAX_INJ_DUMP_SIZE]
The source buffer contents.
Definition: intro_types.h:1492
INTSTATUS IntExceptGetVictimProcess(void *Process, QWORD DestinationGva, DWORD Length, QWORD ZoneFlags, EXCEPTION_VICTIM_ZONE *Victim)
This function is used to get the information about the victim process for injection violations...
struct _EVENT_MEMCOPY_VIOLATION::@298 Victim
PWIN_PROCESS_OBJECT IntWinProcFindObjectByEprocess(QWORD Eprocess)
Finds a process by the address of its _EPROCESS structure.
Definition: winprocesshp.c:96
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1194
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
#define INT_STATUS_NO_MAPPING_STRUCTURES
Indicates that not all mapping structures of a virtual address are present.
Definition: introstatus.h:434
PCHAR Names[MAX_OFFSETS_PER_NAME]
The names pointing to this RVA. Each name will point inside the Names structure inside WINUM_CACHE_EX...
Definition: winumcache.h:31
EXCEPTION_VICTIM_MODULE Library
The victim module of the modified library.
Definition: exceptions.h:877
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1197
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
void IntAlertFillWinUmModule(const WIN_PROCESS_MODULE *Module, INTRO_MODULE *EventModule)
Fills information about a user mode module inside an alert.
Definition: alerts.c:653
64-bit selector.
Definition: glueiface.h:188
#define ZONE_PROC_THREAD_CTX
Used for the CONTEXT structure of a thread.
Definition: exceptions.h:728
Holds register state.
Definition: glueiface.h:30
INTSTATUS IntGetCurrentRing(DWORD CpuNumber, DWORD *Ring)
Read the current protection level.
Definition: introcpu.c:959
QWORD SourceVirtualAddress
The virtual address of the source buffer.
Definition: intro_types.h:1464
QWORD DestinationVirtualAddress
The virtual address of the destination buffer.
Definition: intro_types.h:1474
Context Frame for 64-bit guests.
Definition: wddefs.h:1590
QWORD PcrGla
The guest linear address of the _KPCR structure loaded by this CPU.
Definition: guests.h:108
#define ZONE_WRITE
Used for write violation.
Definition: exceptions.h:734
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
WINUM_CACHE_EXPORT * Export
The export cache for the modified module.
Definition: exceptions.h:843
INTSTATUS IntWinThrGetUmStackBaseAndLimitFromTib(QWORD Tib, IG_CS_TYPE CsType, QWORD Cr3, QWORD *StackBase, QWORD *StackLimit)
Obtains the user mode stack base and stack limit values.
Definition: winthread.c:259
void IntExcept(EXCEPTION_VICTIM_ZONE *Victim, void *Originator, EXCEPTION_TYPE Type, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason, INTRO_EVENT_TYPE EventClass)
This function is the entry point for the exception mechanism.
Definition: exceptions.c:3357
INTSTATUS IntWinThrGetCurrentTib(IG_CS_RING CurrentRing, IG_CS_TYPE CsType, QWORD *Tib)
Obtain the TIB (Thread Information Block) of the thread running on the current CPU.
Definition: winthread.c:170
#define FALSE
Definition: intro_types.h:34
This structure describes a running process inside the guest.
Definition: winprocess.h:83
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68