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  // If the the destination process is a system process, a flag will be set
724  if (pProcVictim->SystemProcess)
725  {
726  pInjEvent->Header.Flags |= ALERT_FLAG_SYSPROC;
727  }
728 
729  pInjEvent->Header.Flags |= ALERT_FLAG_NOT_RING0;
730 
731  // Set the internal information
732  pInjEvent->DestinationVirtualAddress = kthreadVictim;
733  pInjEvent->SourceVirtualAddress = kthreadOriginator;
734 
736 
737  IntAlertFillVersionInfo(&pInjEvent->Header);
738 
739  status = IntNotifyIntroEvent(introEventInjectionViolation, pInjEvent, sizeof(*pInjEvent));
740  if (!INT_SUCCESS(status))
741  {
742  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
743  }
744 
745  status = INT_STATUS_SUCCESS;
746  }
747 
748 cleanup_and_exit:
749  if (NULL != pProcVictim)
750  {
752  }
753 
754  status = IntDetSetReturnValue(Detour, regs,
756  if (!INT_SUCCESS(status))
757  {
758  ERROR("[ERROR] IntDetSetReturnValue failed: 0x%08x\n", status);
759  }
760 
761  return status;
762 }
763 
764 
765 INTSTATUS
767  _In_ void *Detour
768  )
785 {
786  INTSTATUS status;
787  INTRO_ACTION action;
788  QWORD ethreadOriginator;
789  QWORD eprocessOriginator;
790  QWORD victimThread;
791  QWORD eprocessVictim;
792  INTRO_ACTION_REASON reason;
793  EXCEPTION_UM_ORIGINATOR originator;
794  EXCEPTION_VICTIM_ZONE victim;
795  QWORD rip;
796  QWORD functionAddr, functionParameter;
797  QWORD args[4];
798  WIN_PROCESS_OBJECT *pOrigProc, *pVictimProc;
799  WIN_PROCESS_MODULE *pMod;
800  WINUM_CACHE_EXPORT *currentExport;
801 
802  action = introGuestAllowed;
803  reason = introReasonAllowed;
804  pVictimProc = pOrigProc = NULL;
805  pMod = NULL;
806  currentExport = NULL;
807  rip = functionAddr = functionParameter = 0;
808 
809  status = IntDetGetArguments(Detour, 4, args);
810  if (!INT_SUCCESS(status))
811  {
812  ERROR("[ERROR] IntDetGetArguments failed: 0x%08x\n", status);
813  goto cleanup_and_exit;
814  }
815 
816  victimThread = args[0];
817  functionAddr = args[1];
818  functionParameter = args[2];
819  ethreadOriginator = args[3];
820 
821  eprocessOriginator = ethreadOriginator + WIN_KM_FIELD(Thread, Process);
822 
823  status = IntKernVirtMemRead(eprocessOriginator, gGuest.WordSize, &eprocessOriginator, NULL);
824  if (!INT_SUCCESS(status))
825  {
826  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
827  goto cleanup_and_exit;
828  }
829 
830  pOrigProc = IntWinProcFindObjectByEprocess(eprocessOriginator);
831  if (pOrigProc == NULL)
832  {
833  LOG("IntWinProcFindObjectByEprocess failed for originator! \n");
834  status = INT_STATUS_NOT_FOUND;
835  goto cleanup_and_exit;
836  }
837 
838  eprocessVictim = victimThread + WIN_KM_FIELD(Thread, Process);
839 
840  status = IntKernVirtMemRead(eprocessVictim, gGuest.WordSize, &eprocessVictim, NULL);
841  if (!INT_SUCCESS(status))
842  {
843  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x\n", status);
844  goto cleanup_and_exit;
845  }
846 
847  pVictimProc = IntWinProcFindObjectByEprocess(eprocessVictim);
848  if (pVictimProc == NULL)
849  {
850  LOG("IntWinProcFindObjectByEprocess failed for victim! \n");
851  status = INT_STATUS_NOT_FOUND;
852  goto cleanup_and_exit;
853  }
854 
855  if (!pVictimProc->Protected || !pVictimProc->ProtQueueApc)
856  {
857  goto cleanup_and_exit;
858  }
859 
860  if (eprocessVictim == eprocessOriginator)
861  {
862  goto cleanup_and_exit;
863  }
864 
866 
867  memzero(&originator, sizeof(originator));
868  memzero(&victim, sizeof(victim));
869 
870  status = IntExceptUserGetOriginator(pOrigProc, FALSE, 0, NULL, &originator);
871  if (!INT_SUCCESS(status))
872  {
873  ERROR("[ERROR] IntExceptUserGetOriginator failed: 0x%08x\n", status);
874  reason = introReasonInternalError;
875  goto send_notification;
876  }
877 
878  // wow64!NtQueueApcThread will pass to ntdll!NtQueueApcThread the "target function" parameter into EDX
879  // (which in kernel mode will be moved into r8) as (-Target) << 2.
880  // non-wow64 processes should call RtlQueueApcWow64Thread for a wow64 target. This will also do the
881  // targetFunction << 2 * (-1) before actually doing the syscall.
882  if (pOrigProc->Wow64Process || pVictimProc->Wow64Process)
883  {
884  rip = (functionAddr * (-1)) >> 2;
885  }
886  else
887  {
888  rip = functionAddr;
889  }
890 
891  // Calling QueueUserAPC (documented function which will let you queue an APC on a function with 1 parameter)
892  // will cause the following mechanism:
893  // 1. Call ntdll!NtQueueApcThread with the following params: param1 (function address): RtlDispatchApc,
894  // param2 (first function param): real function address
895  // param3 the parameter passed to the real function from the QueueUserAPC call
896  // 2. The kernel will then give control to RtlDispatchApc by delivering an APC
897  // 3. RtlDispatchApc takes at least 2 parameters: the function to call and the parameter passed to the function
898  // to call
899  // 4. RtlDispatchApc will call the function given by first parameter with the second parameter given as a parameter
900  // to the function
901  // So, the real function will actually be the next parameter passed to NtQueueApcThreadEx
902  pMod = IntWinUmModFindByAddress(pVictimProc, rip);
903  if (pMod == NULL)
904  {
905  goto not_rtl_dispatch;
906  }
907 
908  currentExport = IntWinUmModCacheExportFind(pMod, (DWORD)(rip - pMod->VirtualBase), 0);
909  if (currentExport == NULL)
910  {
911  goto not_rtl_dispatch;
912  }
913 
914  // Search for export RtlDispatchApc. Note that there might be multiple export names on one export
915  // rva, therefore we must verify each name for the current cached export.
916  for (DWORD i = 0; i < currentExport->NumberOfOffsets; i++)
917  {
918  if (0 == strncasecmp(currentExport->Names[i], "RtlDispatchApc", currentExport->NameLens[i]))
919  {
920  // This means that the function parameter is the actual RIP which the function RtlDispatchApc
921  // will dispatch to (most probably a call from kernel32!QueueUserAPC).
922  rip = functionParameter;
923  break;
924  }
925  }
926 
927 not_rtl_dispatch:
928  // We put length 1 because of the following reason: on APC injections the length of the "injected" buffer should be
929  // 0, but 0 won't match on delta exports checks (it will check RVA + length - 1 > delta), and it will not match as
930  // it is a comparison of 2 DWORDS, so we put 1 as a "dummy" access size so that we avoid this problem.
931  status = IntExceptGetVictimProcess(pVictimProc, rip, 1, ZONE_PROC_THREAD_APC | ZONE_WRITE, &victim);
932  if (!INT_SUCCESS(status))
933  {
934  ERROR("[ERROR] IntExceptGetModifiedProcess failed: 0x%08x\n", status);
935  reason = introReasonInternalError;
936  goto send_notification;
937  }
938 
939  IntExcept(&victim, &originator, exceptionTypeUm, &action, &reason, introEventInjectionViolation);
940 
941 send_notification:
943 
944  if (IntPolicyProcTakeAction(PROC_OPT_PROT_QUEUE_APC, pVictimProc, &action, &reason))
945  {
947 
948  memzero(pInjEvent, sizeof(*pInjEvent));
949 
950  LOG("[APC HIJACKING] From process '%s' into process '%s' (%llx [%llx] -> %llx [%llx]) to rip %llx\n",
951  pOrigProc->Name, pVictimProc->Name, eprocessOriginator, ethreadOriginator,
952  eprocessVictim, victimThread, rip);
953 
954  pInjEvent->Header.Action = action;
955  pInjEvent->Header.Reason = reason;
956  pInjEvent->Header.MitreID = idProcInject;
957 
958  status = IntVirtMemRead(rip, sizeof(pInjEvent->RawDump), pVictimProc->Cr3, pInjEvent->RawDump, NULL);
959  if (!INT_SUCCESS(status))
960  {
961  WARNING("[WARNING] IntVirtMemRead failed: 0x%08x\n", status);
962  pInjEvent->DumpValid = FALSE;
963  }
964  else
965  {
966  pInjEvent->DumpValid = TRUE;
967 
968  pInjEvent->CopySize = sizeof(pInjEvent->RawDump);
969 
970  IntDumpBuffer(pInjEvent->RawDump,
971  0,
972  sizeof(pInjEvent->RawDump),
973  16,
974  sizeof(BYTE),
975  0,
976  0);
977  }
978 
980  IntAlertFillWinProcess(pOrigProc, &pInjEvent->Originator.Process);
981  IntAlertFillWinProcess(pVictimProc, &pInjEvent->Victim.Process);
983 
984  if (victim.Object.Library.Module)
985  {
987  }
988 
989  if (victim.Object.Library.Export != NULL)
990  {
991  WIN_PROCESS_MODULE *pModule = victim.Object.Library.Module;
992  WINUM_CACHE_EXPORT *pExport = victim.Object.Library.Export;
993 
994  for (DWORD export = 0; export < pExport->NumberOfOffsets; export++)
995  {
996  strlcpy(pInjEvent->Export.Name[export], pExport->Names[export],
997  MIN(ALERT_MAX_FUNCTION_NAME_LEN, pExport->NameLens[export] + 1));
998  pInjEvent->Export.Hash[export] = Crc32Compute(pExport->Names[export],
999  pExport->NameLens[export], INITIAL_CRC_VALUE);
1000  }
1001 
1002  strlcpy(pInjEvent->FunctionName, pExport->Names[0],
1003  MIN(ALERT_MAX_FUNCTION_NAME_LEN, pExport->NameLens[0] + 1));
1004  pInjEvent->FunctionNameHash = Crc32Compute(pExport->Names[0],
1005  pExport->NameLens[0], INITIAL_CRC_VALUE);
1006 
1007  if (pModule != NULL)
1008  {
1009  DWORD writeRva = (DWORD)(rip - pModule->VirtualBase);
1010  pInjEvent->Export.Delta = writeRva - victim.Object.Library.Export->Rva;
1011  }
1012  }
1013 
1014  pInjEvent->Header.Flags = IntAlertProcGetFlags(PROC_OPT_PROT_QUEUE_APC, pVictimProc, reason, 0);
1015 
1016  // If the the destination process is a system process, a flag will be set
1017  if (pVictimProc->SystemProcess)
1018  {
1019  pInjEvent->Header.Flags |= ALERT_FLAG_SYSPROC;
1020  }
1021 
1022  pInjEvent->Header.Flags |= ALERT_FLAG_NOT_RING0;
1023 
1024  // Set the internal information
1025  pInjEvent->DestinationVirtualAddress = rip;
1026  pInjEvent->SourceVirtualAddress = victimThread;
1027 
1029 
1030  IntAlertFillVersionInfo(&pInjEvent->Header);
1031 
1032  status = IntNotifyIntroEvent(introEventInjectionViolation, pInjEvent, sizeof(*pInjEvent));
1033  if (!INT_SUCCESS(status))
1034  {
1035  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
1036  }
1037  }
1038 
1039 cleanup_and_exit:
1040 
1041  if (NULL != pVictimProc)
1042  {
1044  }
1045 
1046  status = IntDetSetReturnValue(Detour, &gVcpu->Regs,
1048  if (!INT_SUCCESS(status))
1049  {
1050  ERROR("[ERROR] IntDetSetReturnValue failed: 0x%08x\n", status);
1051  }
1052 
1053  return status;
1054 
1055 }
1056 
1057 
1058 INTSTATUS
1060  _In_ QWORD FunctionAddress,
1061  _Inout_ void *Handler,
1062  _In_ QWORD HandlerAddress
1063  )
1078 {
1079 
1080  PAPI_HOOK_HANDLER pHandler;
1081 
1082  UNREFERENCED_PARAMETER(FunctionAddress);
1083  UNREFERENCED_PARAMETER(HandlerAddress);
1084 
1085  pHandler = (PAPI_HOOK_HANDLER)Handler;
1086 
1087  if (!gGuest.Guest64)
1088  {
1089 
1090  *(DWORD *)(pHandler->Code + 0x8) = WIN_KM_FIELD(Thread, AttachedProcess);
1091 
1092  *(DWORD *)(pHandler->Code + 0x12) = WIN_KM_FIELD(Thread, Process);
1093 
1094  *(DWORD *)(pHandler->Code + 0x18) = WIN_KM_FIELD(Process, Spare);
1095 
1096  if (gGuest.OSVersion <= 9200 || gGuest.OSVersion == 10240 ||
1097  (gGuest.OSVersion >= 14393 && gGuest.OSVersion <= 18362))
1098  {
1099  // Patch the ret (it is retn 0x14 on these OSes)
1100  pHandler->Code[0x41] = 0x14;
1101  }
1102  else
1103  {
1104  // At offset 2 we have `mov ecx,DWORD PTR [esp+0xc]` instruction, but on 8.1 and
1105  // 10586 and seemingly on 20H1 the injected thread is in ECX
1106  // so we will patch the instruction with 4 nops
1107  *(DWORD *)(pHandler->Code + 0x2) = 0x90909090;
1108 
1109  // We have `cmp eax,DWORD PTR [esp+0xc]`, here we patch the 0xc, as the thread is at
1110  // [esp+0x0] because we have pushed ECX on the stack
1111  pHandler->Code[0x2f] = 0x00;
1112 
1113  // Finally patch the ret (it is retn 0x0C on these OSes)
1114  pHandler->Code[0x41] = 0x0C;
1115  }
1116  }
1117  else
1118  {
1119  *(DWORD *)(pHandler->Code + 0x4) = WIN_KM_FIELD(Thread, AttachedProcess);
1120 
1121  *(DWORD *)(pHandler->Code + 0x10) = WIN_KM_FIELD(Thread, Process);
1122 
1123  *(DWORD *)(pHandler->Code + 0x17) = WIN_KM_FIELD(Process, Spare);
1124  }
1125 
1126  return INT_STATUS_SUCCESS;
1127 }
1128 
1129 
1130 INTSTATUS
1132  _In_ QWORD FunctionAddress,
1133  _Inout_ void *Handler,
1134  _In_ QWORD HandlerAddress
1135  )
1150 {
1151  INTSTATUS status;
1152  PAPI_HOOK_HANDLER pHandler;
1153  QWORD *threadType, *referenceObj, *derefObj;
1154  DWORD *attachedProc, *process, *spare;
1155  DWORD *threadType32, *referenceObj32, *derefObj32;
1156  PCHAR exports[3] = { "PsThreadType", "ObReferenceObjectByHandle", "ObDereferenceObject" };
1157  QWORD gvas[3] = { 0, 0, 0 };
1158  DWORD offsetCallObReferenceObject, offsetCallObDereference,
1159  offsetPsThreadType, offsetAttachedProcess, offsetProcess, offsetSpare;
1160 
1161  UNREFERENCED_PARAMETER(FunctionAddress);
1162  UNREFERENCED_PARAMETER(HandlerAddress);
1163 
1164  threadType = referenceObj = derefObj = NULL;
1165  threadType32 = referenceObj32 = derefObj32 = NULL;
1166  attachedProc = process = spare = NULL;
1167 
1168  pHandler = (PAPI_HOOK_HANDLER)Handler;
1169 
1170  if (gGuest.Guest64)
1171  {
1172  offsetCallObReferenceObject = 0x3b;
1173  offsetCallObDereference = 0xa0;
1174  offsetPsThreadType = 0x11;
1175  offsetAttachedProcess = 0x5f;
1176  offsetProcess = 0x6b;
1177  offsetSpare = 0x72;
1178 
1179  threadType = (QWORD *)&pHandler->Code[offsetPsThreadType];
1180  referenceObj = (QWORD *)&pHandler->Code[offsetCallObReferenceObject];
1181  derefObj = (QWORD *)&pHandler->Code[offsetCallObDereference];
1182  attachedProc = (DWORD *)&pHandler->Code[offsetAttachedProcess];
1183  process = (DWORD *)&pHandler->Code[offsetProcess];
1184  spare = (DWORD *)&pHandler->Code[offsetSpare];
1185  }
1186  else
1187  {
1188  offsetPsThreadType = 0x1d;
1189  offsetCallObReferenceObject = 0x25;
1190  offsetCallObDereference = 0x67;
1191  offsetAttachedProcess = 0x35;
1192  offsetProcess = 0x3f;
1193  offsetSpare = 0x45;
1194 
1195  threadType32 = (DWORD *)&pHandler->Code[offsetPsThreadType];
1196  referenceObj32 = (DWORD *)&pHandler->Code[offsetCallObReferenceObject];
1197  derefObj32 = (DWORD *)&pHandler->Code[offsetCallObDereference];
1198  attachedProc = (DWORD *)&pHandler->Code[offsetAttachedProcess];
1199  process = (DWORD *)&pHandler->Code[offsetProcess];
1200  spare = (DWORD *)&pHandler->Code[offsetSpare];
1201  }
1202 
1203  for (DWORD i = 0; i < 3; i++)
1204  {
1205  status = IntPeFindKernelExport(exports[i], &gvas[i]);
1206  if (!INT_SUCCESS(status))
1207  {
1208  ERROR("[ERROR] Cannot find export %s for patching APC thread handler!\n", exports[i]);
1209  return status;
1210  }
1211  else
1212  {
1213  TRACE("[INFO] Export %s found at gva %016llx\n", exports[i], gvas[i]);
1214  }
1215  }
1216 
1217  if (gGuest.Guest64)
1218  {
1219  threadType[0] = gvas[0];
1220  referenceObj[0] = gvas[1];
1221  derefObj[0] = gvas[2];
1222  attachedProc[0] = WIN_KM_FIELD(Thread, AttachedProcess);
1223  process[0] = WIN_KM_FIELD(Thread, Process);
1224  spare[0] = WIN_KM_FIELD(Process, Spare);
1225  }
1226  else
1227  {
1228  threadType32[0] = (DWORD)gvas[0];
1229  referenceObj32[0] = (DWORD)gvas[1];
1230  derefObj32[0] = (DWORD)gvas[2];
1231  attachedProc[0] = WIN_KM_FIELD(Thread, AttachedProcess);
1232  process[0] = WIN_KM_FIELD(Thread, Process);
1233  spare[0] = WIN_KM_FIELD(Process, Spare);
1234  }
1235 
1236  TRACE("[INFO] Successfully patched NtQueueApcThreadEx handler!\n");
1237 
1238  return INT_STATUS_SUCCESS;
1239 }
void * Module
The internal structure of a module.
Definition: exceptions.h:790
#define INT_STATUS_PAGE_NOT_PRESENT
Indicates that a virtual address is not present.
Definition: introstatus.h:438
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:1391
INTRO_MODULE Module
The module which was written or read.
Definition: intro_types.h:1329
struct _EVENT_MEMCOPY_VIOLATION::@290 Victim
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:347
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:1088
INTSTATUS IntPeFindKernelExport(const char *Name, QWORD *ExportGva)
Find an export inside the NT kernel image.
Definition: winpe.c:1723
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
INTRO_PROCESS Process
The process that attempted the access.
Definition: intro_types.h:1318
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:1299
CHAR FunctionName[ALERT_MAX_FUNCTION_NAME_LEN]
The name of the accessed function, if any.
Definition: intro_types.h:1377
#define WIN_STATUS_SUCCESS
Equivalent to NTSTATUS STATUS_SUCCESS.
Definition: winprocess.h:23
#define STATS_EXIT(id)
Definition: stats.h:148
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:61
Described a detour handler.
Definition: detours.h:279
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:1449
Process Injection.
Definition: intro_types.h:1035
#define PROC_OPT_PROT_SET_THREAD_CTX
Blocks thread hijacking attempts inside the target process (Windows only).
Definition: intro_types.h:343
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1087
INTSTATUS IntWinThrHandleQueueApc(void *Detour)
Handles a NtQueueApcThreadEx call - blocking process injections.Asynchronous Procedure Call (APC) inj...
Definition: winthread.c:766
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:1383
#define ERROR(fmt,...)
Definition: glue.h:62
Describes a user-mode originator.
Definition: exceptions.h:933
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:277
#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:665
PVCPU_STATE VcpuArray
Array of the VCPUs assigned to this guest. The index in this array matches the VCPU number...
Definition: guests.h:368
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:128
#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:123
struct _EVENT_MEMCOPY_VIOLATION::@289 Originator
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:1530
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1084
EXCEPTION_VICTIM_OBJECT Object
The modified object.
Definition: exceptions.h:849
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:1975
INTSTATUS IntWinThrPatchThreadHijackHandler(QWORD FunctionAddress, void *Handler, QWORD HandlerAddress)
This functions is responsible for patching the detour that handles the "PspSetContextThreadInternal"...
Definition: winthread.c:1059
Exposes the functions used to provide Windows Threads related support.
QWORD Cr3
Process PDBR. Includes PCID.
Definition: winprocess.h:96
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:1358
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:1314
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:1355
#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:141
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1085
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:1302
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:286
#define ZONE_PROC_THREAD_APC
Used for the APC thread hijacking technique.
Definition: exceptions.h:694
unsigned long long QWORD
Definition: intro_types.h:53
CHAR Name[IMAGE_BASE_NAME_LEN]
Process base name.
Definition: winprocess.h:106
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:294
#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:1394
#define IS_KERNEL_POINTER_WIN(is64, p)
Checks if a guest virtual address resides inside the Windows kernel address space.
Definition: wddefs.h:76
#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:1312
BOOLEAN DumpValid
True if the contents of RawDump are valid, False if not.
Definition: intro_types.h:1361
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:363
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:98
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.
#define ALERT_FLAG_NOT_RING0
If set, the alert was triggered in ring 1, 2 or 3.
Definition: intro_types.h:636
DWORD ProtThreadCtx
Protect the thread context (protection against thread hijacking).
Definition: winprocess.h:207
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:275
Describes the modified zone.
Definition: exceptions.h:847
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define PROC_OPT_PROT_EXPLOIT
Blocks malicious execution attempts.
Definition: intro_types.h:341
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:726
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:2164
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
struct _EVENT_MEMCOPY_VIOLATION::@291 Export
All the names used to export the modified function.
#define WIN_STATUS_ACCESS_DENIED
Equivalent to NTSTATUS STATUS_ACCESS_DENIED.
Definition: winprocess.h:22
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:884
QWORD EprocessAddress
This will be the address of the ActiveProcess field.
Definition: winprocess.h:88
#define ALERT_FLAG_SYSPROC
If set, the alert is on system process.
Definition: intro_types.h:635
DWORD FunctionNameHash
The hash of the FunctionName. It is the same as Export.Hash[0].
Definition: intro_types.h:1380
DWORD SystemProcess
TRUE if this is a system process.
Definition: winprocess.h:134
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
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 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:1370
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...
PWIN_PROCESS_OBJECT IntWinProcFindObjectByEprocess(QWORD Eprocess)
Finds a process by the address of its _EPROCESS structure.
Definition: winprocesshp.c:23
TIMER_FRIENDLY void IntDumpBuffer(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
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1083
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:831
#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:1086
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:57
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:693
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:1342
QWORD DestinationVirtualAddress
The virtual address of the destination buffer.
Definition: intro_types.h:1352
Context Frame for 64-bit guests.
Definition: wddefs.h:1445
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:698
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
WINUM_CACHE_EXPORT * Export
The export cache for the modified module.
Definition: exceptions.h:797
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:3317
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:81
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68
INTSTATUS IntWinThrPrepareApcHandler(QWORD FunctionAddress, void *Handler, QWORD HandlerAddress)
This functions is responsible for patching the detour that handles the "NtQueueApcThreadEx".This function is invoked every time "NtQueueApcThreadEx" is called (and APC has been queued) but before the actual handler IntWinThrHandleQueueApc, its purpose being to modify the hook code (see winhkhnd.h).
Definition: winthread.c:1131