Bitdefender Hypervisor Memory Introspection
lixprocess.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "lixprocess.h"
6 #include "alerts.h"
7 #include "crc32.h"
8 #include "hook.h"
9 #include "icache.h"
10 #include "lixcrash.h"
11 #include "lixfiles.h"
12 #include "lixmm.h"
13 #include "lixcred.h"
14 #include "lixvdso.h"
15 #include "kernvm.h"
16 #include "lixksym.h"
17 
18 
19 #define LIX_MM_PROT_MASK BIT(63)
20 
21 typedef struct _LIX_TASK_LOG
25 {
27 
29  DWORD Forks: 1;
30  DWORD Execs: 1;
31 
35 } LIX_TASK_LOG;
36 
37 
45 {
46 #if defined(DEBUG)
47  .KmThreads = 1,
48  .UmThreads = 1,
49  .Forks = 1,
50  .Execs = 1,
51 #endif
52 
53  .ProtUmThreads = 1,
54  .ProtForks = 1,
55  .ProtExecs = 1,
56 };
57 
58 
62 static const char *gLixTerminatingTasks[] =
63 {
64  "S90reboot",
65  "systemd-shutdown",
66  "reboot",
67  "shutdown"
68 };
69 
70 
74 static LIST_HEAD gLixTasks = LIST_HEAD_INIT(gLixTasks);
75 
79 static LIST_HEAD gLixProtectedTasks = LIST_HEAD_INIT(gLixProtectedTasks);
80 
84 static LIST_HEAD gLixTasksToProtect = LIST_HEAD_INIT(gLixTasksToProtect);
85 
86 
90 #define for_next_task(_task, _var_name) list_for_next(_task, gLixTasks, LIX_TASK_OBJECT, _var_name)
91 
95 #define for_each_task(_var_name) list_for_each(gLixTasks, LIX_TASK_OBJECT, _var_name)
96 
100 #define for_each_protected_task(_var_name) list_for_each_link(gLixProtectedTasks, LIX_TASK_OBJECT, \
101  ExploitProtProcLink, _var_name)
102 
106 #define for_each_task_to_protect(_var_name) list_for_each(gLixTasksToProtect, LIX_PROTECTED_PROCESS, _var_name)
107 
108 
112 static LIST_HEAD gLixTaskPaths = LIST_HEAD_INIT(gLixTaskPaths);
113 
117 #define for_each_path(_var_name) list_for_each(gLixTaskPaths, LIX_TASK_PATH, _var_name)
118 
119 
120 
121 char *
122 basename_s(char *path, size_t len)
131 {
132  size_t i;
133 
134  if (len == 0)
135  {
136  return path;
137  }
138 
139  for (i = len - 1; i && (path[i] != '/'); i--);
140 
141  return path + i + !!(path[i] == '/');
142 }
143 
144 
145 void
146 sanitize_path(char *path, size_t len, size_t *new_len)
154 {
155  size_t i = len - 1;
156 
157  for (; i && path[i] == '/'; i--)
158  {
159  len--;
160  path[i] = 0;
161  }
162 
163  *new_len = len;
164 }
165 
166 
167 static LIX_TASK_PATH *
169  _In_ LIX_TASK_PATH *Path
170  )
178 {
179  if (__likely(Path))
180  {
181  ++Path->RefCount;
182  }
183 
184  return Path;
185 }
186 
187 
188 static LIX_TASK_PATH *
190  _In_opt_ QWORD FileGva,
191  _In_opt_ QWORD PathGva,
192  _In_ QWORD DentryGva
193  )
210 {
211  INTSTATUS status;
212  char *path = NULL;
213  DWORD len = 0;
214  BOOLEAN allocPath = FALSE;
215 
216  for_each_path(pPath)
217  {
218  if (pPath->DentryGva == DentryGva)
219  {
220  return IntLixTaskPathGetRef(pPath);
221  }
222  }
223 
224  if (FileGva)
225  {
226  status = IntLixFileGetPath(FileGva, &path, &len);
227  if (!INT_SUCCESS(status))
228  {
229  ERROR("[ERROR] IntLixFileGetPath failed for %llx: %08x\n", FileGva, status);
230  return NULL;
231  }
232 
233  allocPath = TRUE;
234  }
235  else if (PathGva)
236  {
237  status = IntReadString(PathGva, 2, FALSE, &path, &len);
238  if (!INT_SUCCESS(status))
239  {
240  ERROR("[ERROR] IntReadString failed for %llx: %08x\n", PathGva, status);
241  return NULL;
242  }
243  }
244  else
245  {
246  return NULL;
247  }
248 
249  LIX_TASK_PATH *pPath = HpAllocWithTag(sizeof(*pPath), IC_TAG_PATH);
250  if (NULL == pPath)
251  {
252  return NULL;
253  }
254 
255  pPath->DentryGva = DentryGva;
256  pPath->RefCount = 1;
257  pPath->PathLength = len;
258 
259  if (allocPath)
260  {
261  pPath->Path = HpAllocWithTag((size_t)len + 1ull, IC_TAG_NAME);
262  if (NULL == pPath->Path)
263  {
265  return NULL;
266  }
267 
268  memcpy(pPath->Path, path, len + 1ull);
269  }
270  else
271  {
272  pPath->Path = path;
273  }
274 
275  pPath->Name = basename_s(pPath->Path, (size_t)len);
276  pPath->NameLength = strlen(pPath->Name);
277 
278  InsertTailList(&gLixTaskPaths, &pPath->Link);
279 
280  return pPath;
281 }
282 
283 
284 static LIX_TASK_PATH *
286  _In_ QWORD FileGva
287  )
295 {
296  QWORD dentry;
297 
298  INTSTATUS status = IntLixFileGetDentry(FileGva, &dentry);
299  if (!INT_SUCCESS(status))
300  {
301  ERROR("[ERROR] IntLixFileGetDentry failed for file %llx: %08x\n", FileGva, status);
302  return NULL;
303  }
304 
305  return IntLixTaskPathGetByDentry(FileGva, 0, dentry);
306 }
307 
308 
309 static LIX_TASK_PATH *
311  _In_ QWORD PathGva,
312  _In_ QWORD DentryGva
313  )
322 {
323  return IntLixTaskPathGetByDentry(0, PathGva, DentryGva);
324 }
325 
326 
327 static void
329  _Inout_ LIX_TASK_PATH **Path
330  )
339 {
340  LIX_TASK_PATH *pPath = *Path;
341 
342  if (NULL == pPath)
343  {
344  return;
345  }
346 
347  *Path = NULL;
348 
349  if (--pPath->RefCount)
350  {
351  return;
352  }
353 
354  RemoveEntryList(&pPath->Link);
355 
356  if (pPath->Path)
357  {
359  }
360 
362 }
363 
364 
365 static BOOLEAN
367  _In_ const LIX_TASK_OBJECT *Task,
368  _In_ BOOLEAN Protected
369  )
379 {
380  if (Task->KernelMode)
381  {
382  // NOTE: Protection is not set for these for now, nothing else to check
383  return gLixTaskLogLevel.KmThreads != 0;
384  }
385  else if (Task->IsThread)
386  {
387  return (gLixTaskLogLevel.UmThreads || (Protected && gLixTaskLogLevel.ProtUmThreads));
388  }
389  else if (Task->Exec)
390  {
391  return (gLixTaskLogLevel.Execs || (Protected && gLixTaskLogLevel.ProtExecs));
392  }
393  else
394  {
395  return (gLixTaskLogLevel.Forks || (Protected && gLixTaskLogLevel.ProtForks));
396  }
397 }
398 
399 
400 INTSTATUS
402  _Out_ QWORD *InitTask
403  )
416 {
417  INTSTATUS status;
418 
419  if (NULL == InitTask)
420  {
422  }
423 
424  // It should be there, except on Debian 8
425  *InitTask = IntKsymFindByName("init_task", NULL);
426  if (*InitTask)
427  {
428  return INT_STATUS_SUCCESS;
429  }
430 
431  for (QWORD startGva = gLixGuest->Layout.DataStart & PAGE_MASK;
432  startGva < gLixGuest->Layout.DataEnd;
433  startGva += PAGE_SIZE)
434  {
435  BYTE *p;
436  BOOLEAN found = FALSE;
437 
438  status = IntVirtMemMap(startGva, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &p);
439  if (!INT_SUCCESS(status))
440  {
441  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx 0x%08x\n", startGva, status);
442  continue;
443  }
444 
445  for (QWORD ts = startGva; ts < startGva + PAGE_SIZE; ts += sizeof(QWORD))
446  {
447  QWORD parent, mm, signal;
448  DWORD offset = ts & PAGE_OFFSET;
449  char comm[LIX_COMM_SIZE] = {0};
450 
451  // 1. init_task->real_parent = init_task
452  if (offset + LIX_FIELD(TaskStruct, RealParent) >= PAGE_SIZE)
453  {
454  status = IntKernVirtMemFetchQword(ts + LIX_FIELD(TaskStruct, RealParent), &parent);
455  if (!INT_SUCCESS(status))
456  {
457  ERROR("[ERROR] IntVirtMemFetchQword failed: 0x%08x\n", status);
458  continue;
459  }
460  }
461  else
462  {
463  parent = *(QWORD *)(p + offset + LIX_FIELD(TaskStruct, RealParent));
464  }
465 
466  if (parent != ts)
467  {
468  continue;
469  }
470 
471  // 2. init_task->parent = init_task
472  if (offset + LIX_FIELD(TaskStruct, Parent) >= PAGE_SIZE)
473  {
474  status = IntKernVirtMemFetchQword(ts + LIX_FIELD(TaskStruct, RealParent), &parent);
475  if (!INT_SUCCESS(status))
476  {
477  ERROR("[ERROR] IntKernVirtMemFetchQword failed: 0x%08x\n", status);
478  continue;
479  }
480  }
481  else
482  {
483  parent = *(QWORD *)(p + offset + LIX_FIELD(TaskStruct, Parent));
484  }
485 
486  if (parent != ts)
487  {
488  continue;
489  }
490 
491  // 3. init_task->mm = 0
492  if (offset + LIX_FIELD(TaskStruct, Mm) >= PAGE_SIZE)
493  {
494  status = IntKernVirtMemFetchQword(ts + LIX_FIELD(TaskStruct, Mm), &mm);
495  if (!INT_SUCCESS(status))
496  {
497  ERROR("[ERROR] IntKernVirtMemFetchQword failed for 0x%016llx: 0x%08x\n",
498  ts + LIX_FIELD(TaskStruct, Mm), status);
499  continue;
500  }
501  }
502  else
503  {
504  mm = *(QWORD *)(p + offset + LIX_FIELD(TaskStruct, Mm));
505  }
506 
507  if (0 != mm)
508  {
509  continue;
510  }
511 
512  // 4. init_task->signal = kernel_ptr
513  if (offset + LIX_FIELD(TaskStruct, Signal) >= PAGE_SIZE)
514  {
515  status = IntKernVirtMemFetchQword(ts + LIX_FIELD(TaskStruct, Signal), &signal);
516  if (!INT_SUCCESS(status))
517  {
518  ERROR("[ERROR] IntKernVirtMemFetchQword failed: 0x%08x\n", status);
519  continue;
520  }
521  }
522  else
523  {
524  signal = *(QWORD *)(p + offset + LIX_FIELD(TaskStruct, Signal));
525  }
526 
527  if (!IS_KERNEL_POINTER_LIX(signal))
528  {
529  continue;
530  }
531 
532  // 5. init_task->comm starts with 'swapper'
533  status = IntKernVirtMemRead(ts + LIX_FIELD(TaskStruct, Comm), sizeof(comm), comm, NULL);
534  if (!INT_SUCCESS(status))
535  {
536  ERROR("[ERROR] Failed reading process name @ 0x%016llx: 0x%08x\n",
537  ts + LIX_FIELD(TaskStruct, Comm), status);
538  continue;
539  }
540 
541  // Here we don't really want to include the NULL terminator because we are only interested in matching
542  // the "swapper" pattern. It may or may not transform into something like "swapper/0".
543  if (0 != memcmp(comm, "swapper", 7))
544  {
545  continue;
546  }
547 
548  TRACE("[LIXTASK] Found init_task @ 0x%016llx with name %s\n", ts, comm);
549 
550  *InitTask = ts;
551 
552  found = TRUE;
553  break;
554  }
555 
556  IntVirtMemUnmap(&p);
557 
558  if (found)
559  {
560  return INT_STATUS_SUCCESS;
561  }
562  }
563 
564  return INT_STATUS_NOT_FOUND;
565 }
566 
567 
568 static QWORD gTaskMapped = 0;
569 static BYTE *gTaskPtr1 = NULL;
570 static BYTE *gTaskPtr2 = NULL;
571 
572 
573 static INTSTATUS
575  _In_ QWORD TaskGva
576  )
588 {
589  INTSTATUS status;
590 
591  gTaskMapped = TaskGva;
592 
594  if (!INT_SUCCESS(status))
595  {
596  ERROR("[ERROR] IntVirtMemMap failed for %llx: %08x\n", gTaskMapped, status);
597  return status;
598  }
599 
600  return INT_STATUS_SUCCESS;
601 }
602 
603 
604 static INTSTATUS
606  _In_ DWORD Offset,
607  _In_ DWORD Size,
608  _Out_ void *Buffer)
609 
610 {
622  {
624  }
625 
626  // NOTE: Should we validate the offset ?! I think that's beyond the scope of this function...
627 
628  QWORD gva = gTaskMapped + Offset;
629 
630  if (PAGE_COUNT(gTaskMapped, (QWORD)Offset + Size) > 1)
631  {
632  DWORD remaining = Size;
633 
635  {
636  DWORD toRead = PAGE_REMAINING(gva);
637 
638  memcpy(Buffer, gTaskPtr1 + Offset, toRead);
639 
640  remaining -= toRead;
641  }
642 
643  if (NULL == gTaskPtr2)
644  {
645  QWORD nextPage = (gTaskMapped + PAGE_SIZE) & PAGE_MASK;
646 
647  INTSTATUS status = IntVirtMemMap(nextPage, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &gTaskPtr2);
648  if (!INT_SUCCESS(status))
649  {
650  ERROR("[ERROR] IntVirtMemMap failed for %llx: %08x\n", nextPage, status);
651  return status;
652  }
653  }
654 
655  memcpy((BYTE *)Buffer + (Size - remaining), gTaskPtr2 + ((gva + Size - remaining) & PAGE_OFFSET), remaining);
656  }
657  else
658  {
659  // The whole it's in the first page
660  memcpy(Buffer, gTaskPtr1 + Offset, Size);
661  }
662 
663  return INT_STATUS_SUCCESS;
664 }
665 
666 
667 static void
669  void
670  )
674 {
676 
677  if (gTaskPtr2)
678  {
680  }
681 
682  gTaskPtr1 = NULL;
683  gTaskPtr2 = NULL;
684 
685  gTaskMapped = 0;
686 }
687 
688 
689 static INTSTATUS
691  _In_opt_ QWORD MmStruct,
692  _In_ LIX_TASK_OBJECT *Task,
693  _In_opt_ LIX_TASK_OBJECT *Parent
694  )
718 {
719  INTSTATUS status;
720  QWORD mmGva, pgd;
721 
722  if (Task->KernelMode)
723  {
725  }
726 
727  if (0 == MmStruct)
728  {
729  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Mm), sizeof(mmGva), &mmGva);
730  if (!INT_SUCCESS(status))
731  {
732  ERROR("[ERROR] Fetching mm failed in task 0x%016llx: 0x%08x\n", Task->Gva, status);
733  return status;
734  }
735  }
736  else
737  {
738  mmGva = MmStruct;
739  }
740 
741  if (!IS_KERNEL_POINTER_LIX(mmGva))
742  {
744  }
745 
746  Task->MmGva = mmGva;
747 
748  if (Parent && Parent->MmGva == mmGva)
749  {
750  Task->Cr3 = Parent->Cr3;
751 
752  return INT_STATUS_SUCCESS;
753  }
754 
755  status = IntKernVirtMemFetchQword(mmGva + LIX_FIELD(MmStruct, Pgd), &pgd);
756  if (!INT_SUCCESS(status))
757  {
758  ERROR("[ERROR] IntKernVirtMemFetchQword failed for GVA 0x%016llx: 0x%08x\n",
759  mmGva + LIX_FIELD(MmStruct, Pgd), status);
760  goto _cleanup_and_exit;
761  }
762 
763  if (!IS_KERNEL_POINTER_LIX(pgd))
764  {
765  ERROR("[ERROR] The PGD 0x%016llx @ 0x%016llx (offset %x) is not a valid one!\n",
766  pgd, mmGva + LIX_FIELD(MmStruct, Pgd), LIX_FIELD(MmStruct, Pgd));
767 
769 
770  goto _cleanup_and_exit;
771  }
772 
773  status = IntTranslateVirtualAddress(pgd, gGuest.Mm.SystemCr3, &Task->Cr3);
774  if (!INT_SUCCESS(status))
775  {
776  ERROR("[ERROR] Failed translating PGD 0x%016llx: 0x%08x\n", pgd, status);
777  Task->Cr3 = 0;
778  }
779 
780  status = INT_STATUS_SUCCESS;
781 
782 _cleanup_and_exit:
783  if (!INT_SUCCESS(status))
784  {
785  Task->Cr3 = 0;
786  }
787 
788  return status;
789 }
790 
791 
792 INTSTATUS
794  _In_ DWORD CpuNumber,
795  _Out_ QWORD *TaskStruct
796  )
807 {
808  INTSTATUS status;
809  QWORD gsBase;
810  QWORD current;
811 
812  if (TaskStruct == NULL)
813  {
815  }
816 
817  if (CpuNumber == IG_CURRENT_VCPU)
818  {
819  CpuNumber = gVcpu->Index;
820  }
821 
822  status = IntGsRead(CpuNumber, &gsBase);
823  if (!INT_SUCCESS(status))
824  {
825  ERROR("[ERROR] IntGsRead failed for cpu %d: 0x%08x.\n", CpuNumber, status);
826  return status;
827  }
828 
829  if (!IS_KERNEL_POINTER_LIX(gsBase))
830  {
831  WARNING("[WARNING] 'gs' for cpu %d is not in kernel mode: %llx\n", CpuNumber, gsBase);
833  }
834 
836  if (!INT_SUCCESS(status))
837  {
838  ERROR("[ERROR] IntKernVirtMemPatchQword failed for gva 0x%llx with status: 0x%08x\n",
839  gsBase + gLixGuest->OsSpecificFields.CurrentTaskOffset, status);
840  return status;
841  }
842 
843  if (__unlikely(!IS_KERNEL_POINTER_LIX(current) || (current % 8)))
844  {
845  ERROR("[ERROR] Current task 0x%016llx is not valid!\n", current);
847  }
848 
849  *TaskStruct = current;
850 
851  return INT_STATUS_SUCCESS;
852 }
853 
854 
857  _In_ DWORD CpuNumber
858  )
871 {
872  QWORD currentTs;
873 
874  INTSTATUS status = IntLixTaskGetCurrentTaskStruct(CpuNumber, &currentTs);
875  if (!INT_SUCCESS(status))
876  {
877  return NULL;
878  }
879 
880  return IntLixTaskFindByGva(currentTs);
881 }
882 
883 
884 static __forceinline QWORD
886  _In_ QWORD Pgd
887  )
895 {
896  return Pgd & ~(BIT(LIX_PTI_PGTABLE_SWITCH_BIT));
897 }
898 
899 
900 static __forceinline QWORD
902  _In_ QWORD Pgd
903  )
911 {
912  return Pgd | BIT(LIX_PTI_PGTABLE_SWITCH_BIT);
913 }
914 
915 
916 QWORD
918  _In_ QWORD Cr3
919  )
927 {
928  Cr3 &= CR3_LONG_MODE_MASK;
929 
930  if (!gGuest.KptiActive)
931  {
932  return Cr3;
933  }
934 
935  return IntLixUserToKernelPgd(Cr3);
936 }
937 
938 
941  _In_ QWORD Cr3
942  )
951 {
952  if (0 == Cr3)
953  {
954  return NULL;
955  }
956 
958 
959  for_each_task(pTask)
960  {
961  if (pTask->Cr3 == Cr3)
962  {
963  return pTask;
964  }
965  }
966 
967  return NULL;
968 }
969 
970 
973  _In_ QWORD MmGva
974  )
983 {
985  {
986  if (pTask->MmGva == MmGva)
987  {
988  return pTask;
989  }
990  }
991 
992  return NULL;
993 }
994 
995 
998  _In_ QWORD MmGva
999  )
1008 
1009 {
1010  for_each_task(pTask)
1011  {
1012  if (pTask->MmGva == MmGva)
1013  {
1014  return pTask;
1015  }
1016  }
1017 
1018  return NULL;
1019 }
1020 
1021 
1024  _In_ QWORD TaskStruct
1025  )
1034 
1035 {
1036  for_each_task(pTask)
1037  {
1038  if (pTask->Gva == TaskStruct)
1039  {
1040  return pTask;
1041  }
1042  }
1043 
1044  return NULL;
1045 }
1046 
1047 
1050  _In_ DWORD Pid
1051  )
1060 
1061 {
1062  for_each_task(pTask)
1063  {
1064  if (pTask->Pid == Pid)
1065  {
1066  return pTask;
1067  }
1068  }
1069 
1070  return NULL;
1071 }
1072 
1073 
1074 DWORD
1076  void
1077  )
1081 {
1082  DWORD count = 0;
1083  for_each_task(pTask)
1084  {
1085  if (pTask->Exec)
1086  {
1087  ++count;
1088  }
1089  }
1090 
1091  return count;
1092 }
1093 
1094 
1095 INTSTATUS
1097  _In_ const LIX_TASK_OBJECT *Task,
1098  _Out_ LIX_TRAP_FRAME *TrapFrame
1099  )
1119 {
1120  INTSTATUS status;
1121  QWORD trapFrameGva, kmStack;
1122 
1123  status = IntKernVirtMemFetchQword(Task->Gva + LIX_FIELD(TaskStruct, Stack), &kmStack);
1124  if (!INT_SUCCESS(status))
1125  {
1126  ERROR("[ERROR] Failed to read km stack pointer for task %s (%d 0x%llx). Status: 0x%08x\n",
1127  Task->Comm, Task->Pid, Task->Gva, status);
1128  return status;
1129  }
1130 
1131  trapFrameGva = kmStack + LIX_FIELD(Info, ThreadSize) - sizeof(*TrapFrame);
1132 
1133  memzero(TrapFrame, sizeof(*TrapFrame));
1134 
1135  return IntKernVirtMemRead(trapFrameGva, sizeof(*TrapFrame), TrapFrame, NULL);
1136 }
1137 
1138 
1139 static LIX_PROTECTED_PROCESS *
1141  _In_ const LIX_TASK_OBJECT *Task
1142  )
1151 {
1153  {
1154  return NULL;
1155  }
1156 
1158  {
1159  if ((pProt->NamePattern && Task->Path && IntMatchPatternUtf8(pProt->NamePattern, Task->Path->Name, 0)) ||
1160  IntMatchPatternUtf8(pProt->CommPattern, Task->Comm, INTRO_MATCH_TRUNCATED))
1161  {
1162  return pProt;
1163  }
1164  }
1165 
1166  return NULL;
1167 }
1168 
1169 
1170 void
1172  _In_ const void *Name,
1173  _In_ const CAMI_STRING_ENCODING Encoding,
1174  _In_ const CAMI_PROT_OPTIONS *Options
1175  )
1184 {
1185  if (Encoding != CAMI_STRING_ENCODING_UTF8)
1186  {
1187  WARNING("[WARNING] Unsupported string encoding: %d\n", Encoding);
1188  return;
1189  }
1190 
1191  for_each_task_to_protect(pProcess)
1192  {
1193  if (IntMatchPatternUtf8(Name, pProcess->CommPattern, 0))
1194  {
1195  pProcess->Protection.Current = pProcess->Protection.Original & ~(Options->ForceOff);
1196  pProcess->Protection.Beta = Options->ForceBeta;
1197  pProcess->Protection.Feedback = Options->ForceFeedback;
1198 
1199  TRACE("[CAMI] Protected process info updated. Original : 0x%llx, Current : 0x%llx, "
1200  "Beta : 0x%llx, Feedback : 0x%llx", pProcess->Protection.Original, pProcess->Protection.Current,
1201  pProcess->Protection.Beta, pProcess->Protection.Feedback);
1202  }
1203  }
1204 }
1205 
1206 
1207 static INTSTATUS
1209  _In_ LIX_TASK_OBJECT *Task
1210  )
1219 {
1220  INTSTATUS status;
1221  QWORD flags;
1222 
1223  if (!(Task->Protection.Mask & PROC_OPT_PROT_EXPLOIT))
1224  {
1226  }
1227 
1228  // Invalidate all the entries inside the ICACHE associated to this process,
1229  // since the CR3 will not be hooked anymore.
1230  status = IntIcFlushVaSpace(gGuest.InstructionCache, Task->Cr3);
1231  if (!INT_SUCCESS(status))
1232  {
1233  ERROR("[ERROR] IntIcFlushVaSpace failed: 0x%08x\n", status);
1234  }
1235 
1236  IntLixMmDestroyVmas(Task);
1237 
1238  status = IntKernVirtMemFetchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), &flags);
1239  if (!INT_SUCCESS(status))
1240  {
1241  CRITICAL("[ERROR] IntKernVirtMemFetchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1242  }
1243  else
1244  {
1245  flags &= ~LIX_MM_PROT_MASK;
1246 
1247  // NOTE: IntVirtMemSafeWrite should be used instead, but it will induce significant performance penalty.
1248  status = IntKernVirtMemPatchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), flags);
1249  if (!INT_SUCCESS(status))
1250  {
1251  ERROR("[ERROR] IntKernVirtMemPatchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1252  }
1253  }
1254 
1255  if (Task->HookObject)
1256  {
1257  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&Task->HookObject, 0);
1258  if (!INT_SUCCESS(status))
1259  {
1260  ERROR("[ERROR] IntHookObjectDestroy failed: %08x", status);
1261  }
1262  }
1263 
1264  RemoveEntryList(&Task->ExploitProtProcLink);
1265 
1266  return INT_STATUS_SUCCESS;
1267 }
1268 
1269 
1270 static INTSTATUS
1272  _In_ LIX_TASK_OBJECT *Task
1273  )
1286 {
1287  INTSTATUS status;
1288  QWORD flags;
1289 
1290  if (!(Task->Protection.Mask & PROC_OPT_PROT_EXPLOIT))
1291  {
1293  }
1294 
1295  if (0 == Task->Cr3)
1296  {
1297  ERROR("[ERROR] Requesting to protect process '%s' (%d, %llx, %llx)but it has no CR3 (mm 0x%016llx)!\n",
1298  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, Task->MmGva);
1300  }
1301 
1302  if (Task->StaticDetected)
1303  {
1304  // This shouldn't happen since we ignore tasks which have PF_EXITING flag set.
1305  // But it's better to do it anyway.
1306  DWORD mmUsers = 0, mmCount = 0;
1307 
1308  status = IntKernVirtMemFetchDword(Task->MmGva + LIX_FIELD(MmStruct, MmUsers), &mmUsers);
1309  if (!INT_SUCCESS(status))
1310  {
1311  ERROR("[ERROR] Failed getting mm_users: %08x\n", status);
1312  goto _protect_task;
1313  }
1314 
1315  status = IntKernVirtMemFetchDword(Task->MmGva + LIX_FIELD(MmStruct, MmCount), &mmCount);
1316  if (!INT_SUCCESS(status))
1317  {
1318  ERROR("[ERROR] Failed getting mm_count: %08x\n", status);
1319  goto _protect_task;
1320  }
1321 
1322  if (0 == mmUsers || 0 == mmCount)
1323  {
1324  WARNING("[WARNING] Process %s (%d, %llx, %llx) has a dying mm @ %llx: (%d, %d)!\n",
1325  Task->Comm, Task->Pid, Task->Gva, Task->Cr3, Task->MmGva, mmUsers, mmCount);
1326 
1328  }
1329  }
1330 
1331 _protect_task:
1332  status = IntHookObjectCreate(introObjectTypeUmGenericNxZone, Task->Cr3, &Task->HookObject);
1333  if (!INT_SUCCESS(status))
1334  {
1335  ERROR("[ERROR] IntHookObjectCreate failed: %08x\n", status);
1336  return status;
1337  }
1338 
1339  status = IntLixMmPopulateVmas(Task);
1340  if (!INT_SUCCESS(status))
1341  {
1342  ERROR("[ERROR] IntLixMmPopulateVmas failed: 0x%08x\n", status);
1343  goto _free_and_exit;
1344  }
1345 
1346  InsertTailList(&gLixProtectedTasks, &Task->ExploitProtProcLink);
1347 
1348  status = IntKernVirtMemFetchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), &flags);
1349  if (!INT_SUCCESS(status))
1350  {
1351  ERROR("[ERROR] IntKernVirtMemFetchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1352  goto _free_and_exit;
1353  }
1354 
1355  flags |= LIX_MM_PROT_MASK;
1356 
1357  // NOTE: IntVirtMemSafeWrite should be used instead, but it will induce significant performance penalty.
1358  status = IntKernVirtMemPatchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), flags);
1359  if (!INT_SUCCESS(status))
1360  {
1361  ERROR("[ERROR] IntKernVirtMemPatchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1362  goto _free_and_exit;
1363  }
1364 
1365  status = INT_STATUS_SUCCESS;
1366 
1367 _free_and_exit:
1368  if (!INT_SUCCESS(status))
1369  {
1371  }
1372 
1373  return status;
1374 }
1375 
1376 
1377 static INTSTATUS
1379  _In_ LIX_TASK_OBJECT *Task,
1380  _In_opt_ LIX_TASK_OBJECT *Parent
1381  )
1392 {
1393  INTSTATUS status;
1394  BOOLEAN sameCr3;
1395 
1396  if (Task->Interpreter)
1397  {
1399  }
1400 
1402  {
1404  }
1405 
1406  // No point in checking the list if the Task is actually a thread/fork. The task name can be
1407  // anything, so the threads/forks should inherit the parent's protection.
1408  if (Task->Exec)
1409  {
1410  const LIX_PROTECTED_PROCESS *pProt = IntLixTaskShouldProtect(Task);
1411 
1412  if (NULL != pProt)
1413  {
1414  Task->Protection.Mask = pProt->Protection.Current;
1415  Task->Protection.Beta = pProt->Protection.Beta;
1416  Task->Protection.Feedback = pProt->Protection.Feedback;
1417  Task->Context = pProt->Context;
1418  }
1419  else
1420  {
1421  Task->Protection.Mask = 0;
1422  Task->Context = 0;
1423  }
1424 
1425  Task->RootProtectionMask = Task->Protection.Mask;
1426  }
1427  else if (Parent)
1428  {
1429  // A thread/fork should inherit the parent's root protection.
1430  // This is for a special case where a thread does fork, because
1431  // the thread protection mask will not include PROC_OPT_PROT_EXPLOIT
1432  Task->Protection.Mask = Parent->RootProtectionMask;
1433  Task->Protection.Beta = Parent->Protection.Beta;
1434  Task->Protection.Feedback = Parent->Protection.Feedback;
1435 
1436  Task->RootProtectionMask = Parent->RootProtectionMask;
1437  Task->Context = Parent->Context;
1438  }
1439 
1440  if (0 == Task->Protection.Mask)
1441  {
1443  }
1444 
1445  sameCr3 = Parent && (Task->Cr3 == Parent->Cr3);
1446 
1447  // Clean the protection flags (for example, if the parent & child share the same CR3, then
1448  // it's pointless to activate protection again on the same CR3).
1449  if (sameCr3)
1450  {
1452  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
1453  Task->Protection.Mask &= ~PROC_OPT_PROT_CORE_HOOKS;
1454  }
1455 
1456  status = IntLixTaskActivateExploitProtection(Task);
1457  if (!INT_SUCCESS(status))
1458  {
1459  ERROR("[ERROR] Process '%s' (%d, %llx, %llx) will not be exploit-protected: %08x!\n",
1460  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
1461 
1462  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
1463 
1464  status = INT_STATUS_SUCCESS;
1465  }
1466 
1467  // Keep the threads as protected... But mark the processes that failed to activate
1468  // the given protection as !protected
1469  if (!sameCr3 && (Task->Protection.Mask == 0))
1470  {
1471  ERROR("[ERROR] Task %s, 0x%016llx failed to activate any protection!\n", Task->Comm, Task->Gva);
1472 
1473  Task->Protected = FALSE;
1474 
1475  if (INT_SUCCESS(status))
1476  {
1478  }
1479 
1480  return status;
1481  }
1482 
1483  if (IntLixTaskMustLog(Task, TRUE))
1484  {
1485  TRACE("[PROC] Activated protection %llx for '%s' (%d, %llx, %llx)\n",
1486  Task->Protection.Mask, Task->ProcName, Task->Pid, Task->Gva, Task->Cr3);
1487  }
1488 
1489  Task->Protected = TRUE;
1490 
1491  return INT_STATUS_SUCCESS;
1492 }
1493 
1494 
1495 static void
1497  _In_ LIX_TASK_OBJECT *Task
1498  )
1504 {
1505  INTSTATUS status;
1506 
1507  if (!Task->Protected)
1508  {
1509  return;
1510  }
1511 
1513  if (!INT_SUCCESS(status))
1514  {
1515  WARNING("[WARNING] Process '%s' (%d, %llx, %llx) failed to deactivate protection: %08x\n",
1516  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
1517  }
1518 
1519  if (IntLixTaskMustLog(Task, TRUE))
1520  {
1521  LOG("[PROC] Deactivated protection for %s '%s' (%d, %llx, %llx)!\n",
1522  Task->IsThread ? "thread" : "process", Task->ProcName,
1523  Task->Pid, Task->Gva, Task->Cr3);
1524  }
1525 
1526  Task->Protection.Mask = 0;
1527  Task->RootProtectionMask = 0;
1528  Task->Context = 0;
1529  Task->Protected = FALSE;
1530 }
1531 
1532 
1533 static INTSTATUS
1535  _In_ LIX_TASK_OBJECT *Process,
1536  _In_ QWORD BinprmGva
1537  )
1548 {
1549  INTSTATUS status;
1550  QWORD vmaStart, vmaEnd, iter, file;
1551  DWORD argc, curLength;
1552  BYTE *pMapping = NULL;
1553 
1554  status = IntVirtMemMap(BinprmGva, LIX_FIELD(Binprm, Sizeof), gGuest.Mm.SystemCr3, 0, &pMapping);
1555  if (!INT_SUCCESS(status))
1556  {
1557  ERROR("[ERROR] IntVirtMemMap failed for %llx: 0x%08x\n", BinprmGva, status);
1558  return status;
1559  }
1560 
1561  argc = *(DWORD *)(pMapping + LIX_FIELD(Binprm, Argc));
1562  file = *(QWORD *)(pMapping + LIX_FIELD(Binprm, Vma));
1563 
1564  IntVirtMemUnmap(&pMapping);
1565 
1566  status = IntVirtMemMap(file,
1567  MAX(LIX_FIELD(Vma, VmaStart), LIX_FIELD(Vma, VmaEnd)) + sizeof(QWORD),
1569  0,
1570  &pMapping);
1571  if (!INT_SUCCESS(status))
1572  {
1573  ERROR("[ERROR] IntVirtMemMap failed for %llx: 0x%08x\n", file, status);
1574  return status;
1575  }
1576 
1577  vmaStart = *(QWORD *)(pMapping + LIX_FIELD(Vma, VmaStart));
1578  vmaEnd = *(QWORD *)(pMapping + LIX_FIELD(Vma, VmaEnd));
1579 
1580  IntVirtMemUnmap(&pMapping);
1581 
1582  if (vmaStart >= vmaEnd)
1583  {
1584  ERROR("[ERROR] Start of vma_struct %llx is bigger or equal that the end %llx!\n", vmaStart, vmaEnd);
1586  }
1587 
1588  if (vmaEnd - vmaStart > ONE_GIGABYTE)
1589  {
1590  ERROR("[ERROR] Argpage VMA is to big: 0x%llu. Cmdline will not be fetched\n", vmaEnd - vmaStart);
1592  }
1593 
1594  // Skip pages which are not present
1595  for (iter = vmaStart;
1596  iter < vmaEnd && (!INT_SUCCESS(IntVirtMemMap(iter, PAGE_SIZE, Process->Cr3, 0, &pMapping)));
1597  iter += PAGE_SIZE) { }
1598 
1599  if ((iter >= vmaEnd) || NULL == pMapping)
1600  {
1601  ERROR("[ERROR] Could not read cmdline. Not a single page from VMA 0x%llx to 0x%llx is present!\n",
1602  vmaStart, vmaEnd);
1603  return INT_STATUS_NOT_FOUND;
1604  }
1605 
1606  Process->CmdLine = HpAllocWithTag(vmaEnd - iter, IC_TAG_PCMD);
1607  if (NULL == Process->CmdLine)
1608  {
1609  IntVirtMemUnmap(&pMapping);
1611  }
1612 
1613  curLength = 0;
1614  for (;;)
1615  {
1616  DWORD parsed = 0;
1617  while ((0 == curLength) && (parsed < PAGE_SIZE) && (0 == pMapping[parsed]))
1618  {
1619  parsed++;
1620  }
1621 
1622  while (parsed < PAGE_SIZE)
1623  {
1624  if (0 == pMapping[parsed])
1625  {
1626  argc--;
1627  if (0 == argc)
1628  {
1629  break;
1630  }
1631  Process->CmdLine[curLength] = ' ';
1632  }
1633  else
1634  {
1635  Process->CmdLine[curLength] = pMapping[parsed];
1636  }
1637 
1638  parsed++;
1639  curLength++;
1640  }
1641 
1642  IntVirtMemUnmap(&pMapping);
1643 
1644  if (0 == argc)
1645  {
1646  break;
1647  }
1648 
1649  iter += PAGE_SIZE;
1650  if (iter >= vmaEnd)
1651  {
1652  WARNING("[WARNING] Reached end of vma, but there are %d more args to be read!\n", argc);
1653  break;
1654  }
1655 
1656  status = IntVirtMemMap(iter, PAGE_SIZE, Process->Cr3, 0, &pMapping);
1657  if (!INT_SUCCESS(status))
1658  {
1659  ERROR("[ERROR] IntVirtMemMap failed: %08x\n", status);
1660  break;
1661  }
1662  }
1663 
1664  Process->CmdLine[curLength] = 0;
1665  Process->CmdLineLength = curLength + 1;
1666 
1667  return INT_STATUS_SUCCESS;
1668 }
1669 
1670 
1671 static void
1673  _Inout_ LIX_TASK_OBJECT *Task
1674  )
1682 {
1683  if (Task->Path)
1684  {
1685  Task->ProcName = Task->Path->Path;
1686  Task->ProcNameLength = (DWORD)Task->Path->PathLength;
1687  }
1688  else
1689  {
1690  Task->ProcName = Task->Comm;
1691  Task->ProcNameLength = strlen(Task->Comm);
1692  }
1693 }
1694 
1695 
1696 static void
1698  _In_ QWORD FileGva,
1699  _In_ QWORD DPathGva,
1700  _Out_ LIX_TASK_OBJECT *Task
1701  )
1709 {
1710  INTSTATUS status = IntLixFileGetDentry(FileGva, &Task->ExeFileDentry);
1711  if (!INT_SUCCESS(status))
1712  {
1713  ERROR("[ERROR] IntLixFileGetDentry failed for %llx: %08x\n", FileGva, status);
1714  return;
1715  }
1716 
1717  if (!IS_ERR(DPathGva) && DPathGva)
1718  {
1719  Task->Path = IntLixTaskPathGetByPath(DPathGva, Task->ExeFileDentry);
1720  }
1721 
1722  if (NULL == Task->Path)
1723  {
1724  Task->Path = IntLixTaskPathGetByDentry(FileGva, 0, Task->ExeFileDentry);
1725  }
1726 }
1727 
1728 
1729 static INTSTATUS
1731  _In_ LIX_TASK_OBJECT *OriginalTask,
1732  _In_ QWORD BinprmGva,
1733  _In_ QWORD PathGva,
1734  _Out_ LIX_TASK_OBJECT *UpdatedTask
1735  )
1748 {
1749  INTSTATUS status;
1750  QWORD pathGva, interpGva;
1751  DWORD pid;
1752 
1753  BYTE *pBinprm;
1754  int exitSignal;
1755 
1756  if (!IS_KERNEL_POINTER_LIX(BinprmGva))
1757  {
1759  }
1760 
1761  UpdatedTask->ActualParent = OriginalTask->Parent;
1762  UpdatedTask->AgentTag = OriginalTask->AgentTag;
1763  UpdatedTask->Context = OriginalTask->Context;
1764  UpdatedTask->CreationTime = OriginalTask->CreationTime;
1765  UpdatedTask->Gva = OriginalTask->Gva;
1766  UpdatedTask->IsThread = FALSE;
1767  UpdatedTask->KernelMode = FALSE;
1768  UpdatedTask->Parent = OriginalTask->Parent;
1769  UpdatedTask->RealParent = OriginalTask->RealParent;
1770  UpdatedTask->Tgid = OriginalTask->Tgid;
1771  UpdatedTask->Pid = OriginalTask->Pid;
1772 
1773  memcpy(UpdatedTask->Comm, OriginalTask->Comm, sizeof(UpdatedTask->Comm));
1774 
1775  pBinprm = NULL;
1776 
1777  UpdatedTask->ReExecToSelf = FALSE;
1778 
1779  status = IntVirtMemMap(BinprmGva, LIX_FIELD(Binprm, Sizeof), gGuest.Mm.SystemCr3, 0, &pBinprm);
1780  if (!INT_SUCCESS(status))
1781  {
1782  ERROR("[ERROR] IntVirtMemMap failed for GVA 0x%016llx: 0x%08x\n", BinprmGva, status);
1783  goto _cleanup_and_exit;
1784  }
1785 
1786  status = _IntLixTaskStartMap(UpdatedTask->Gva);
1787  if (!INT_SUCCESS(status))
1788  {
1789  ERROR("[ERROR] _IntLixTaskStartMap failed for task %llx: 0x%08x\n", UpdatedTask->Gva, status);
1790  goto _cleanup_and_exit;
1791  }
1792 
1793  {
1794  DWORD in;
1795 
1796  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, InExecve), sizeof(in), &in);
1797  if (!INT_SUCCESS(status))
1798  {
1799  ERROR("[ERROR] _IntLixTaskRead failed for %llx: %08x\n",
1800  UpdatedTask->Gva + LIX_FIELD(TaskStruct, InExecve), status);
1801  }
1802  else if (0 == (in & BIT(LIX_FIELD(TaskStruct, InExecveBit))))
1803  {
1804  ERROR("[ERROR][CRITICAL] in_execve is not in fact set: 0x%02x\n", in);
1805  }
1806  }
1807 
1808  status = IntLixTaskFetchMm(*(QWORD *)(pBinprm + LIX_FIELD(Binprm, Mm)), UpdatedTask, NULL);
1809  if (!INT_SUCCESS(status))
1810  {
1811  ERROR("[ERROR] IntLixTaskFetchMm failed for %s: 0x%08x. The process cannot be protected!\n",
1812  UpdatedTask->ProcName, status);
1813  }
1814 
1815  IntLixTaskGetPath(*(QWORD *)(pBinprm + LIX_FIELD(Binprm, File)), PathGva, UpdatedTask);
1816 
1817  pathGva = *(QWORD *)(pBinprm + LIX_FIELD(Binprm, Filename));
1818  interpGva = *(QWORD *)(pBinprm + LIX_FIELD(Binprm, Interp));
1819 
1820  if (interpGva && interpGva != pathGva)
1821  {
1822  status = IntReadString(interpGva, 2, FALSE, &UpdatedTask->Interpreter, &UpdatedTask->InterpLength);
1823  if (!INT_SUCCESS(status))
1824  {
1825  UpdatedTask->Interpreter = NULL;
1826  UpdatedTask->InterpLength = 0;
1827  }
1828  }
1829 
1830  // The new exec process can change PID if it assumes the role of group leader
1831  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, ExitSignal), sizeof(exitSignal), &exitSignal);
1832  if (!INT_SUCCESS(status))
1833  {
1834  ERROR("[ERROR] Failed getting the PID from task_struct %llx: %08x\n",
1835  UpdatedTask->Gva + LIX_FIELD(TaskStruct, Pid), status);
1836  }
1837  else if (exitSignal < 0)
1838  {
1839  // This means that a thread is doing an exec, so get the PID of the group leader (which can be dead by now)
1840  QWORD groupLeader;
1841 
1842  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, GroupLeader), sizeof(groupLeader), &groupLeader);
1843  if (!INT_SUCCESS(status))
1844  {
1845  ERROR("[ERROR] Failed getting the group_leader from task_struct %llx: %08x\n",
1846  UpdatedTask->Gva + LIX_FIELD(TaskStruct, GroupLeader), status);
1847  }
1848  else
1849  {
1850  LIX_TASK_OBJECT *pGroupLeader = IntLixTaskFindByGva(groupLeader);
1851  if (NULL == pGroupLeader)
1852  {
1853  status = IntKernVirtMemFetchDword(groupLeader + LIX_FIELD(TaskStruct, Pid), &pid);
1854  if (!INT_SUCCESS(status))
1855  {
1856  ERROR("[ERROR] IntKernVirtMemFetchDword failed for %llx: %08x\n",
1857  groupLeader + LIX_FIELD(TaskStruct, Pid), status);
1858  }
1859  }
1860  else
1861  {
1862  pid = pGroupLeader->Pid;
1863  }
1864 
1865  if (UpdatedTask->Pid != pid)
1866  {
1867  TRACE("[INFO] Process '%s' changes PID from %d to %d\n", UpdatedTask->ProcName, UpdatedTask->Pid, pid);
1868 
1869  UpdatedTask->Pid = pid;
1870  }
1871  }
1872  }
1873 
1874  if (OriginalTask->ExeFileDentry == UpdatedTask->ExeFileDentry)
1875  {
1876  UpdatedTask->ReExecToSelf = TRUE;
1877  }
1878 
1879  if ((!UpdatedTask->ReExecToSelf) && OriginalTask->Interpreter && UpdatedTask->Interpreter)
1880  {
1881  if (OriginalTask->InterpLength == UpdatedTask->InterpLength &&
1882  0 == strcmp(OriginalTask->Interpreter, UpdatedTask->Interpreter))
1883  {
1884  UpdatedTask->ReExecToSelf = TRUE;
1885  }
1886  }
1887 
1888  IntLixTaskSetProcName(UpdatedTask);
1889 
1890  InitializeListHead(&UpdatedTask->Vmas);
1891 
1892  InitializeListHead(&UpdatedTask->ExploitProtProcLink);
1893 
1894  status = IntLixCredAdd(*(QWORD *)(pBinprm + LIX_FIELD(Binprm, Cred)), &UpdatedTask->Creds);
1895  if (!INT_SUCCESS(status))
1896  {
1897  ERROR("[ERROR] IntLixCredAdd failed for %s (%d 0x%llx). Status: 0x%08x\n",
1898  UpdatedTask->Comm, UpdatedTask->Pid, UpdatedTask->Gva, status);
1899  UpdatedTask->Creds = NULL;
1900  }
1901 
1903 
1904  status = INT_STATUS_SUCCESS;
1905 
1906 _cleanup_and_exit:
1907  IntVirtMemUnmap(&pBinprm);
1908 
1909  return status;
1910 }
1911 
1912 
1913 static void
1915  _In_ LIX_TASK_OBJECT *Task,
1916  _In_ DWORD ExitCode,
1917  _In_ BOOLEAN Created,
1918  _In_ BOOLEAN Crashed,
1919  _In_ BOOLEAN StaticDetected
1920  )
1930 {
1931  INTSTATUS status;
1932  EVENT_PROCESS_EVENT *pProcEvent;
1933  LIX_TASK_OBJECT *pParent;
1934 
1936  {
1937  return;
1938  }
1939 
1940  if (Created && Task->ReExecToSelf)
1941  {
1942  return;
1943  }
1944 
1945  pProcEvent = &gAlert.Process;
1946  memzero(pProcEvent, sizeof(*pProcEvent));
1947 
1948  pProcEvent->Created = Created;
1949  pProcEvent->Protected = Task->Protected != 0;
1950  pProcEvent->Crashed = Crashed;
1951  pProcEvent->ExitStatus = ExitCode;
1952 
1953  if (!StaticDetected)
1954  {
1956  }
1957  else
1958  {
1959  pProcEvent->CurrentProcess.Valid = FALSE;
1960  }
1961 
1962  IntAlertFillLixProcess(Task, &pProcEvent->Child);
1963 
1964  pParent = IntLixTaskFindByGva(Task->Parent);
1965  if (pParent)
1966  {
1967  IntAlertFillLixProcess(pParent, &pProcEvent->Parent);
1968  }
1969  else
1970  {
1971  pProcEvent->Parent.Valid = FALSE;
1972  }
1973 
1974  status = IntNotifyIntroEvent(introEventProcessEvent, pProcEvent, sizeof(*pProcEvent));
1975  if (!INT_SUCCESS(status))
1976  {
1977  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
1978  }
1979 }
1980 
1981 
1982 static void
1984  _In_ LIX_TASK_OBJECT *Task,
1985  _In_ DWORD ExitCode,
1986  _In_ BOOLEAN Created
1987  )
1995 {
1996  INTSTATUS status = INT_STATUS_SUCCESS;
1997  EVENT_AGENT_EVENT *pAgentEvent;
1998 
1999  if (!Task->AgentTag)
2000  {
2001  return;
2002  }
2003 
2004  pAgentEvent = &gAlert.Agent;
2005  memzero(pAgentEvent, sizeof(*pAgentEvent));
2006 
2007  IntAlertFillLixProcess(Task, &pAgentEvent->CurrentProcess);
2008 
2009  pAgentEvent->ErrorCode = 0;
2010 
2011  pAgentEvent->Event = Created ? agentStarted : agentTerminated;
2012  pAgentEvent->AgentTag = Task->AgentTag;
2013  pAgentEvent->ErrorCode = ExitCode;
2014 
2015  status = IntNotifyIntroEvent(introEventAgentEvent, pAgentEvent, sizeof(*pAgentEvent));
2016  if (!INT_SUCCESS(status))
2017  {
2018  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
2019  }
2020 }
2021 
2022 
2023 INTSTATUS
2025  _In_ LIX_TASK_OBJECT *Task,
2026  _Out_opt_ QWORD *StackPointer,
2027  _Out_opt_ QWORD *StackBase,
2028  _Out_opt_ QWORD *StackLimit
2029  )
2040 {
2041  INTSTATUS status;
2042  LIX_TRAP_FRAME trapFrame;
2043  QWORD base, limit;
2044 
2045  status = IntLixTaskGetTrapFrame(Task, &trapFrame);
2046  if (!INT_SUCCESS(status))
2047  {
2048  ERROR("[ERROR] IntLixTaskGetTrapFrame failed: %08x\n", status);
2049  return status;
2050  }
2051 
2052  if (NULL != StackBase || NULL != StackLimit)
2053  {
2054  status = IntLixMmFindVmaRange(trapFrame.Rsp, Task, &limit, &base);
2055  if (!INT_SUCCESS(status))
2056  {
2057  ERROR("[ERROR] Failed to find stack limits for process %s (%d 0x%llx), rsp: 0x%llx. Status:0x%08x\n",
2058  Task->Comm, Task->Pid, Task->Gva, trapFrame.Rsp, status);
2059  return status;
2060  }
2061 
2062  if (NULL != StackBase)
2063  {
2064  *StackBase = base;
2065  }
2066 
2067  if (NULL != StackLimit)
2068  {
2069  *StackLimit = limit;
2070  }
2071  }
2072 
2073  if (NULL != StackPointer)
2074  {
2075  *StackPointer = trapFrame.Rsp;
2076  }
2077 
2078  return INT_STATUS_SUCCESS;
2079 }
2080 
2081 
2082 static INTSTATUS
2084  _In_ LIX_TASK_OBJECT *Parent,
2085  _In_ LIX_TASK_OBJECT *RealParent,
2086  _In_ QWORD TaskStruct,
2087  _In_ BOOLEAN StaticDetected,
2088  _Out_opt_ LIX_TASK_OBJECT **Task
2089  )
2106 {
2107  INTSTATUS status;
2108  LIX_TASK_OBJECT *pTask, *pActualParent;
2109  DWORD flags = 0;
2110  QWORD file = 0;
2111 
2112  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Flags), sizeof(flags), &flags);
2113  if (!INT_SUCCESS(status))
2114  {
2115  ERROR("[ERROR] Failed getting the flags in task 0x%016llx: 0x%08x\n", TaskStruct, status);
2116  return status;
2117  }
2118 
2119  if ((flags & PF_EXITING) != 0)
2120  {
2121  TRACE("[INFO] Task with 0x%llx is dying while initializing (static: %d)... Will ignore.\n",
2122  TaskStruct, StaticDetected);
2123  if (NULL != Task)
2124  {
2125  *Task = NULL;
2126  }
2128  }
2129 
2130  pTask = HpAllocWithTag(sizeof(*pTask), IC_TAG_POBJ);
2131  if (NULL == pTask)
2132  {
2134  }
2135 
2136  pTask->Gva = TaskStruct;
2137 
2138  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Pid), sizeof(pTask->Pid), &pTask->Pid);
2139  if (!INT_SUCCESS(status))
2140  {
2141  ERROR("[ERROR] Failed getting the 'PID' in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2142  goto _free_and_exit;
2143  }
2144 
2145  if (!StaticDetected)
2146  {
2147  LIX_TASK_OBJECT *pExistingTask = IntLixTaskFindByPid(pTask->Pid);
2148  if (pExistingTask)
2149  {
2150  //
2151  // For now, just log an error, this is a cache issue 99.999%
2152  //
2153  ERROR("[ERROR] [CRITICAL] There is already an existing task with PID %d\n", pTask->Pid);
2154 
2155  LOG("[ERROR] %s%s %s (%d/%d, %16llx)]\n",
2156  pExistingTask->IsThread ? "Thread" : "Process",
2157  pExistingTask->KernelMode ? "(KM)" : "",
2158  pExistingTask->ProcName,
2159  pExistingTask->Pid, pExistingTask->Tgid, pExistingTask->Gva);
2160  }
2161  }
2162 
2163  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Tgid), sizeof(pTask->Tgid), &pTask->Tgid);
2164  if (!INT_SUCCESS(status))
2165  {
2166  ERROR("[ERROR] Failed getting the 'tgid' in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2167  goto _free_and_exit;
2168  }
2169 
2171  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Comm), sizeof(pTask->Comm), pTask->Comm);
2172  if (!INT_SUCCESS(status))
2173  {
2174  ERROR("[ERROR] Failed reading 'comm' in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2175  goto _free_and_exit;
2176  }
2177 
2178  pTask->CommHash = Crc32String(pTask->Comm, INITIAL_CRC_VALUE);
2179 
2180  pTask->IsThread = pTask->Tgid != pTask->Pid;
2181 
2182  if (flags & PF_KTHREAD)
2183  {
2184  pTask->IsThread = TRUE;
2185  pTask->KernelMode = TRUE;
2186  }
2187 
2188  if (StaticDetected && !pTask->KernelMode)
2189  {
2190  // The actual way to determine if a thread was already executed (PF_FORKNOEXEC).
2191  // But there is another special case:
2192  // 1. exec /usr/bin/squid (new PID = 32)
2193  // 2. squid does a fork() (new PID = 33)
2194  // 3. original squid (PID 32) dies
2195  // the parent of squid (PID 33) becomes PID 1
2196  // but this should be considered an exec by us
2197  if (!(flags & PF_FORKNOEXEC) ||
2198  (!pTask->IsThread && RealParent->Pid == 1))
2199  {
2200  pTask->IsThread = FALSE;
2201  pTask->Exec = TRUE;
2202  }
2203  }
2204 
2205  // When activating protection, we care about the TGID, since that's the CR3
2206  // NOTE: in case this changes in future versions, then get the CR3 and search by that
2207  pActualParent = RealParent;
2208  if (!pTask->KernelMode && pTask->IsThread && RealParent->Pid != pTask->Tgid)
2209  {
2210  pActualParent = IntLixTaskFindByPid(pTask->Tgid);
2211  if (NULL == pActualParent)
2212  {
2213  WARNING("[WARNING] Task with TGID %d is dead\n", pTask->Tgid);
2214  pActualParent = RealParent;
2215  }
2216  }
2217 
2218  pTask->ActualParent = pActualParent->Gva;
2219  pTask->RealParent = RealParent->Gva;
2220  pTask->Parent = Parent->Gva;
2221 
2222  if (gGuest.OSVersion < LIX_CREATE_VERSION(3, 17, 0))
2223  {
2224  QWORD time[2] = {0};
2225 
2226  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, StartTime), sizeof(time), time);
2227  if (!INT_SUCCESS(status))
2228  {
2229  ERROR("[ERROR] Failed getting the start time in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2230  goto _free_and_exit;
2231  }
2232 
2233  // Convert to nanoseconds
2234  pTask->CreationTime = time[0] * NSEC_PER_SEC + time[1];
2235  }
2236  else
2237  {
2238  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, StartTime),
2239  sizeof(pTask->CreationTime),
2240  &pTask->CreationTime);
2241  if (!INT_SUCCESS(status))
2242  {
2243  ERROR("[ERROR] Failed getting the start time in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2244  goto _free_and_exit;
2245  }
2246  }
2247 
2248  // Forked processes should not have their own memory allocated for the path (if
2249  // you want a path, get the from the parent). Also, that helps us in no way, since we
2250  // protect every sub-process (at least until it's doing an exec).
2251  pTask->StaticDetected = StaticDetected;
2252 
2253  status = IntLixTaskFetchMm(0, pTask, pTask->IsThread ? pActualParent : NULL);
2254  if (!INT_SUCCESS(status))
2255  {
2256  ERROR("[ERROR] IntLixTaskFetchMm failed: 0x%08x. This task (%s) cannot be protected!\n",
2257  status, pTask->Comm);
2258  }
2259 
2260  if (pTask->IsThread || !pTask->StaticDetected || !IS_KERNEL_POINTER_LIX(pTask->MmGva))
2261  {
2262  // 1. If we have a thread, it's simple => just inherit the parent name
2263  // 2. If the process is not found at a static scan => we already got the filename for the task, no
2264  // point in doing it again
2265  // 3. An error occurred and we have no mm => nothing we can, so inherit parent
2266  pTask->ExeFileDentry = pActualParent->ExeFileDentry;
2267 
2268  pTask->Path = IntLixTaskPathGetRef(pActualParent->Path);
2269  }
2270  else
2271  {
2272  status = IntKernVirtMemFetchQword(pTask->MmGva + LIX_FIELD(MmStruct, ExeFile), &file);
2273  if (!INT_SUCCESS(status))
2274  {
2275  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: %08x\n",
2276  pTask->MmGva + LIX_FIELD(MmStruct, ExeFile), status);
2277  goto _initialize_and_prot;
2278  }
2279 
2280  status = IntLixFileGetDentry(file, &pTask->ExeFileDentry);
2281  if (!INT_SUCCESS(status))
2282  {
2283  ERROR("[ERROR] IntLixFileGetDentry failed for %llx: %08x\n", file, status);
2284  goto _initialize_and_prot;
2285  }
2286 
2287  pTask->Path = IntLixTaskPathGetByDentry(file, 0, pTask->ExeFileDentry);
2288  }
2289 
2290 _initialize_and_prot:
2291 
2292  InitializeListHead(&pTask->Vmas);
2294 
2295  InsertTailList(&gLixTasks, &pTask->Link);
2296 
2297  IntLixTaskSetProcName(pTask);
2298 
2299  if (StaticDetected)
2300  {
2301  pTask->IsPreviousAgent = pTask->Comm[14] == '?';
2302  }
2303 
2304  status = IntLixTaskActivateProtection(pTask, pActualParent);
2305  if (!INT_SUCCESS(status))
2306  {
2307  ERROR("[ERROR] IntLixTaskActivateProtection failed for %s (%llx): 0x%08x\n", pTask->Comm, pTask->Gva, status);
2308 
2310  }
2311 
2312  if (!pTask->KernelMode)
2313  {
2314  QWORD creds;
2315 
2316  // On static detected processes, use the real_cred pointer. We may catch the creds inside a
2317  // override_creds - revert_creds flow, and the creds won't be reliable.
2318  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Cred), sizeof(creds), &creds);
2319  if (INT_SUCCESS(status))
2320  {
2321  status = IntLixCredAdd(creds, &pTask->Creds);
2322  if (!INT_SUCCESS(status))
2323  {
2324  ERROR("[ERROR] IntLixCredAdd failed for task %s (%d 0x%llx) with status: 0x%08x!. Creds gva: 0x%llx\n",
2325  pTask->Comm, pTask->Pid, pTask->Gva, status, creds);
2326  }
2327  }
2328  else
2329  {
2330  ERROR("[ERROR] _IntLixTaskRead failed for task %s (%d 0x%llx): 0x%08x\n",
2331  pTask->Comm, pTask->Pid, pTask->Gva, status);
2332  }
2333 
2334  pTask->UserStack.Valid = TRUE;
2335 
2336  status = IntLixTaskGetUserStack(pTask, NULL, &pTask->UserStack.Base, &pTask->UserStack.Limit);
2337  if (!INT_SUCCESS(status))
2338  {
2339  ERROR("[ERROR] Failed to get user mode stack for process %s (%d, 0x%llx). Status: 0x%08x\n",
2340  pTask->Comm, pTask->Pid, pTask->Gva, status);
2341 
2342  pTask->UserStack.Valid = FALSE;
2343  }
2344  }
2345 
2346  pTask->Dpi.StolenTokens = RealParent->Dpi.StolenTokens || Parent->Dpi.StolenTokens;
2347 
2348  if (IntLixTaskMustLog(pTask, pTask->Protected != 0))
2349  {
2350  if (!pTask->IsThread)
2351  {
2352  LOG("[%s]%s %s (%s), (%d/%d, %llx, %llx) [from %s%s %s (%d, %16llx)]\n",
2353  pTask->Exec ? "EXEC" : "FORK",
2354  pTask->Protected ? "[PROT]" : "",
2355  pTask->ProcName,
2356  pTask->Comm,
2357  pTask->Pid, pTask->Tgid,
2358  pTask->Cr3, pTask->Gva,
2359  pActualParent->IsThread ? "Thread" : "Process",
2360  pActualParent->KernelMode ? "(KM)" : "",
2361  pActualParent->ProcName,
2362  pActualParent->Pid, pActualParent->Gva);
2363  }
2364  else
2365  {
2366  LOG("[THREAD]%s %s, (%d/%d, %llx, %llx)\n",
2367  pTask->Protected ? "[PROT]" : "",
2368  pTask->Comm,
2369  pTask->Pid, pTask->Tgid,
2370  pTask->Cr3, pTask->Gva);
2371  }
2372  }
2373 
2374  if (pActualParent->AgentTag)
2375  {
2376  // Mark this one as agent too
2377  pTask->AgentTag = IntLixAgentIncProcRef(pTask->Comm);
2378  }
2379 
2380  if (StaticDetected && pTask->Exec)
2381  {
2382  if (NULL == gLixGuest->InitProcessObj && pTask->Pid == 1)
2383  {
2384  gLixGuest->InitProcessObj = pTask;
2385  }
2386 
2387  IntLixTaskSendTaskEvent(pTask, 0, TRUE, FALSE, StaticDetected);
2388  }
2389 
2390  if (NULL != Task)
2391  {
2392  *Task = pTask;
2393  }
2394 
2395  return INT_STATUS_SUCCESS;
2396 
2397 _free_and_exit:
2398  if (NULL != pTask)
2399  {
2401  }
2402 
2403  return status;
2404 }
2405 
2406 
2407 static void
2409  _In_ LIX_TASK_OBJECT *Task
2410  )
2416 {
2417  if (Task->Protected)
2418  {
2420  }
2421 
2422  IntLixCredRemove(&Task->Creds);
2423 
2424  IntLixTaskPathFree(&Task->Path);
2425 
2426  if (NULL != Task->Interpreter)
2427  {
2428  HpFreeAndNullWithTag(&Task->Interpreter, IC_TAG_NAME);
2429  }
2430 
2431  if (NULL != Task->CmdLine)
2432  {
2433  HpFreeAndNullWithTag(&Task->CmdLine, IC_TAG_PCMD);
2434  }
2435 
2437 }
2438 
2439 
2440 static void
2442  _In_ LIX_TASK_OBJECT *Task
2443  )
2449 {
2450  INTSTATUS status;
2451  char specialChar = '?';
2452 
2453  if (!Task->AgentTag || gGuest.ShutDown)
2454  {
2455  return;
2456  }
2457 
2459  Task->Gva + LIX_FIELD(TaskStruct, Comm) + 14,
2460  1,
2461  &specialChar,
2462  IG_CS_RING_0);
2463  if (!INT_SUCCESS(status))
2464  {
2465  ERROR("[ERROR] IntVirtMemSafeWrite failed for task %llx: 0x%08x\n", Task->Gva, status);
2466  }
2467 }
2468 
2469 
2470 static void
2472  _In_ LIX_TASK_OBJECT *Task,
2473  _In_ DWORD ExitCode
2474  )
2481 {
2482  INTSTATUS status;
2483  DWORD signal;
2484  BOOLEAN crashed = FALSE, lastAgent = FALSE, wasProtected = FALSE;
2485 
2486  if (Task->Protected)
2487  {
2489  wasProtected = TRUE;
2490  }
2491 
2492  signal = ExitCode & 0x7f;
2493 
2494  if ((ExitCode & 0x80) ||
2495  signal == SIGQUIT ||
2496  signal == SIGILL ||
2497  signal == SIGTRAP ||
2498  signal == SIGABRT ||
2499  signal == SIGBUS ||
2500  signal == SIGFPE ||
2501  signal == SIGSEGV)
2502  {
2503  IntLixTaskSendExceptionEvent(signal, Task);
2504 
2505  crashed = TRUE;
2506  }
2507 
2508  if (__unlikely(Task->MustKill && signal != SIGKILL))
2509  {
2510  WARNING("[WARNING] Task %s (%d, %llx, %llx) was marked to be killed, but the signal it received is %d",
2511  Task->ProcName, Task->Pid, Task->Cr3, Task->Gva, signal);
2512  }
2513 
2514  Task->AgentTag = IntLixAgentDecProcRef(Task->Comm, &lastAgent);
2515 
2516  if (Task->Exec)
2517  {
2518  IntLixTaskSendTaskEvent(Task, ExitCode, FALSE, crashed, FALSE);
2519  }
2520 
2521  if (lastAgent)
2522  {
2523  IntLixTaskSendAgentEvent(Task, ExitCode, FALSE);
2524  }
2525 
2526  RemoveEntryList(&Task->Link);
2527 
2528  if (!Task->IsThread && !Task->KernelMode && Task->Cr3 != 0)
2529  {
2530  // Invalidate all the entries inside the ICACHE associated to this process.
2531  status = IntIcFlushVaSpace(gGuest.InstructionCache, Task->Cr3);
2532  if (!INT_SUCCESS(status))
2533  {
2534  ERROR("[ERROR] IntIcFlushVaSpace failed: 0x%08x\n", status);
2535  }
2536  }
2537 
2538  // One last check on process exit
2539 
2540  IntLixCredsVerify(Task);
2541 
2542  IntLixCredRemove(&Task->Creds);
2543 
2544  if (IntLixTaskMustLog(Task, wasProtected))
2545  {
2546  LOG("[EXIT] %s %s (%d, %llx, %llx), crashed: %d, signal: %d\n",
2547  Task->IsThread ? "Thread" : "Process", Task->ProcName, Task->Pid,
2548  Task->Cr3, Task->Gva, crashed, signal);
2549  }
2550 
2551  if (!Task->IsThread)
2552  {
2553  IntUDRemoveAllEntriesForCr3(Task->Cr3);
2554  }
2555 
2556  IntLixTaskRemoveEntry(Task);
2557 }
2558 
2559 
2561 static DWORD
2563  _In_ DWORD Flags
2564  )
2572 {
2574  {
2575  return idAccessToken;
2576  }
2577 
2579  {
2580  return idExploitClientExec;
2581  }
2582 
2583  ERROR("[ERROR] We do not have any known DPI flag set -> Flags:0x%x\n", Flags);
2584  return 0;
2585 }
2586 
2587 
2588 static void
2590  _In_ LIX_TASK_OBJECT *OldTask,
2591  _In_ LIX_TASK_OBJECT *NewTask,
2592  _In_ INTRO_ACTION Action,
2593  _In_ INTRO_ACTION_REASON Reason,
2594  _In_ DWORD PcType
2595  )
2605 {
2606  INTSTATUS status;
2608 
2609  pEvent = &gAlert.ProcessCreation;
2610  memzero(pEvent, sizeof(*pEvent));
2611 
2612  pEvent->Header.Action = Action;
2613  pEvent->Header.Reason = Reason;
2614  pEvent->Header.MitreID = idExecApi;
2615 
2617 
2618  // Fill with OldTask info, since that is the current process (it's more relevant this way)
2619  IntAlertFillLixProcess(OldTask, &pEvent->Header.CurrentProcess);
2620 
2622  IntAlertFillVersionInfo(&pEvent->Header);
2623 
2624  IntAlertFillLixProcess(NewTask, &pEvent->Originator);
2625  IntAlertFillLixProcess(OldTask, &pEvent->Victim);
2626 
2627  if (PcType)
2628  {
2629  pEvent->PcType = PcType;
2630  pEvent->Header.MitreID = IntLixTaskGetDpiMitreId(PcType);
2631  }
2632 
2633  status = IntNotifyIntroEvent(introEventProcessCreationViolation, pEvent, sizeof(*pEvent));
2634  if (!INT_SUCCESS(status))
2635  {
2636  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
2637  }
2638 }
2639 
2640 
2642 static DWORD
2644  _In_ LIX_TASK_OBJECT *Task
2645  )
2653 {
2654  DWORD flags = 0;
2655 
2656  if (Task->Dpi.IsPivoted)
2657  {
2659  }
2660 
2661  if (Task->Dpi.StolenTokens)
2662  {
2664  }
2665 
2666  return flags;
2667 }
2668 
2669 
2670 static void
2672  _In_ LIX_TASK_OBJECT *ChildTask,
2673  _In_ LIX_TASK_OBJECT *ParentTask,
2674  _In_ INTRO_OBJECT_TYPE ObjectType,
2675  _Out_ INTRO_ACTION *Action,
2676  _Out_ INTRO_ACTION_REASON *Reason
2677  )
2688 {
2689  INTSTATUS status = INT_STATUS_SUCCESS;
2690  EXCEPTION_UM_ORIGINATOR originator = { 0 };
2691  EXCEPTION_VICTIM_ZONE victim = { 0 };
2692 
2693  *Action = introGuestAllowed;
2694  *Reason = introReasonAllowed;
2695 
2696  if (ObjectType != introObjectTypeProcessCreation && ObjectType != introObjectTypeProcessCreationDpi)
2697  {
2698  ERROR("[ERROR] IntLixValidateProcessCreationRights called with object type %d!\n", ObjectType);
2699  return;
2700  }
2701 
2702  // If the process re-executes to self, then we'll allow it
2703  if (ChildTask->ReExecToSelf)
2704  {
2705  return;
2706  }
2707 
2708  // Ignore kthreads
2709  if (ParentTask->KernelMode)
2710  {
2711  return;
2712  }
2713 
2714  if (ObjectType == introObjectTypeProcessCreationDpi)
2715  {
2716  originator.PcType = IntLixTaskGetDpiViolationFlags(ChildTask);
2717 
2718  if (originator.PcType == 0)
2719  {
2720  *Action = introGuestAllowed;
2721  *Reason = introReasonAllowed;
2722 
2723  return;
2724  }
2725  }
2726 
2727  status = IntExceptUserGetOriginator(ChildTask, FALSE, 0, NULL, &originator);
2728  if (!INT_SUCCESS(status))
2729  {
2730  ERROR("[ERROR] IntExceptUserGetOriginator failed with status: 0x%08x.\n", status);
2731  *Action = introGuestNotAllowed;
2732  *Reason = introReasonInternalError;
2733  return;
2734  }
2735 
2736  status = IntExceptGetVictimProcessCreation(ParentTask, ObjectType, &victim);
2737  if (!INT_SUCCESS(status))
2738  {
2739  ERROR("[ERROR] IntExceptGetVictimProcessCreation failed with status: 0x%08x.\n", status);
2740  *Action = introGuestNotAllowed;
2741  *Reason = introReasonInternalError;
2742  return;
2743  }
2744 
2745  IntExcept(&victim, &originator, exceptionTypeUm, Action, Reason, introEventProcessCreationViolation);
2746 
2747  if (ObjectType == introObjectTypeProcessCreation)
2748  {
2749  if (IntPolicyProcTakeAction(PROC_OPT_PROT_PREVENT_CHILD_CREATION, ParentTask, Action, Reason))
2750  {
2751  LOG("[PROCESS CREATION] Process creation blocked. Process `%s` tried to start using process `%s`",
2752  ChildTask->Comm, ParentTask->Comm);
2753 
2754  IntLixTaskSendBlockedEvent(ParentTask, ChildTask, *Action, *Reason, originator.PcType);
2755 
2757  }
2758  }
2759  else
2760  {
2761  if (IntPolicyCoreTakeAction(INTRO_OPT_PROT_DPI, Action, Reason))
2762  {
2763  LOG("[PROCESS CREATION] Process creation blocked. Process `%s` tried to start with DPI using process `%s`",
2764  ChildTask->Comm, ParentTask->Comm);
2765 
2766  IntLixTaskSendBlockedEvent(ParentTask, ChildTask, *Action, *Reason, originator.PcType);
2767 
2769  }
2770  }
2771 }
2772 
2773 
2774 INTSTATUS
2776  _In_ LIX_TASK_OBJECT *Task,
2777  _In_ QWORD Ptr,
2778  _Out_ BOOLEAN *IsPivoted
2779  )
2793 {
2794  INTSTATUS status;
2795  QWORD base, limit;
2796 
2797  if (NULL == Task || Task->KernelMode)
2798  {
2800  }
2801 
2802  if (IS_KERNEL_POINTER_LIX(Ptr))
2803  {
2805  }
2806 
2807  if (NULL == IsPivoted)
2808  {
2810  }
2811 
2812  if (Task->UserStack.Valid)
2813  {
2814  if (__likely(IN_RANGE_INCLUSIVE(Ptr, Task->UserStack.Limit, Task->UserStack.Base)))
2815  {
2816  *IsPivoted = FALSE;
2817 
2818  return INT_STATUS_SUCCESS;
2819  }
2820  }
2821 
2822  if (!Task->IsThread)
2823  {
2824  if (__unlikely(0 == Task->MmGva))
2825  {
2826  WARNING("[WARNING] Parent task %s (%d 0x%llx) is not a thread and doesn't have a valid mm pointer!\n",
2827  Task->Comm, Task->Pid, Task->Gva);
2828 
2829  goto _check_altstack;
2830  }
2831 
2832  status = IntKernVirtMemFetchQword(Task->MmGva + LIX_FIELD(MmStruct, StartStack), &base);
2833  if (!INT_SUCCESS(status))
2834  {
2835  ERROR("[ERROR] Failed to read start_stack from mm. Task %s (%d 0x%llx), MmGva 0x%llx, status: 0x%08x\n",
2836  Task->Comm, Task->Pid, Task->Gva, Task->MmGva, status);
2837  return status;
2838  }
2839 
2840  status = IntLixMmFindVmaRange(base, Task, &limit, &base);
2841  if (!INT_SUCCESS(status))
2842  {
2843  ERROR("[ERROR] Failed to find VAD for task->mm.start_stack(0x%llx) for process %s (%d 0x%llx). "
2844  "Status: 0x%08x\\n", base, Task->Comm, Task->Pid, Task->Gva, status);
2845  return status;
2846  }
2847 
2848  if (__likely(IN_RANGE_INCLUSIVE(Ptr, limit, base)))
2849  {
2850  *IsPivoted = FALSE;
2851  return INT_STATUS_SUCCESS;
2852  }
2853  }
2854 
2855 _check_altstack:
2856  status = IntKernVirtMemFetchQword(Task->Gva + LIX_FIELD(TaskStruct, AltStackSp), &base);
2857  if (!INT_SUCCESS(status))
2858  {
2859  ERROR("[ERROR] Failed to read alt stack. Task %s (%d 0x%llx), status: 0x%08x\n",
2860  Task->Comm, Task->Pid, Task->Gva, status);
2861 
2862  return status;
2863  }
2864 
2865  if (0 != base)
2866  {
2867  status = IntLixMmFindVmaRange(base, Task, &limit, &base);
2868  if (!INT_SUCCESS(status))
2869  {
2870  ERROR("[ERROR] Failed to find alt stack (0x%llx) VAD for process %s (%d 0x%llx). Status: 0x%08x\n",
2871  base, Task->Comm, Task->Pid, Task->Gva, status);
2872  return status;
2873  }
2874 
2875  if (IN_RANGE_INCLUSIVE(Ptr, limit, base))
2876  {
2877  *IsPivoted = FALSE;
2878  return INT_STATUS_SUCCESS;
2879  }
2880  }
2881 
2882  // NOTE: Should we set this right after the check_altstack label?
2883  *IsPivoted = TRUE;
2884 
2885  return INT_STATUS_SUCCESS;
2886 }
2887 
2888 
2889 static void
2891  _In_ LIX_TASK_OBJECT *ParentTask,
2892  _In_ LIX_TASK_OBJECT *CurrentTask
2893  )
2900 {
2901  INTSTATUS status;
2902 
2903  QWORD stackPointer;
2904 
2905  if (ParentTask->KernelMode)
2906  {
2907  return;
2908  }
2909 
2910  status = IntLixTaskGetUserStack(ParentTask, &stackPointer, NULL, NULL);
2911  if (!INT_SUCCESS(status))
2912  {
2913  ERROR("[ERROR] Failed to get user mode stack pointer for parent task %s (%d 0x%llx). Status: 0x%08x\n",
2914  ParentTask->Comm, ParentTask->Pid, ParentTask->Gva, status);
2915  return;
2916  }
2917 
2918  status = IntLixTaskIsUserStackPivoted(ParentTask, stackPointer, &CurrentTask->Dpi.IsPivoted);
2919  if (!INT_SUCCESS(status))
2920  {
2921  ERROR("[ERROR] IntLixTaskIsStackPivoted failed for stack ptr 0x%llx with status 0x%08x.", stackPointer, status);
2922  }
2923 }
2924 
2925 
2926 INTSTATUS
2928  _In_ void *Detour
2929  )
2938 {
2939  INTSTATUS status;
2940  QWORD binprm, dPathResult;
2941  BOOLEAN lastAgent;
2942  LIX_TASK_OBJECT *pTask;
2943  LIX_TASK_OBJECT *pOldTask;
2946  QWORD oldProtectionMask;
2947  static DWORD taskCount = 0;
2948 
2949  UNREFERENCED_PARAMETER(Detour);
2950 
2951  pOldTask = IntLixTaskFindByGva(gVcpu->Regs.R8);
2952  if (NULL == pOldTask)
2953  {
2954  ERROR("[ERROR] No task on for exec!\n");
2956  }
2957 
2958  lastAgent = FALSE;
2959  binprm = gVcpu->Regs.R9;
2960  dPathResult = gVcpu->Regs.R10;
2961 
2962  // Keep the old protection mask in order to validate the process creation rights
2963  oldProtectionMask = pOldTask->Protection.Mask;
2964 
2965  // It's certain that the CR3 will change, so disable the protection (doing a full cleanup).
2966  // It will be activated again after we get the new CR3 (if it's still a protected process)
2968 
2969  // no point in keeping it anymore
2970  pOldTask->IsPreviousAgent = FALSE;
2971 
2972  pTask = HpAllocWithTag(sizeof(LIX_TASK_OBJECT), IC_TAG_POBJ);
2973  if (NULL == pTask)
2974  {
2976  }
2977 
2978  status = IntLixTaskCreateFromBinprm(pOldTask, binprm, dPathResult, pTask);
2979  if (!INT_SUCCESS(status))
2980  {
2981  ERROR("[ERROR] Failed updating process contents from the linux_binprm @ %16llx: 0x%08x\n",
2982  binprm, status);
2983  // Add it to the list, still...
2984  }
2985 
2986  // Only now set the comm to whatever we have. Let's hope the #IntLixTaskUpdateFromBinprm didn't fail to
2987  // read the name/path. In that case, there is nothing we can do.
2988  if (pTask->Path)
2989  {
2990  strlcpy(pTask->Comm, pTask->Path->Name, sizeof(pTask->Comm));
2991 
2992  pTask->CommHash = Crc32String(pTask->Comm, INITIAL_CRC_VALUE);
2993  }
2994  else
2995  {
2996  ERROR("[ERROR] We couldn't get path for process, the comm will be the old one!\n");
2997  }
2998 
2999  // Only update here! We check this flag when we update from the binprm.
3000  pTask->Exec = TRUE;
3001 
3002  // Check if this is the init process, and save the new pointer to it.
3003  if (__unlikely((NULL == gLixGuest->InitProcessObj && pTask->Pid == 1) ||
3004  gLixGuest->InitProcessObj == pOldTask))
3005  {
3006  if (gLixGuest->InitProcessObj == pOldTask)
3007  {
3008  TRACE("[INIT] Init process re-executes itself\n");
3009  }
3010 
3011  gLixGuest->InitProcessObj = pTask;
3012  }
3013 
3014  IntLixCredsVerify(pOldTask);
3015 
3017  {
3018  IntLixValidateExecStack(pOldTask, pTask);
3019  }
3020 
3022  {
3023  IntLixValidateProcessCreationRights(pTask, pOldTask, introObjectTypeProcessCreationDpi, &action, &reason);
3024 
3025  if (action == introGuestNotAllowed)
3026  {
3027  goto _action_not_allowed;
3028  }
3029  }
3030 
3031  if (oldProtectionMask & PROC_OPT_PROT_PREVENT_CHILD_CREATION)
3032  {
3033  IntLixValidateProcessCreationRights(pTask, pOldTask, introObjectTypeProcessCreation, &action, &reason);
3034  }
3035 
3036 _action_not_allowed:
3037  if (action == introGuestNotAllowed)
3038  {
3039  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, (QWORD) - EACCES);
3040  if (!INT_SUCCESS(status))
3041  {
3042  ERROR("[ERROR] IntDetSetReturnValue failed: %08x\n", status);
3043  }
3044 
3045  IntLixTaskRemoveEntry(pTask);
3046 
3047  return INT_STATUS_SUCCESS;
3048  }
3049 
3050 
3051  InsertAfterList(&pOldTask->Link, &pTask->Link);
3052  RemoveEntryList(&pOldTask->Link);
3053 
3054  // Now it's safe to reactivate the protection. The new CR3 is in place.
3055  // We also don't depend on the parent anymore.
3056  status = IntLixTaskActivateProtection(pTask, NULL);
3057  if (!INT_SUCCESS(status))
3058  {
3059  ERROR("[ERROR] IntLixTaskActivateProtection failed for %s: 0x%08x\n", pTask->Comm, status);
3060 
3062 
3063  pTask->Protected = FALSE;
3064  }
3065 
3066  if (IntLixTaskMustLog(pTask, pTask->Protected != 0))
3067  {
3068  if (pTask->Interpreter != NULL)
3069  {
3070  LOG("[EXEC] %s %s (%d, %llx, %llx) exec to %s (interp: %s)\n",
3071  pOldTask->IsThread ? "Thread" : "Process", pOldTask->Comm, pTask->Pid, pTask->Cr3, pTask->Gva,
3072  pTask->ProcName, pTask->Interpreter);
3073  }
3074  else
3075  {
3076  LOG("[EXEC] %s %s (%d, %llx, %llx) exec to %s\n",
3077  pOldTask->IsThread ? "Thread" : "Process", pOldTask->Comm, pTask->Pid, pTask->Cr3,
3078  pTask->Gva, pTask->ProcName);
3079  }
3080 
3081  if (pTask->CmdLine)
3082  {
3083  LOG("[EXEC] Task '%s' has command line '%s'\n", pTask->ProcName, pTask->CmdLine);
3084  }
3085  }
3086 
3087  if (pOldTask->AgentTag)
3088  {
3089  size_t oldLen = strlen_s(pTask->Comm, sizeof(pTask->Comm));
3090  size_t newLen = strlen_s(pOldTask->Comm, sizeof(pOldTask->Comm));
3091 
3092  // If it changed the name, then we need to decrement the old agent refcount
3093  // If it didn't change the name, then we must leave it marked as agent (and don't decrement!)
3094  if ((oldLen != newLen) || (0 != memcmp(pOldTask->Comm, pTask->Comm, oldLen)))
3095  {
3096  pTask->AgentTag = IntLixAgentDecProcRef(pOldTask->Comm, &lastAgent);
3097  }
3098  }
3099  else
3100  {
3101  // What if it executed to a new agent !? Simple, we mark this as an agent too...
3102  pTask->AgentTag = IntLixAgentIncProcRef(pTask->Comm);
3103  }
3104 
3105  if (lastAgent)
3106  {
3107  IntLixTaskSendAgentEvent(pTask, 0, FALSE);
3108  }
3109  else if (!pOldTask->AgentTag && pTask->AgentTag)
3110  {
3111  IntLixTaskSendAgentEvent(pTask, 0, TRUE);
3112  }
3113 
3114  IntLixTaskSendTaskEvent(pTask, 0, TRUE, FALSE, FALSE);
3115 
3116  IntLixTaskRemoveEntry(pOldTask);
3117 
3118  if (taskCount == 1)
3119  {
3121  {
3122  status = IntLixVdsoDynamicProtect();
3123  if (!INT_SUCCESS(status))
3124  {
3125  ERROR("[ERROR] IntLixVdsoDynamicProtect failed with status: 0x%08x.", status);
3126  }
3127  }
3128  }
3129  taskCount++;
3130 
3131  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, 0);
3132  if (!INT_SUCCESS(status))
3133  {
3134  ERROR("[ERROR] IntDetSetReturnValue failed: %08x\n", status);
3135  }
3136 
3137  return INT_STATUS_SUCCESS;
3138 }
3139 
3140 
3141 INTSTATUS
3143  _In_ void *Detour
3144  )
3152 {
3153  UNREFERENCED_PARAMETER(Detour);
3154 
3155  INTSTATUS status = IntLixTaskAdd(gVcpu->Regs.R9, FALSE);
3156  if (!INT_SUCCESS(status))
3157  {
3158  ERROR("[ERROR] IntLixTaskAdd failed for %llx on cpu %d: 0x%08x\n",
3159  gVcpu->Regs.R9, gVcpu->Index, status);
3160  }
3161 
3162  return INT_STATUS_SUCCESS;
3163 }
3164 
3165 
3166 static void
3168  _In_ LIX_TASK_OBJECT *Source,
3169  _In_ LIX_TASK_OBJECT *Victim,
3170  _In_ INTRO_ACTION Action,
3171  _In_ INTRO_ACTION_REASON Reason
3172  )
3181 {
3182  INTSTATUS status;
3183  EVENT_MEMCOPY_VIOLATION *pInjEvent;
3184 
3185  pInjEvent = &gAlert.Injection;
3186 
3187  memzero(pInjEvent, sizeof(*pInjEvent));
3188 
3189  pInjEvent->Header.Action = Action;
3190  pInjEvent->Header.Reason = Reason;
3191  pInjEvent->Header.MitreID = idProcInject;
3192 
3194 
3195  pInjEvent->DestinationVirtualAddress = 0;
3196  pInjEvent->SourceVirtualAddress = 0;
3197 
3198  IntAlertFillLixProcess(Source, &pInjEvent->Originator.Process);
3199  IntAlertFillLixProcess(Victim, &pInjEvent->Victim.Process);
3200 
3202 
3203  pInjEvent->Header.Flags = IntAlertProcGetFlags(PROC_OPT_PROT_WRITE_MEM, Victim, Reason, 0);
3204 
3205  pInjEvent->Header.Flags |= ALERT_FLAG_LINUX;
3206  pInjEvent->Header.Flags |= ALERT_FLAG_NOT_RING0;
3207 
3208  IntAlertFillVersionInfo(&pInjEvent->Header);
3209 
3210  status = IntNotifyIntroEvent(introEventInjectionViolation, pInjEvent, sizeof(*pInjEvent));
3211  if (!INT_SUCCESS(status))
3212  {
3213  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
3214  }
3215 }
3216 
3217 
3218 static INTSTATUS
3220  _In_ QWORD Victim,
3221  _In_ BOOLEAN Pid,
3222  _In_ QWORD InjectionFlag,
3223  _Out_ BOOLEAN *Block
3224  )
3235 {
3236  INTSTATUS status;
3237  QWORD currentTask;
3238  LIX_TASK_OBJECT *pSource, *pVictim;
3239  INTRO_ACTION action;
3240  INTRO_ACTION_REASON reason;
3241  EXCEPTION_UM_ORIGINATOR originator;
3242  EXCEPTION_VICTIM_ZONE victim;
3243 
3244  *Block = FALSE;
3245  pSource = pVictim = NULL;
3246 
3247  status = IntLixTaskGetCurrentTaskStruct(gVcpu->Index, &currentTask);
3248  if (!INT_SUCCESS(status))
3249  {
3250  ERROR("[ERROR] IntLixTaskGetCurrentStruct failed: 0x%08x\n", status);
3251  return status;
3252  }
3253 
3254  for_each_task(pProc)
3255  {
3256  if ((Pid && pProc->Pid == (DWORD)Victim) || (!Pid && pProc->Gva == Victim))
3257  {
3258  pVictim = pProc;
3259  }
3260 
3261  if (pProc->Gva == currentTask)
3262  {
3263  pSource = pProc;
3264  }
3265 
3266  if ((NULL != pVictim) && (NULL != pSource))
3267  {
3268  break;
3269  }
3270  }
3271 
3272  if (NULL == pVictim || NULL == pSource)
3273  {
3274  // We don't know these processes
3275  return INT_STATUS_SUCCESS;
3276  }
3277 
3278  if (!(pVictim->Protection.Mask & InjectionFlag))
3279  {
3280  LOG("[PTRACE] Injection from %s (%d, %llx) into %s (%d, %llx)\n",
3281  pSource->ProcName, pSource->Pid, pSource->Gva,
3282  pVictim->ProcName, pVictim->Pid, pVictim->Gva);
3283  return INT_STATUS_SUCCESS;
3284  }
3285 
3287 
3288  // from now on, block by default
3289  action = introGuestNotAllowed;
3290  reason = introReasonUnknown;
3291 
3292  memzero(&originator, sizeof(originator));
3293  memzero(&victim, sizeof(victim));
3294 
3295  status = IntExceptUserGetOriginator(pSource, FALSE, 0, NULL, &originator);
3296  if (!INT_SUCCESS(status))
3297  {
3298  reason = introReasonInternalError;
3299  action = introGuestNotAllowed;
3300 
3301  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
3302  goto _send_notification;
3303  }
3304 
3305  status = IntExceptGetVictimProcess(pVictim, 0, 0, ZONE_WRITE, &victim);
3306 
3307  if (!INT_SUCCESS(status))
3308  {
3309  reason = introReasonInternalError;
3310  action = introGuestNotAllowed;
3311 
3312  ERROR("[ERROR] Failed getting modified zone: 0x%08x\n", status);
3313  goto _send_notification;
3314  }
3315 
3316  IntExcept(&victim, &originator, exceptionTypeUm, &action, &reason, introEventInjectionViolation);
3317 
3318 _send_notification:
3320 
3321  if (IntPolicyProcTakeAction(PROC_OPT_PROT_WRITE_MEM, pVictim, &action, &reason))
3322  {
3323  LOG("[INJECTION] Block injection from process %s (%d) into process %s (%d)\n",
3324  pSource->Comm, pSource->Pid, pVictim->Comm, pVictim->Pid);
3325 
3327  }
3328  else
3329  {
3330  LOG("[INJECTION] Allow injection from process %s into process %s\n",
3331  pSource->Comm, pVictim->Comm);
3332  }
3333 
3334 
3336 
3337  *Block = action == introGuestNotAllowed;
3338 
3339  return INT_STATUS_SUCCESS;
3340 }
3341 
3342 
3343 INTSTATUS
3345  _In_ void *Detour
3346  )
3351 //
3360 {
3361  INTSTATUS status;
3362 
3363  BOOLEAN block = FALSE;
3364 
3365  DWORD pid = (DWORD)gVcpu->Regs.R9;
3366 
3367  status = IntLixTaskHandleInjection(pid, TRUE, PROC_OPT_PROT_WRITE_MEM, &block);
3368  if (!INT_SUCCESS(status))
3369  {
3370  ERROR("[ERROR] IntLixTaskHandleInjection failed: %08x\n", status);
3371  goto _emulate_and_leave;
3372  }
3373 
3374 _emulate_and_leave:
3375  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, block ? -EACCES : 0);
3376  if (!INT_SUCCESS(status))
3377  {
3378  ERROR("[ERROR] IntDetoursGstSetReturnValue failed: 0x%08x\n", status);
3379  }
3380 
3381  return INT_STATUS_SUCCESS;
3382 }
3383 
3384 
3385 INTSTATUS
3387  _In_ void *Detour
3388  )
3398 {
3399  INTSTATUS status;
3400 
3401  BOOLEAN block = FALSE;
3402 
3403  QWORD child = gVcpu->Regs.R8;
3404  QWORD request = gVcpu->Regs.R9;
3405 
3406  if (PTRACE_POKEDATA == request || PTRACE_POKETEXT == request)
3407  {
3408  status = IntLixTaskHandleInjection(child, FALSE, PROC_OPT_PROT_WRITE_MEM, &block);
3409  if (!INT_SUCCESS(status))
3410  {
3411  ERROR("[ERROR] IntLixTaskHandleInjection failed: %08x\n", status);
3412  goto _emulate_and_leave;
3413  }
3414  }
3415  else if (PTRACE_SETFPREGS == request || PTRACE_SETFPXREGS == request || PTRACE_SETREGS == request)
3416  {
3417  status = IntLixTaskHandleInjection(child, FALSE, PROC_OPT_PROT_PTRACE, &block);
3418  if (!INT_SUCCESS(status))
3419  {
3420  ERROR("[ERROR] IntLixTaskHandleSetRegs failed: %08x\n", status);
3421  goto _emulate_and_leave;
3422  }
3423  }
3424  else
3425  {
3426  WARNING("[WARNING] The request argument (%llx) for 'ptrace' is allowed ...\n", request);
3427  block = FALSE;
3428  goto _emulate_and_leave;
3429  }
3430 
3431 _emulate_and_leave:
3432 
3433  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, block ? -EACCES : 0);
3434  if (!INT_SUCCESS(status))
3435  {
3436  ERROR("[ERROR] IntDetoursGstSetReturnValue failed: 0x%08x\n", status);
3437  }
3438 
3439  return INT_STATUS_SUCCESS;
3440 }
3441 
3442 
3443 INTSTATUS
3445  _In_ void *Detour
3446  )
3454 {
3455  UNREFERENCED_PARAMETER(Detour);
3456 
3458  if (NULL == pTask)
3459  {
3460  TRACE("[INFO] Task dying without being in the list... Ignore it!");
3462  }
3463 
3464  if (__unlikely(pTask == gLixGuest->InitProcessObj))
3465  {
3466  LOG("[ERROR] Init task is exiting, something isn't right...\n");
3467  }
3468 
3469  IntLixTaskDestroy(pTask, (DWORD)gVcpu->Regs.R9);
3470 
3471  return INT_STATUS_SUCCESS;
3472 }
3473 
3474 
3475 static INTSTATUS
3477  _In_ QWORD TaskStructGva,
3479  _In_ QWORD Aux
3480  )
3493 
3494 {
3495  INTSTATUS status;
3496  QWORD currentThread;
3497  QWORD signal = 0;
3498  QWORD signalListHead = 0;
3499  int nrThreads = 0;
3500  DWORD count = 0;
3501 
3502  if (gLixGuest->Version.Version >= 3)
3503  {
3504  status = IntKernVirtMemFetchQword(TaskStructGva + LIX_FIELD(TaskStruct, Signal), &signal);
3505  if (!INT_SUCCESS(status))
3506  {
3507  ERROR("[ERROR] Failed reading the signal struct: 0x%08x\n", status);
3508  return status;
3509  }
3510 
3511  if (!IS_KERNEL_POINTER_LIX(signal))
3512  {
3513  ERROR("[ERROR] task->signal value (0x%llx) does not point to a valid kernel memory location.", signal);
3515  }
3516 
3517  status = IntKernVirtMemFetchDword(signal + LIX_FIELD(Ungrouped, SignalNrThreads), (DWORD *)&nrThreads);
3518  if (!INT_SUCCESS(status))
3519  {
3520  ERROR("[ERROR] Failed reading from the signal struct: 0x%08x\n", status);
3521  return status;
3522  }
3523 
3524  // We must subtract the entry that's TaskStructGva
3525  nrThreads--;
3526 
3527  // Only one thread in this signal struct, so skip it
3528  if (nrThreads == 0)
3529  {
3531  }
3532  else if (nrThreads < 0)
3533  {
3534  ERROR("[ERROR] Negative number of threads: %d\n", nrThreads);
3536  }
3537  }
3538 
3539  signalListHead = signal + LIX_FIELD(Ungrouped, SignalListHead);
3540 
3541  status = IntKernVirtMemFetchQword(signalListHead, &currentThread);
3542  if (!INT_SUCCESS(status))
3543  {
3544  ERROR("[ERROR] Failed getting the first task from signal 0x%016llx\n", signalListHead);
3545  return status;
3546  }
3547 
3548  currentThread -= LIX_FIELD(TaskStruct, ThreadNode);
3549 
3550  // This one was already added, so skip it and go to the next one
3551  status = IntKernVirtMemFetchQword(currentThread + LIX_FIELD(TaskStruct, ThreadNode), &currentThread);
3552  if (!INT_SUCCESS(status))
3553  {
3554  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3555  currentThread + LIX_FIELD(TaskStruct, ThreadNode));
3556  return status;
3557  }
3558 
3559  while ((currentThread != signalListHead) && (count++ < LIX_PROCESSES_MAX_COUNT))
3560  {
3561  if (!IS_KERNEL_POINTER_LIX(currentThread))
3562  {
3563  ERROR("[ERROR] Thread 0x%llx does not point to a valid kernel memory location.\n", currentThread);
3565  }
3566 
3567  currentThread -= LIX_FIELD(TaskStruct, ThreadNode);
3568 
3569  status = Callback(currentThread, Aux);
3570  if (!INT_SUCCESS(status))
3571  {
3572  return status;
3573  }
3574  else if (INT_STATUS_BREAK_ITERATION == status)
3575  {
3576  return INT_STATUS_SUCCESS;
3577  }
3578 
3579  nrThreads--;
3580 
3581  status = IntKernVirtMemFetchQword(currentThread + LIX_FIELD(TaskStruct, ThreadNode), &currentThread);
3582  if (!INT_SUCCESS(status))
3583  {
3584  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3585  currentThread + LIX_FIELD(TaskStruct, ThreadNode));
3586  break;
3587  }
3588  }
3589 
3590  if (gLixGuest->Version.Version >= 3)
3591  {
3592  if (nrThreads > 0)
3593  {
3594  ERROR("[ERROR] We didn't processed enough threads. Remaining: %d\n", nrThreads);
3596  }
3597  else if (nrThreads < 0)
3598  {
3599  ERROR("[ERROR] We processed more threads. Over: %d\n", nrThreads);
3601  }
3602  }
3603 
3604  return INT_STATUS_SUCCESS;
3605 }
3606 
3607 
3608 static INTSTATUS
3610  _In_ QWORD TaskStructGva,
3612  _In_ QWORD Aux
3613  )
3626 {
3627  INTSTATUS status;
3628  QWORD currentThread;
3629  QWORD signal, threadListHead;
3630  DWORD count = 0;
3631  int nrThreads = 0;
3632 
3633  if (gLixGuest->Version.Version >= 3)
3634  {
3635  status = IntKernVirtMemFetchQword(TaskStructGva + LIX_FIELD(TaskStruct, Signal), &signal);
3636  if (!INT_SUCCESS(status))
3637  {
3638  ERROR("[ERROR] Failed reading the signal struct: 0x%08x\n", status);
3639  return status;
3640  }
3641 
3642  if (!IS_KERNEL_POINTER_LIX(signal))
3643  {
3644  ERROR("[ERROR] task->signal value (0x%llx) does not point to a valid kernel memory location.", signal);
3646  }
3647 
3648  status = IntKernVirtMemFetchDword(signal + LIX_FIELD(Ungrouped, SignalNrThreads), (DWORD *)&nrThreads);
3649  if (!INT_SUCCESS(status))
3650  {
3651  ERROR("[ERROR] Failed reading from the signal struct: 0x%08x\n", status);
3652  return status;
3653  }
3654 
3655  // We must subtract the entry that's TaskStructGva
3656  nrThreads--;
3657 
3658  // Only one thread in this signal struct, so skip it
3659  if (nrThreads == 0)
3660  {
3662  }
3663  else if (nrThreads < 0)
3664  {
3665  ERROR("[ERROR] Negative number of threads: %d\n", nrThreads);
3667  }
3668  }
3669 
3670  threadListHead = TaskStructGva + LIX_FIELD(TaskStruct, ThreadGroup);
3671 
3672  status = IntKernVirtMemFetchQword(threadListHead, &currentThread);
3673  if (!INT_SUCCESS(status))
3674  {
3675  ERROR("[ERROR] Failed getting the first task from signal 0x%016llx\n", threadListHead);
3676  return status;
3677  }
3678 
3679  while (currentThread && (currentThread != threadListHead) && (count++ < LIX_PROCESSES_MAX_COUNT))
3680  {
3681  if (!IS_KERNEL_POINTER_LIX(currentThread))
3682  {
3683  ERROR("[ERROR] Thread 0x%llx does not point to a valid kernel memory location.\n", currentThread);
3685  }
3686 
3687  currentThread -= LIX_FIELD(TaskStruct, ThreadGroup);
3688 
3689  status = Callback(currentThread, Aux);
3690  if (!INT_SUCCESS(status))
3691  {
3692  return status;
3693  }
3694  else if (INT_STATUS_BREAK_ITERATION == status)
3695  {
3696  return INT_STATUS_SUCCESS;
3697  }
3698 
3699  nrThreads--;
3700 
3701  status = IntKernVirtMemFetchQword(currentThread + LIX_FIELD(TaskStruct, ThreadGroup), &currentThread);
3702  if (!INT_SUCCESS(status))
3703  {
3704  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3705  currentThread + LIX_FIELD(TaskStruct, ThreadGroup));
3706  break;
3707  }
3708  }
3709 
3710  if (gLixGuest->Version.Version >= 3)
3711  {
3712  if (nrThreads > 0)
3713  {
3714  ERROR("[ERROR] We didn't processed enough threads. Remaining: %d\n", nrThreads);
3716  }
3717  else if (nrThreads < 0)
3718  {
3719  ERROR("[ERROR] We processed more threads. Over: %d\n", nrThreads);
3721  }
3722  }
3723 
3724  return INT_STATUS_SUCCESS;
3725 }
3726 
3727 
3728 static INTSTATUS
3730  _In_ QWORD TaskStructGva,
3732  _In_ QWORD Aux
3733  )
3744 {
3745  if (0 != LIX_FIELD(TaskStruct, ThreadGroup))
3746  {
3747  return IntLixTaskIterateThreadGroup(TaskStructGva, Callback, Aux);
3748  }
3749  else if (0 != LIX_FIELD(TaskStruct, ThreadNode))
3750  {
3751  return IntLixTaskIterateThreadNode(TaskStructGva, Callback, Aux);
3752  }
3753  else
3754  {
3755  // WARNING("[WARNING] IntLixTaskIterateThreads while not a single process was created!\n");
3757  }
3758 }
3759 
3760 
3761 INTSTATUS
3764  _In_ QWORD Aux
3765  )
3775 {
3776  INTSTATUS status;
3777  QWORD initGva, currentTask;
3778  DWORD count = 0;
3779 
3780  status = IntLixGetInitTask(&initGva);
3781  if (!INT_SUCCESS(status))
3782  {
3783  ERROR("[ERROR] Failed finding the init_task: 0x%08x\n", status);
3784  return status;
3785  }
3786 
3787  //
3788  // IMPORTANT: We must call the callback for the init_task too!
3789  //
3790  status = Callback(initGva, Aux);
3791  if (!INT_SUCCESS(status))
3792  {
3793  return status;
3794  }
3795  else if (INT_STATUS_BREAK_ITERATION == status)
3796  {
3797  return INT_STATUS_SUCCESS;
3798  }
3799 
3800  status = IntLixTaskIterateThreads(initGva, Callback, Aux);
3801  if (!INT_SUCCESS(status))
3802  {
3803  return status;
3804  }
3805 
3806  status = IntKernVirtMemFetchQword(initGva + LIX_FIELD(TaskStruct, Tasks), &currentTask);
3807  if (!INT_SUCCESS(status))
3808  {
3809  ERROR("[ERROR] Failed getting the first task from 0x%016llx\n", initGva + LIX_FIELD(TaskStruct, Tasks));
3810  return status;
3811  }
3812 
3813  currentTask -= LIX_FIELD(TaskStruct, Tasks);
3814 
3815  while (currentTask != initGva && (count++ < LIX_PROCESSES_MAX_COUNT))
3816  {
3817  if (!IS_KERNEL_POINTER_LIX(currentTask))
3818  {
3819  ERROR("[ERROR] task_struct 0x%llx does not point to a valid kernel memory location.\n", currentTask);
3821  }
3822 
3823  status = Callback(currentTask, Aux);
3824  if (!INT_SUCCESS(status))
3825  {
3826  return status;
3827  }
3828  else if (INT_STATUS_BREAK_ITERATION == status)
3829  {
3830  return INT_STATUS_SUCCESS;
3831  }
3832 
3833  IntLixTaskIterateThreads(currentTask, Callback, Aux);
3834 
3835  status = IntKernVirtMemFetchQword(currentTask + LIX_FIELD(TaskStruct, Tasks), &currentTask);
3836  if (!INT_SUCCESS(status))
3837  {
3838  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3839  currentTask + LIX_FIELD(TaskStruct, Tasks));
3840  break;
3841  }
3842 
3843  currentTask -= LIX_FIELD(TaskStruct, Tasks);
3844  }
3845 
3846  if (count >= LIX_PROCESSES_MAX_COUNT)
3847  {
3848  return INT_STATUS_NOT_SUPPORTED;
3849  }
3850 
3851  return INT_STATUS_SUCCESS;
3852 }
3853 
3854 
3855 static INTSTATUS
3857  _In_ QWORD TaskGva,
3858  _Out_opt_ LIX_TASK_OBJECT **Task
3859  )
3869 {
3870  INTSTATUS status;
3871  LIX_TASK_OBJECT *pInitTask;
3872 
3873  pInitTask = HpAllocWithTag(sizeof(*pInitTask), IC_TAG_POBJ);
3874  if (NULL == pInitTask)
3875  {
3877  }
3878 
3879  pInitTask->Gva = TaskGva;
3880  TRACE("[LIXTASK] Init task @ 0x%016llx\n", pInitTask->Gva);
3881 
3882  // Linux convention...
3883  pInitTask->Parent = TaskGva;
3884  pInitTask->RealParent = TaskGva;
3885 
3886  pInitTask->Path = NULL;
3887  pInitTask->Interpreter = NULL;
3888  pInitTask->IsThread = TRUE;
3889  pInitTask->KernelMode = TRUE;
3890  pInitTask->Protection.Mask = 0;
3891  pInitTask->Protected = FALSE;
3892  pInitTask->Context = 0;
3893 
3894  status = IntKernVirtMemFetchDword(pInitTask->Gva + LIX_FIELD(TaskStruct, Pid), &pInitTask->Pid);
3895  if (!INT_SUCCESS(status))
3896  {
3897  ERROR("[ERROR] Failed getting PID of the init process @0x%016llx: 0x%08x\n",
3898  pInitTask->Gva, status);
3899  }
3900 
3901  status = IntKernVirtMemRead(pInitTask->Gva + LIX_FIELD(TaskStruct, Comm),
3902  sizeof(pInitTask->Comm),
3903  pInitTask->Comm,
3904  NULL);
3905 
3906  if (!INT_SUCCESS(status))
3907  {
3908  ERROR("[ERROR] Failed reading init process name @ 0x%016llx: 0x%08x\n",
3909  pInitTask->Gva + LIX_FIELD(TaskStruct, Comm), status);
3910  pInitTask->Comm[0] = 0;
3911  }
3912 
3913  // Make sure the NULL terminator is there.
3914  pInitTask->Comm[sizeof(pInitTask->Comm) - 1] = 0;
3915 
3916  pInitTask->CommHash = Crc32String(pInitTask->Comm, INITIAL_CRC_VALUE);
3917 
3918  if ((0 == LIX_FIELD(TaskStruct, ThreadGroup)) && (0 == LIX_FIELD(TaskStruct, ThreadNode)))
3919  {
3920  QWORD signal, flink;
3921 
3922  status = IntKernVirtMemFetchQword(TaskGva + LIX_FIELD(TaskStruct, Signal), &signal);
3923  if (!INT_SUCCESS(status))
3924  {
3925  ERROR("[ERROR] Failed reading the init's signal struct: 0x%08x\n", status);
3926  return status;
3927  }
3928 
3929  status = IntKernVirtMemFetchQword(signal + LIX_FIELD(Ungrouped, SignalListHead), &flink);
3930  if (!INT_SUCCESS(status))
3931  {
3932  ERROR("[ERROR] Failed reading from init's signal struct: 0x%08x\n", status);
3933  return status;
3934  }
3935 
3936  // Sanity check: 'signal' field is below sizeof(task_struct), which for now we assume that of PAGE_SIZE
3937  if (flink - TaskGva > PAGE_SIZE)
3938  {
3939  ERROR("[ERROR] Signal's struct is not good: 0x%016llx 0x%016llx 0x%016llx\n", flink, signal, TaskGva);
3940  return INT_STATUS_NOT_SUPPORTED;
3941  }
3942 
3943  // Detect dynamically the thread node
3944  LIX_FIELD(TaskStruct, ThreadNode) = (DWORD)(flink - TaskGva);
3945  }
3946 
3947  InsertTailList(&gLixTasks, &pInitTask->Link);
3948 
3949  if (Task)
3950  {
3951  *Task = pInitTask;
3952  }
3953 
3954  return INT_STATUS_SUCCESS;
3955 }
3956 
3957 
3958 INTSTATUS
3960  _In_ QWORD TaskGva,
3961  _In_ QWORD StaticDetected
3962  )
3972 {
3973  INTSTATUS status;
3974 
3975  if (!IS_KERNEL_POINTER_LIX(TaskGva))
3976  {
3978  }
3979 
3980  // This is the first call to this function, so create the init task
3981  if (__unlikely(StaticDetected && IsListEmpty(&gLixTasks)))
3982  {
3983  status = IntLixTaskCreateInitTask(TaskGva, NULL);
3984  if (!INT_SUCCESS(status))
3985  {
3986  ERROR("[ERROR] IntLixTaskCreateInitTask failed: 0x%08x\n", status);
3987  return status;
3988  }
3989  }
3990  else
3991  {
3992  QWORD parentTs, realParentTs;
3993  LIX_TASK_OBJECT *pParent, *pRealParent;
3994 
3995  parentTs = realParentTs = 0;
3996 
3997  status = _IntLixTaskStartMap(TaskGva);
3998  if (!INT_SUCCESS(status))
3999  {
4000  ERROR("[ERROR] _IntLixTaskStartMap failed for %llx: %08x\n", TaskGva, status);
4001  return status;
4002  }
4003 
4004  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, RealParent), sizeof(realParentTs), &realParentTs);
4005  if (!INT_SUCCESS(status))
4006  {
4007  ERROR("[ERROR] Failed getting the real parent: %08x\n", status);
4008  goto _finish_task;
4009  }
4010 
4011  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Parent), sizeof(parentTs), &parentTs);
4012  if (!INT_SUCCESS(status))
4013  {
4014  ERROR("[ERROR] Failed getting the parent: %08x\n", status);
4015  goto _finish_task;
4016  }
4017 
4018  pParent = IntLixTaskFindByGva(parentTs);
4019  if (NULL == pParent)
4020  {
4021  WARNING("[WARNING] IntLixTaskFindByGva failed for parent 0x%016llx\n", parentTs);
4022 
4023  pParent = IntLixTaskFindByPid(1);
4024  if (NULL == pParent)
4025  {
4026  ERROR("[ERROR] IntLixTaskFindByPid failed for PID 1!\n");
4027  goto _finish_task;
4028  }
4029  }
4030  else
4031  {
4032  IntLixCredsVerify(pParent);
4033  }
4034 
4035  if (parentTs == realParentTs)
4036  {
4037  pRealParent = pParent;
4038  }
4039  else
4040  {
4041  pRealParent = IntLixTaskFindByGva(realParentTs);
4042 
4043  if (NULL == pRealParent)
4044  {
4045  WARNING("[WARNING] IntLixTaskFindByGva failed for real parent 0x%016llx\n", realParentTs);
4046  pRealParent = pParent;
4047  }
4048  else
4049  {
4050  IntLixCredsVerify(pRealParent);
4051  }
4052  }
4053 
4054  status = IntLixTaskCreate(pParent, pRealParent, TaskGva, StaticDetected != 0, NULL);
4055  if (!INT_SUCCESS(status))
4056  {
4057  ERROR("[ERROR] IntLixTaskCreate failed: 0x%08x\n", status);
4058  }
4059 
4060 _finish_task:
4062  }
4063 
4064  return status;
4065 }
4066 
4067 
4068 static INTSTATUS
4070  _In_ LIX_TASK_OBJECT *Task,
4071  _In_ QWORD NewProtection,
4072  _In_ QWORD NewRootProtection,
4073  _In_ QWORD Context
4074  )
4086 {
4087  INTSTATUS status;
4088  QWORD oldProtection;
4089 
4090  oldProtection = Task->Protection.Mask;
4091 
4092  if (NewProtection == oldProtection)
4093  {
4095  }
4096 
4097  if (0 == NewProtection)
4098  {
4099  LOG("[PROT] Removing %s %s (%llx, %llx, %d) from protection", Task->Exec ? "exec process" : "fork process",
4100  Task->Comm, Task->Gva, Task->Cr3, Task->Pid);
4101 
4103 
4104  Task->Protected = FALSE;
4105 
4106  return INT_STATUS_SUCCESS;
4107  }
4108 
4109  LOG("[PROT] Changing protection flags for `%s` (Pid %d, ts 0x%016llx): 0x%llx -> 0x%llx\n",
4110  Task->Comm, Task->Pid, Task->Gva, oldProtection, NewProtection);
4111 
4112  if ((PROC_OPT_PROT_EXPLOIT & NewProtection) != (PROC_OPT_PROT_EXPLOIT & oldProtection))
4113  {
4114  if (0 != (PROC_OPT_PROT_EXPLOIT & NewProtection))
4115  {
4116  LOG("[PROT] PROC_OPT_PROT_EXPLOIT disabled -> enabled for %s (%llx, %d)\n",
4117  Task->Comm, Task->Cr3, Task->Pid);
4118 
4119  Task->Protection.Mask |= PROC_OPT_PROT_EXPLOIT;
4120 
4121  status = IntLixTaskActivateExploitProtection(Task);
4122  if (!INT_SUCCESS(status))
4123  {
4124  WARNING("[WARNING] Process '%s' (%d, %llx, %llx) will not be exploit-protected: %08x!\n",
4125  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
4126 
4127  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
4128  }
4129  }
4130  else
4131  {
4132  LOG("[PROT] PROC_OPT_PROT_EXPLOIT enabled -> disabled for %s (%llx, %d)\n",
4133  Task->Comm, Task->Cr3, Task->Pid);
4134 
4136  if (!INT_SUCCESS(status))
4137  {
4138  ERROR("[ERROR] Process '%s' (%d, %llx, %llx) failed to deactivate protection: %08x\n",
4139  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
4140  }
4141 
4142  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
4143  }
4144  }
4145 
4146  if ((PROC_OPT_REMEDIATE & NewProtection) != (PROC_OPT_REMEDIATE & oldProtection))
4147  {
4148  LOG("[PROT] PROC_OPT_REMEDIATE %s for process %s, %d\n",
4149  0 != (PROC_OPT_REMEDIATE & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4150  Task->Comm, Task->Pid);
4151  }
4152 
4153  if ((PROC_OPT_KILL_ON_EXPLOIT & NewProtection) != (PROC_OPT_KILL_ON_EXPLOIT & oldProtection))
4154  {
4155  LOG("[PROCESS] PROC_OPT_KILL_ON_EXPLOIT %s for process %s, %d\n",
4156  0 != (PROC_OPT_KILL_ON_EXPLOIT & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4157  Task->Comm, Task->Pid);
4158  }
4159 
4160  if ((PROC_OPT_PROT_WRITE_MEM & NewProtection) != (PROC_OPT_PROT_WRITE_MEM & oldProtection))
4161  {
4162  LOG("[PROCESS] PROC_OPT_PROT_WRITE_MEM %s for process %s, %d\n",
4163  0 != (PROC_OPT_PROT_WRITE_MEM & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4164  Task->Comm, Task->Pid);
4165  }
4166 
4167  if ((PROC_OPT_PROT_PTRACE & NewProtection) != (PROC_OPT_PROT_PTRACE & oldProtection))
4168  {
4169  LOG("[PROCESS] PROC_OPT_PROT_PTRACE %s for process %s, %d\n",
4170  0 != (PROC_OPT_PROT_PTRACE & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4171  Task->Comm, Task->Pid);
4172  }
4173 
4174  if ((PROC_OPT_BETA & NewProtection) != (PROC_OPT_BETA & oldProtection))
4175  {
4176  LOG("[PROCESS] PROC_OPT_BETA %s for process %s, %d\n",
4177  0 != (PROC_OPT_BETA & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4178  Task->Comm, Task->Pid);
4179  }
4180 
4181  Task->Protected = TRUE;
4182  Task->Protection.Mask = NewProtection;
4183  Task->RootProtectionMask = NewRootProtection;
4184  Task->Context = Context;
4185 
4186  return INT_STATUS_SUCCESS;
4187 }
4188 
4189 
4190 static INTSTATUS
4192  _In_ const LIX_PROTECTED_PROCESS *ProtProc,
4193  _In_ BOOLEAN Remove
4194  )
4204 {
4206  {
4208  }
4209 
4210  for_each_task(pTask)
4211  {
4212  INTSTATUS status;
4213  QWORD protMask, childProtMask, protBetaMask, protFeedbackMask;
4214  QWORD context;
4215 
4216  if (pTask->IsThread)
4217  {
4218  continue;
4219  }
4220 
4221  if (!(ProtProc->NamePattern && pTask->Path &&
4222  IntMatchPatternUtf8(ProtProc->NamePattern, pTask->Path->Name, 0)) &&
4223  (!IntMatchPatternUtf8(ProtProc->CommPattern, pTask->Comm, INTRO_MATCH_TRUNCATED)))
4224  {
4225  continue;
4226  }
4227 
4228  protMask = Remove ? 0 : ProtProc->Protection.Current;
4229  context = Remove ? 0 : ProtProc->Context;
4230  protBetaMask = Remove ? 0 : ProtProc->Protection.Beta;
4231  protFeedbackMask = Remove ? 0 : ProtProc->Protection.Feedback;
4232 
4233  if (pTask->Context != context)
4234  {
4235  pTask->Context = context;
4236  }
4237 
4238  pTask->Protection.Beta = protBetaMask;
4239  pTask->Protection.Feedback = protFeedbackMask;
4240 
4241  if (pTask->Protection.Mask == protMask)
4242  {
4243  continue;
4244  }
4245 
4246  status = IntLixTaskChangeProtectionFlags(pTask, protMask, protMask, context);
4247  if (!INT_SUCCESS(status))
4248  {
4249  ERROR("[ERROR] IntLixTaskChangeProtectionFlags failed for 0x%016llx (Cr3 0x%016llx): 0x%08x\n",
4250  pTask->Gva, pTask->Cr3, status);
4251  }
4252 
4253  // Children should be further down in list (not before this)
4254  for_next_task(pTask, pChild)
4255  {
4256  if (pChild->Exec || pChild->ActualParent != pTask->Gva)
4257  {
4258  continue;
4259  }
4260 
4261  if (Remove)
4262  {
4263  childProtMask = 0;
4264  pChild->Protection.Beta = 0;
4265  pChild->Protection.Feedback = 0;
4266  }
4267  else
4268  {
4269  // Need to reset it every time (it may change below for the current child)
4270  childProtMask = pTask->Protection.Mask;
4271  pChild->Protection.Beta = pTask->Protection.Beta;
4272  pChild->Protection.Feedback = pTask->Protection.Feedback;
4273 
4274  context = pTask->Context;
4275 
4276  if (pTask->Cr3 == pChild->Cr3 || 0 == pChild->Cr3)
4277  {
4278  childProtMask &= ~PROC_OPT_PROT_EXPLOIT;
4279  childProtMask &= ~PROC_OPT_PROT_CORE_HOOKS;
4280  }
4281  }
4282 
4283  status = IntLixTaskChangeProtectionFlags(pChild, childProtMask, pTask->RootProtectionMask, context);
4284  if (!INT_SUCCESS(status))
4285  {
4286  ERROR("[ERROR] IntLixTaskChangeProtectionFlags failed for 0x%016llx (Cr3 0x%016llx): 0x%08x\n",
4287  pChild->Gva, pChild->Cr3, status);
4288  }
4289  }
4290  }
4291 
4292  return INT_STATUS_SUCCESS;
4293 }
4294 
4295 
4296 INTSTATUS
4298  _In_ const char *ProcessName,
4299  _In_ QWORD ProtectionMask,
4300  _In_ QWORD Context
4301  )
4314 {
4315  size_t nameLen;
4316  INTSTATUS status;
4317  LIX_PROTECTED_PROCESS *pProt = NULL;
4318 
4319  if (NULL == ProcessName)
4320  {
4322  }
4323 
4324  nameLen = strlen(ProcessName);
4325 
4326  if (nameLen >= 64 * ONE_KILOBYTE)
4327  {
4328  ERROR("[ERROR] Names longer than 64K are not supported!\n");
4329  return INT_STATUS_NOT_SUPPORTED;
4330  }
4331 
4332  for_each_task_to_protect(pExtProt)
4333  {
4334  if ((pExtProt->NamePattern && 0 == strncasecmp(pExtProt->NamePattern, ProcessName, nameLen + 1)) ||
4335  0 == strncasecmp(pExtProt->CommPattern, ProcessName, MIN(LIX_COMM_SIZE, nameLen + 1)))
4336  {
4337  LOG("[PROT] Process %s already protected as %s with %llx... Update the protection to %llx\n",
4338  ProcessName, pExtProt->NamePattern ? pExtProt->NamePattern : pExtProt->CommPattern,
4339  pExtProt->Protection.Original, ProtectionMask);
4340 
4341  pExtProt->Protection.Original = ProtectionMask;
4342  pExtProt->Protection.Current = ProtectionMask;
4343  pExtProt->Protection.Beta = 0;
4344  pExtProt->Protection.Feedback = 0;
4345 
4346  pExtProt->Context = Context;
4347  pProt = pExtProt;
4348 
4349  break;
4350  }
4351  }
4352 
4353  // If not found, add a new entry
4354  if (NULL == pProt)
4355  {
4356  pProt = HpAllocWithTag(sizeof(*pProt), IC_TAG_POBJ);
4357  if (NULL == pProt)
4358  {
4360  }
4361 
4362  pProt->NamePattern = HpAllocWithTag(nameLen + 1, IC_TAG_NAME);
4363  if (NULL == pProt->NamePattern)
4364  {
4365  ERROR("[ERROR] Process '%s' will not be protected as there is not enough memory available\n", ProcessName);
4366 
4368 
4370  }
4371 
4372  strlcpy(pProt->NamePattern, ProcessName, nameLen + 1);
4373 
4374  strlcpy(pProt->CommPattern, ProcessName, sizeof(pProt->CommPattern));
4375 
4376  pProt->Protection.Original = ProtectionMask;
4377  pProt->Protection.Current = ProtectionMask;
4378  pProt->Protection.Beta = 0;
4379  pProt->Protection.Feedback = 0;
4380 
4381  pProt->Context = Context;
4382 
4384 
4385  LOG("[PROT] Process %s / %s protected with %llx\n",
4386  pProt->CommPattern, pProt->NamePattern, pProt->Protection.Original);
4387 
4388  InsertTailList(&gLixTasksToProtect, &pProt->Link);
4389  }
4390 
4391  status = IntLixTaskAdjustProtections(pProt, FALSE);
4392  if (!INT_SUCCESS(status))
4393  {
4394  ERROR("[ERROR] IntLixTaskAdjustProtection failed for '%s': %08x\n", pProt->CommPattern, status);
4395  }
4396 
4397  return INT_STATUS_SUCCESS;
4398 }
4399 
4400 
4401 INTSTATUS
4403  _In_ const char *ProcessName
4404  )
4413 {
4414  INTSTATUS status;
4415  size_t nameLen;
4416  LIX_PROTECTED_PROCESS *pProt = NULL;
4417 
4418  if (NULL == ProcessName)
4419  {
4421  }
4422 
4423  nameLen = strlen(ProcessName);
4424 
4425  for_each_task_to_protect(pExtProt)
4426  {
4427  if (0 == strncasecmp(pExtProt->NamePattern, ProcessName, nameLen + 1))
4428  {
4429  LOG("Remove process %s from protected list!\n", pExtProt->NamePattern);
4430 
4431  pProt = pExtProt;
4432  break;
4433  }
4434  }
4435 
4436  if (NULL == pProt)
4437  {
4438  return INT_STATUS_NOT_FOUND;
4439  }
4440 
4441  status = IntLixTaskAdjustProtections(pProt, TRUE);
4442  if (!INT_SUCCESS(status))
4443  {
4444  ERROR("[ERROR] IntLixTaskAdjustProtection failed for '%s': %08x\n", pProt->CommPattern, status);
4445  }
4446 
4447  RemoveEntryList(&pProt->Link);
4448 
4450 
4452 
4453  return INT_STATUS_SUCCESS;
4454 }
4455 
4456 
4457 void
4459  void
4460  )
4464 {
4465  INTSTATUS status;
4466 
4467  for_each_task(pTask)
4468  {
4469  const LIX_PROTECTED_PROCESS *pProt = IntLixTaskShouldProtect(pTask);
4470 
4471  if (NULL == pProt)
4472  {
4474  }
4475  else
4476  {
4477  status = IntLixTaskAdjustProtections(pProt, FALSE);
4478  if (!INT_SUCCESS(status))
4479  {
4480  ERROR("[ERROR] IntLixTaskAdjustProtection failed for '%s': %08x\n", pProt->CommPattern, status);
4481  }
4482  }
4483  }
4484 }
4485 
4486 
4487 INTSTATUS
4489  _Out_writes_bytes_(Length) char *CommandLine,
4490  _In_ DWORD Length
4491  )
4501 {
4502  char *cmd = CommandLine;
4503 
4504  for_each_task(pTask)
4505  {
4506  INT32 len;
4507 
4508  if (!pTask->AgentTag)
4509  {
4510  continue;
4511  }
4512 
4513  len = snprintf(cmd, Length, "%s %d ", pTask->Path ? pTask->Path->Name : pTask->Comm, pTask->Pid);
4514  Length -= len;
4515  cmd += len;
4516 
4517  if ((int)Length < 0)
4518  {
4520  }
4521  }
4522 
4523  return INT_STATUS_SUCCESS;
4524 }
4525 
4526 
4527 void
4529  void
4530  )
4534 {
4535  for_each_task(pTask)
4536  {
4537  IntLixTaskMarkAgent(pTask);
4538 
4539  RemoveEntryList(&pTask->Link);
4540 
4541  IntLixTaskRemoveEntry(pTask);
4542  }
4543 
4545  {
4546  RemoveEntryList(&pProt->Link);
4547 
4548  if (NULL != pProt->NamePattern)
4549  {
4550  HpFreeAndNullWithTag(&pProt->NamePattern, IC_TAG_NAME);
4551  }
4552 
4554  }
4555 }
4556 
4557 
4558 static void
4560  _In_opt_ LIX_TASK_OBJECT *Task,
4561  _In_ DWORD Level
4562  )
4569 {
4570  DWORD flags;
4571  INTSTATUS status;
4572 
4573  // Search the first process and start there
4574  if (NULL == Task)
4575  {
4576  for_each_task(pProc)
4577  {
4578  if (!pProc->IsThread)
4579  {
4580  Task = pProc;
4581  break;
4582  }
4583  }
4584  }
4585 
4586  if (NULL == Task)
4587  {
4588  LOG("We have no processes in the system!\n");
4589  return;
4590  }
4591 
4592  if (Level >= 2)
4593  {
4594  for (DWORD i = 0; i < Level - 1; i++)
4595  {
4596  NLOG("--");
4597  }
4598 
4599  NLOG("--| ");
4600  }
4601  else if (Level >= 1)
4602  {
4603  NLOG("| ");
4604  }
4605 
4606  status = IntKernVirtMemFetchDword(Task->Gva + LIX_FIELD(TaskStruct, Flags), &flags);
4607  if (!INT_SUCCESS(status))
4608  {
4609  flags = 0;
4610  }
4611 
4612  if (!Task->IsThread)
4613  {
4614  NLOG("%6d/%-6d: %s %-40s CR3: 0x%016llx, prot: %d/%llx, mm_struct: 0x%016llx, task_struct: 0x%016llx, "
4615  "flags: %08x%s%s, parent: 0x%016llx, real_parent: 0x%016llx\n",
4616  Task->Pid, Task->Tgid,
4617  Task->Exec ? "EXEC" : "FORK",
4618  Task->ProcName,
4619  Task->Cr3,
4620  Task->Protected, Task->Protection.Mask,
4621  Task->MmGva,
4622  Task->Gva,
4623  flags,
4624  Task->Interpreter ? ", by interpreter " : "",
4625  Task->Interpreter ? Task->Interpreter : "",
4626  Task->Parent, Task->RealParent);
4627  }
4628  else
4629  {
4630  char newComm[LIX_COMM_SIZE] = {0};
4631 
4632  status = IntKernVirtMemRead(Task->Gva + LIX_FIELD(TaskStruct, Comm),
4633  sizeof(newComm),
4634  newComm,
4635  NULL);
4636  if (!INT_SUCCESS(status))
4637  {
4638  newComm[0] = '\0';
4639  }
4640 
4641  NLOG("%6d/%-6d: %-16s / %-16s task_struct: 0x%016llx, prot: %d/%llx, flags: %08x, parent: 0x%016llx, "
4642  "real_parent: 0x%016llx\n",
4643  Task->Pid, Task->Tgid, Task->Comm, newComm, Task->Gva, Task->Protected,
4644  Task->Protection.Mask, flags, Task->Parent, Task->RealParent);
4645  }
4646 
4647  for_each_task(pProc)
4648  {
4649  if (pProc == Task)
4650  {
4651  continue;
4652  }
4653 
4654  if (!pProc->IsThread && pProc->Parent == Task->Gva)
4655  {
4656  IntLixTaskDumpTree(pProc, Level + 1);
4657  }
4658  else if (pProc->IsThread && pProc->Tgid == Task->Pid)
4659  {
4660  if (Task->IsThread)
4661  {
4662  IntLixTaskDumpTree(pProc, Level);
4663  }
4664  else
4665  {
4666  IntLixTaskDumpTree(pProc, Level + 1);
4667  }
4668  }
4669  }
4670 }
4671 
4672 
4673 static void
4675  _In_opt_ LIX_TASK_OBJECT *Thread,
4676  _In_ DWORD Level
4677  )
4687 {
4688  DWORD i, flags;
4689  INTSTATUS status;
4690 
4691  // Search the first kernel thread and start there
4692  if (NULL == Thread)
4693  {
4694  for_each_task(pThread)
4695  {
4696  if (pThread->IsThread && pThread->KernelMode)
4697  {
4698  Thread = pThread;
4699  break;
4700  }
4701  }
4702  }
4703 
4704  if (NULL == Thread || !Thread->IsThread || !Thread->KernelMode)
4705  {
4706  if (Level == 0)
4707  {
4708  LOG("We have no kernel thread in the system!\n");
4709  }
4710 
4711  return;
4712  }
4713 
4714  for (i = 0; i < Level; i++)
4715  {
4716  NLOG("--");
4717  }
4718 
4719  NLOG("> ");
4720 
4721  status = IntKernVirtMemFetchDword(Thread->Gva + 2 * sizeof(QWORD) + sizeof(DWORD), &flags);
4722  if (!INT_SUCCESS(status))
4723  {
4724  flags = 0;
4725  }
4726 
4727  NLOG("%6d/%-6d : %-16s task_struct: 0x%016llx, prot: %d/%llx, flags: %08x\n",
4728  Thread->Pid, Thread->Tgid, Thread->Comm, Thread->Gva,
4729  Thread->Protected, Thread->Protection.Mask, flags);
4730 
4731  for_each_task(pThread)
4732  {
4733  if (pThread->Parent == Thread->Gva)
4734  {
4735  IntLixTaskDumpKernelThreadTree(pThread, Level + 1);
4736  }
4737  }
4738 }
4739 
4740 
4741 void
4743  void
4744  )
4748 {
4749  IntLixTaskDumpTree(NULL, 0);
4751 }
4752 
4753 
4754 void
4756  void
4757  )
4761 {
4762  for_each_task(pTask)
4763  {
4764  if (!pTask->IsThread)
4765  {
4766  LOG("Process %s (%s), PID: %d, TS 0x%016llx, Mm 0x%016llx, Parent 0x%016llx, RealParent 0x%016llx, "
4767  "Protected: %d/%llx\n", !pTask->Exec ? "(no exec)" : pTask->Path ? pTask->Path->Path : "(no path)",
4768  pTask->Comm, pTask->Pid, pTask->Gva, pTask->MmGva,
4769  pTask->Parent, pTask->RealParent,
4770  pTask->Protected, pTask->Protection.Mask);
4771 
4772  if (pTask->Protection.Mask & PROC_OPT_PROT_EXPLOIT)
4773  {
4774  list_for_each(pTask->Vmas, LIX_VMA, pVma)
4775  {
4776  LOG(" [%016llx -> %016llx] : %08llx [file @%016llx] @ %016llx Hooked=%d (%c%c%c)\n",
4777  pVma->Start, pVma->End, pVma->Flags, pVma->File, pVma->Gva, pVma->Hook ? 1 : 0,
4778  (pVma->Flags & VM_EXEC) ? 'X' : '-',
4779  (pVma->Flags & VM_WRITE) ? 'W' : '-',
4780  (pVma->Flags & VM_READ) ? 'R' : '-');
4781  }
4782 
4783  IntLixMmListVmas(pTask->MmGva, pTask);
4784  }
4785 
4786  // Parse the list again for this process threads
4787  for_each_task(pThr)
4788  {
4789  if (!pThr->IsThread)
4790  {
4791  continue;
4792  }
4793 
4794  if (pThr->Tgid != pTask->Tgid)
4795  {
4796  continue;
4797  }
4798 
4799  LOG("----> Thread %s %d/%d @ 0x%016llx, Parent 0x%016llx, RealParent 0x%016llx, Protected: %d/%llx\n",
4800  pThr->Comm, pThr->Pid, pThr->Tgid, pThr->Gva,
4801  pThr->Parent, pThr->RealParent,
4802  pThr->Protected, pThr->Protection.Mask);
4803  }
4804  }
4805  }
4806 
4807  for_each_task(pKThread)
4808  {
4809  if (pKThread->IsThread && pKThread->KernelMode)
4810  {
4811  LOG("Kernel Thread %s, P(TG)ID %d/%d, CR3 0x%016llx, TS 0x%016llx, Parent 0x%016llx, "
4812  "RealParent 0x%016llx, ActualParent 0x%016llx, Protected: %d/%llx\n",
4813  pKThread->Comm, pKThread->Pid, pKThread->Tgid,
4814  pKThread->Cr3,
4815  pKThread->Gva,
4816  pKThread->Parent,
4817  pKThread->RealParent,
4818  pKThread->ActualParent,
4819  pKThread->Protected,
4820  pKThread->Protection.Mask);
4821  }
4822  }
4823 }
4824 
4825 
4826 void
4828  void
4829  )
4833 {
4834  DWORD i = 0;
4835 
4837  {
4838  LOG("# %04d %s, %llx, '%s'\n",
4839  i,
4840  pProt->CommPattern,
4841  pProt->Protection.Original,
4842  pProt->NamePattern ? pProt->NamePattern : "(none)");
4843 
4844  i++;
4845  }
4846 }
4847 
4848 
4849 INTSTATUS
4852  )
4861 {
4862  if (NULL == Callback)
4863  {
4865  }
4866 
4867  for_each_task(pTask)
4868  {
4869  INTSTATUS status = Callback(pTask);
4870  if (!INT_SUCCESS(status))
4871  {
4872  WARNING("[WARNING] Callback failed: 0x%08x\n", status);
4873  }
4874  }
4875 
4876  return INT_STATUS_SUCCESS;
4877 }
4878 
4879 
4880 BOOLEAN
4882  void
4883  )
4900 {
4901  LIX_TASK_OBJECT *pTerminateTask = NULL;
4902  WORD userModeTasks = 0;
4903  int systemState = IntLixGuestGetSystemState();
4904 
4905  if (systemState > (int)(LIX_FIELD(Ungrouped, Running)))
4906  {
4907  LOG("[LIX-GUEST] Found system state '%d'\n", systemState);
4908  return TRUE;
4909  }
4910 
4912  {
4913  return TRUE;
4914  }
4915 
4916  if (IsListEmpty(&gLixTasks))
4917  {
4918  return TRUE;
4919  }
4920 
4921  for_each_task(pTask)
4922  {
4923  if (pTask->KernelMode)
4924  {
4925  continue;
4926  }
4927 
4928  userModeTasks++;
4929 
4930  if (pTerminateTask != NULL)
4931  {
4932  continue;
4933  }
4934 
4935  for (DWORD index = 0; index < ARRAYSIZE(gLixTerminatingTasks); index++)
4936  {
4937  if (pTask->Path &&
4938  0 == strncmp(pTask->Path->Name, gLixTerminatingTasks[index], strlen(gLixTerminatingTasks[index]) + 1))
4939  {
4940  pTerminateTask = pTask;
4941  }
4942 
4943  if (0 == strncmp(pTask->Comm, gLixTerminatingTasks[index], sizeof(pTask->Comm)))
4944  {
4945  pTerminateTask = pTask;
4946  }
4947  }
4948  }
4949 
4950  if (pTerminateTask == NULL)
4951  {
4952  return FALSE;
4953  }
4954 
4955  LOG("[LIX-GUEST] Found shutdown/reboot task '%s'\n", pTerminateTask->Comm);
4956 
4957  if (userModeTasks > 6)
4958  {
4959  return FALSE;
4960  }
4961 
4962  return TRUE;
4963 }
4964 
4965 
4966 INTSTATUS
4968  _In_ void *Detour
4969  )
4981 {
4982  INTSTATUS status = INT_STATUS_SUCCESS;
4985  EXCEPTION_UM_ORIGINATOR originator = { 0 };
4986  EXCEPTION_VICTIM_ZONE victim = { 0 };
4989  QWORD dstAddress = gVcpu->Regs.R9;
4990  QWORD address = gVcpu->Regs.R10;
4991  DWORD length = (DWORD)(gVcpu->Regs.R11);
4992 
4993  UNREFERENCED_PARAMETER(Detour);
4994 
4996 
4997  if (!(pVictim->Protection.Mask & PROC_OPT_PROT_WRITE_MEM))
4998  {
4999  action = introGuestAllowed;
5000  goto _exit;
5001  }
5002 
5003  status = IntExceptUserGetOriginator(pOriginator, FALSE, address, NULL, &originator);
5004  if (!INT_SUCCESS(status))
5005  {
5006  reason = introReasonInternalError;
5007  action = introGuestNotAllowed;
5008 
5009  ERROR("[ERROR] IntExceptUserGetOriginator failed with status: 0x%08x\n", status);
5010  goto _exit;
5011  }
5012 
5013  status = IntExceptGetVictimProcess(pVictim, dstAddress, length, ZONE_WRITE, &victim);
5014  if (!INT_SUCCESS(status))
5015  {
5016  reason = introReasonInternalError;
5017  action = introGuestNotAllowed;
5018 
5019  ERROR("[ERROR] IntExceptGetVictimProcess failed with status: 0x%08x\n", status);
5020  goto _exit;
5021  }
5022 
5023  IntExcept(&victim, &originator, exceptionTypeUm, &action, &reason, introEventInjectionViolation);
5024 
5025 _exit:
5027 
5028  if (IntPolicyProcTakeAction(PROC_OPT_PROT_WRITE_MEM, pVictim, &action, &reason))
5029  {
5030  IntLixTaskSendInjectionEvent(pOriginator, pVictim, action, reason);
5031  }
5032 
5034 
5035  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, action == introGuestAllowed ? 0 : -EACCES);
5036  if (!INT_SUCCESS(status))
5037  {
5038  ERROR("[ERROR] IntDetoursGstSetReturnValue failed: 0x%08x\n", status);
5039  }
5040 
5041  return INT_STATUS_SUCCESS;
5042 }
BOOLEAN StolenTokens
TRUE if credentials for this process have been altered.
Definition: lixprocess.h:117
#define _In_opt_
Definition: intro_sal.h:16
char * ProcName
The process name that is always valid. It&#39;s set depending which info is available in order: Path...
Definition: lixprocess.h:54
INTSTATUS IntLixTaskGetTrapFrame(const LIX_TASK_OBJECT *Task, LIX_TRAP_FRAME *TrapFrame)
Retrieves the trap frame for a Linux task.
Definition: lixprocess.c:1096
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
struct _LIX_TASK_OBJECT::@135 Dpi
DPI related information.
LIX_OPAQUE_FIELDS OsSpecificFields
OS-dependent and specific information.
Definition: lixguest.h:576
#define PTRACE_SETREGS
Definition: lixddefs.h:106
#define __unlikely(x)
Definition: common.h:47
static INTSTATUS IntLixTaskFetchMm(QWORD MmStruct, LIX_TASK_OBJECT *Task, LIX_TASK_OBJECT *Parent)
Fetches the CR3 of a Linux task.
Definition: lixprocess.c:690
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
INTSTATUS IntLixTaskGetUserStack(LIX_TASK_OBJECT *Task, QWORD *StackPointer, QWORD *StackBase, QWORD *StackLimit)
Finds the user mode stack limits for a Linux process.
Definition: lixprocess.c:2024
QWORD Cr3
The CR3 for this process.
Definition: lixprocess.h:70
void IntLixCredsVerify(LIX_TASK_OBJECT *Task)
Verifies whether the credentials of a process has been altered or not.
Definition: lixcred.c:534
INTSTATUS IntLixAccessRemoteVmHandler(void *Detour)
Detour handler for __access_remote_vm.
Definition: lixprocess.c:4967
Sent for unauthorized process creation alerts. See EVENT_PROCESS_CREATION_VIOLATION.
Definition: intro_types.h:115
BOOLEAN Valid
Set to True if the information in the structure is valid, False otherwise.
Definition: intro_types.h:818
void IntLixTaskUninit(void)
Uninitializes the Linux process subsystem.
Definition: lixprocess.c:4528
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
static BYTE * gTaskPtr2
Definition: lixprocess.c:570
static LIST_HEAD gLixTaskPaths
The list with all cached paths.
Definition: lixprocess.c:112
#define SIGQUIT
Definition: lixddefs.h:197
void IntLixTaskDumpAsTree(void)
Dump the process tree.
Definition: lixprocess.c:4742
struct _EVENT_MEMCOPY_VIOLATION::@290 Victim
INTSTATUS IntLixMmFindVmaRange(QWORD Gva, LIX_TASK_OBJECT *Task, QWORD *VmaStart, QWORD *VmaEnd)
Finds the VMA limits that contain an address.
Definition: lixmm.c:640
An internal error occurred (no memory, pages not present, etc.).
Definition: intro_types.h:195
INTRO_PROCESS Victim
The process that was compromised.
Definition: intro_types.h:1629
BOOLEAN IntPolicyCoreForceBetaIfNeeded(QWORD Flag, INTRO_ACTION *Action)
Checks if a forced action should be taken even if the log-only mode is active.
Definition: introcore.c:2803
uint8_t BYTE
Definition: intro_types.h:47
#define PTRACE_POKETEXT
Definition: lixddefs.h:98
static INTSTATUS IntLixTaskHandleInjection(QWORD Victim, BOOLEAN Pid, QWORD InjectionFlag, BOOLEAN *Block)
Handles the injection into a protected process.
Definition: lixprocess.c:3219
INTSTATUS IntHookObjectDestroy(HOOK_OBJECT_DESCRIPTOR **Object, DWORD Flags)
Destroy an entire hook object. All regions belonging to this object will be removed.
Definition: hook_object.c:357
Describe the introcore protection options.
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
BOOLEAN Created
True if the process was created, False if it was terminated.
Definition: intro_types.h:1750
DWORD Index
The VCPU number.
Definition: guests.h:172
#define _In_
Definition: intro_sal.h:21
#define INTRO_OPT_PROT_DPI_STACK_PIVOT
Enable process creation protection for pivoted stack.
Definition: intro_types.h:474
void IntLixTaskDump(void)
Dumps the process list.
Definition: lixprocess.c:4755
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1088
char * basename_s(char *path, size_t len)
Returns a pointer inside a path string pointing to the beginning of the file base name...
Definition: lixprocess.c:122
static LIST_HEAD gLixProtectedTasks
The list with all tasks that are currently protected.
Definition: lixprocess.c:79
#define CLEAN_PHYS_ADDRESS64(x)
Definition: pgtable.h:119
static QWORD IntLixUserToKernelPgd(QWORD Pgd)
Translates the value of a user page global directory to it&#39;s corresponding kernel value when KPTI is ...
Definition: lixprocess.c:885
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:207
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
BOOLEAN IntPolicyCoreTakeAction(QWORD Flag, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason)
Returns the action that should be taken for a core introspection option.
Definition: introcore.c:2693
Event structure for process creation/termination.
Definition: intro_types.h:1747
INTRO_PROCESS Process
The process that attempted the access.
Definition: intro_types.h:1318
Measures user mode exceptions checks.
Definition: stats.h:50
#define BIT(x)
Definition: common.h:51
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
INTSTATUS IntReadString(QWORD StrGva, DWORD MinimumLength, BOOLEAN AnsiOnly, char **String, DWORD *StringLength)
Reads a string from the guest kernel memory.
Definition: introcore.c:2880
#define for_each_path(_var_name)
list_for_each wrapper used to iterate cached paths.
Definition: lixprocess.c:117
uint16_t WORD
Definition: intro_types.h:48
Event structure for process creation violation events.
Definition: intro_types.h:1610
#define STATS_EXIT(id)
Definition: stats.h:148
CAMI_STRING_ENCODING
Describes the encoding of a string received from the CAMI file.
Definition: update_guests.h:52
#define _Out_writes_bytes_(expr)
Definition: intro_sal.h:38
INTSTATUS IntLixTaskSendExceptionEvent(DWORD Signal, LIX_TASK_OBJECT *Task)
Sends an event that contains the information about signal received by the provided task...
Definition: lixcrash.c:240
INTSTATUS IntLixVdsoDynamicProtect(void)
This function activates the protection for the vDSO image.
Definition: lixvdso.c:726
#define IC_TAG_POBJ
Process Object List Entry.
Definition: memtags.h:14
static BOOLEAN IntLixTaskMustLog(const LIX_TASK_OBJECT *Task, BOOLEAN Protected)
Controls whether information about a task must be logged or not.
Definition: lixprocess.c:366
User-mode exception.
Definition: exceptions.h:61
User-mode non executable zone.
Definition: intro_types.h:244
Process creation violation.
Definition: intro_types.h:259
#define SIGTRAP
Definition: lixddefs.h:199
#define SIGFPE
Definition: lixddefs.h:203
static void IntLixTaskPathFree(LIX_TASK_PATH **Path)
Release a LIX_TASK_PATH object.
Definition: lixprocess.c:328
AGENT_EVENT_TYPE Event
The type of the agent.
Definition: intro_types.h:2184
LIX_TASK_OBJECT * IntLixTaskFindByGva(QWORD TaskStruct)
Finds Linux process with the provided "task_struct" guest virtual address.
Definition: lixprocess.c:1023
QWORD Gva
The guest virtual address of the task_struct.
Definition: lixprocess.h:42
static void IntLixTaskSendAgentEvent(LIX_TASK_OBJECT *Task, DWORD ExitCode, BOOLEAN Created)
Sends an agent event.
Definition: lixprocess.c:1983
#define IN_RANGE_INCLUSIVE(x, start, end)
Definition: introdefs.h:171
BOOLEAN ShutDown
True if the system process protection is in beta (log-only) mode.
Definition: guests.h:309
DWORD UmThreads
If the user mode threads events should be logged.
Definition: lixprocess.c:28
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
#define SIGBUS
Definition: lixddefs.h:202
static BOOLEAN IsListEmpty(const LIST_ENTRY *ListHead)
Definition: introlists.h:78
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
LIX_AGENT_TAG IntLixAgentDecProcRef(const char *Name, BOOLEAN *Removed)
Checks if a process is an agent or not, and decrements the ref count of that name.
Definition: lixagent.c:1901
#define CR3_LONG_MODE_MASK
Definition: pgtable.h:112
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1087
INTSTATUS IntExceptGetVictimProcessCreation(void *Process, INTRO_OBJECT_TYPE ObjectType, EXCEPTION_VICTIM_ZONE *Victim)
This function is used to get the information about the victim for process-creation violation...
static LIX_TASK_PATH * IntLixTaskPathGetByFile(QWORD FileGva)
Get a LIX_TASK_PATH object based on the guest virtual address of a "file" structure.
Definition: lixprocess.c:285
#define PAGE_OFFSET
Definition: pgtable.h:32
The action was not allowed because there was no reason to allow it.
Definition: intro_types.h:183
#define ARRAYSIZE(A)
Definition: introdefs.h:101
int32_t INT32
Definition: intro_types.h:44
INTSTATUS IntLixTaskAddProtected(const char *ProcessName, QWORD ProtectionMask, QWORD Context)
Adds a protected process name pattern.
Definition: lixprocess.c:4297
Event structure for agent injection and termination.
Definition: intro_types.h:2182
void * InitProcessObj
The LIX_TASK_OBJECT of the &#39;init&#39; process.
Definition: lixguest.h:529
The agent process finished execution.
Definition: intro_types.h:1939
void sanitize_path(char *path, size_t len, size_t *new_len)
Sanitizes an Unix path by removing trailing path delimiters.
Definition: lixprocess.c:146
void IntLixTaskUpdateProtection(void)
Adjusts protection for all active Linux processes.
Definition: lixprocess.c:4458
static INTSTATUS IntLixTaskDeactivateExploitProtection(LIX_TASK_OBJECT *Task)
Deactivates exploit protection for a Linux task.
Definition: lixprocess.c:1208
QWORD Parent
Depends if this is a thread or a process.
Definition: lixprocess.h:57
#define for_each_protected_task(_var_name)
list_for_each wrapper used to iterate protected tasks.
Definition: lixprocess.c:100
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
INTSTATUS IntLixTaskIsUserStackPivoted(LIX_TASK_OBJECT *Task, QWORD Ptr, BOOLEAN *IsPivoted)
Verifies whether the stack of a Linux process is pivoted or not.
Definition: lixprocess.c:2775
Process creation violation DPI.
Definition: intro_types.h:262
#define ERROR(fmt,...)
Definition: glue.h:62
DWORD Tgid
The task Thread-Group-ID.
Definition: lixprocess.h:73
Describes a user-mode originator.
Definition: exceptions.h:933
#define INTRO_OPT_PROT_DPI
Aggregates all the deep process inspection flags.
Definition: intro_types.h:502
static BYTE * gTaskPtr1
Definition: lixprocess.c:569
static LIST_HEAD gLixTasksToProtect
The list with all tasks that should be protected.
Definition: lixprocess.c:84
static INTSTATUS IntLixTaskCreate(LIX_TASK_OBJECT *Parent, LIX_TASK_OBJECT *RealParent, QWORD TaskStruct, BOOLEAN StaticDetected, LIX_TASK_OBJECT **Task)
Creates a Linux process object.
Definition: lixprocess.c:2083
LIX_TASK_PATH * Path
The path of the file executed.
Definition: lixprocess.h:50
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
int INTSTATUS
The status data type.
Definition: introstatus.h:24
#define PROC_OPT_PROT_PTRACE
Blocks thread hijacking attempts inside the target process (Linux only).
Definition: intro_types.h:345
LIX_TASK_OBJECT * IntLixTaskProtFindByMm(QWORD MmGva)
Finds the protected Linux process having the provided mm guest virtual address.
Definition: lixprocess.c:972
DWORD ExitStatus
The exit code of the process.
Definition: intro_types.h:1764
DWORD OSVersion
Os version.
Definition: guests.h:277
int IntLixGuestGetSystemState(void)
Get the system state of the Linux guest.
Definition: lixguest.c:2254
QWORD CreationTime
The creation timestamp for this process.
Definition: lixprocess.h:75
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
static LIX_TASK_PATH * IntLixTaskPathGetByPath(QWORD PathGva, QWORD DentryGva)
Get a LIX_TASK_PATH object based on the guest virtual address of a path string.
Definition: lixprocess.c:310
DWORD IsPreviousAgent
TRUE if this process is an agent remaining from a previous session.
Definition: lixprocess.h:98
QWORD Base
The user mode stack base.
Definition: lixprocess.h:109
#define PAGE_COUNT(addr, bytes)
Definition: pgtable.h:189
static const char * gLixTerminatingTasks[]
Linux processes signaling that the guest OS is shutting down.
Definition: lixprocess.c:62
DWORD RefCount
The number of references for this cache entry.
Definition: lixprocess.h:34
#define ONE_KILOBYTE
Definition: introdefs.h:89
BOOLEAN IntMatchPatternUtf8(const CHAR *Pattern, const CHAR *String, DWORD Flags)
Matches a pattern using glob match.
Definition: introcore.c:2454
PCHAR NamePattern
Full application file name.
Definition: lixguest.h:24
Describes a path cache entry.
Definition: lixprocess.h:22
INTRO_PROCESS CurrentProcess
The agent process.
Definition: intro_types.h:2192
void IntLixProcUpdateProtectedProcess(const void *Name, const CAMI_STRING_ENCODING Encoding, const CAMI_PROT_OPTIONS *Options)
Updates the protection flags for Linux tasks that should be protected based on options received via C...
Definition: lixprocess.c:1171
void IntLixMmDestroyVmas(LIX_TASK_OBJECT *Task)
Remove protection for the VMAs belonging to a process.
Definition: lixmm.c:1016
Sent for code/data injection alerts. See EVENT_MEMCOPY_VIOLATION.
Definition: intro_types.h:96
EVENT_PROCESS_CREATION_VIOLATION ProcessCreation
Definition: alerts.h:31
static INTSTATUS IntLixTaskAdjustProtections(const LIX_PROTECTED_PROCESS *ProtProc, BOOLEAN Remove)
Adjusts the protection flags for processes associated with the LIX_PROTECTED_PROCESS object...
Definition: lixprocess.c:4191
#define ALERT_FLAG_LINUX
Definition: intro_types.h:638
#define LIX_PTI_PGTABLE_SWITCH_BIT
The bit marking whether the kernel memory is mapped in a PGD.
Definition: lixddefs.h:290
struct _LIX_PROTECTED_PROCESS::@120 Protection
What protection policies should be applied.
void IntLixCredRemove(LIX_CREDS **Creds)
Removes the integrity protection for the credentials set that belong to a process.
Definition: lixcred.c:441
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
Encapsulates a protected Linux process.
Definition: lixguest.h:17
DWORD ErrorCode
The error code of the event. Success is 0.
Definition: intro_types.h:2186
#define MIN(a, b)
Definition: introdefs.h:146
#define IC_TAG_PATH
Object path.
Definition: memtags.h:55
INTRO_PC_VIOLATION_TYPE PcType
Valid if the current violation is DPI Process Creation Violation.
Definition: exceptions.h:980
QWORD RealParent
The process which called fork()
Definition: lixprocess.h:56
INTSTATUS(* PFUNC_LixTaskIterateTasks)(LIX_TASK_OBJECT *Task)
Definition: lixprocess.h:183
DWORD CommHash
The CRC32 checksum of the Comm field.
Definition: lixprocess.h:65
#define INTRO_OPT_PROT_KM_VDSO
Enable vDSO image protection (Linux only).
Definition: intro_types.h:484
#define INTRO_MATCH_TRUNCATED
If set, matching functions like IntMatchPatternUtf8 will match up until the first wild char encounter...
Definition: introcore.h:13
#define LIX_CREATE_VERSION(K, Patch, Sublevel)
Definition: lixguest.h:593
#define LOG(fmt,...)
Definition: glue.h:61
char * CmdLine
The process command line.
Definition: lixprocess.h:48
#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
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
#define IS_ERR(x)
Definition: lixddefs.h:293
QWORD IntLixGetKernelCr3(QWORD Cr3)
Transforms an user CR3 into a kernel CR3 on systems with KPTI enabled and active. ...
Definition: lixprocess.c:917
BOOLEAN KptiActive
True if KPTI is enabled on this guest, False if it is not.
Definition: guests.h:287
#define PAGE_FRAME_NUMBER(addr)
Definition: pgtable.h:181
QWORD Feedback
Flags that will be forced to feedback only mode.
Definition: lixguest.h:35
QWORD ExeFileDentry
The guest virtual address of the executable file&#39;s "dentry" structure.
Definition: lixprocess.h:60
Execution through API call.
Definition: intro_types.h:1040
Access Token Manipulation.
Definition: intro_types.h:1042
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1084
void IntUDRemoveAllEntriesForCr3(const QWORD Cr3)
Remove all pending UD entries for a given virtual address space.
Definition: udlist.c:116
#define EACCES
Definition: lixddefs.h:160
static INTSTATUS _IntLixTaskStartMap(QWORD TaskGva)
Map the task_struct in order to perform further reads from it without any overhead.
Definition: lixprocess.c:574
static void IntLixTaskSendBlockedEvent(LIX_TASK_OBJECT *OldTask, LIX_TASK_OBJECT *NewTask, INTRO_ACTION Action, INTRO_ACTION_REASON Reason, DWORD PcType)
Sends a blocked process creation event.
Definition: lixprocess.c:2589
QWORD Limit
The user mode stack limit.
Definition: lixprocess.h:110
static void IntLixTaskRemoveEntry(LIX_TASK_OBJECT *Task)
Removes a Linux process from the process list.
Definition: lixprocess.c:2408
static void IntLixTaskDumpTree(LIX_TASK_OBJECT *Task, DWORD Level)
Dumps the user mode tasks tree.
Definition: lixprocess.c:4559
static INTSTATUS IntLixTaskFetchCmdLine(LIX_TASK_OBJECT *Process, QWORD BinprmGva)
Fetches the command line for a Linux process on the exec() system call.
Definition: lixprocess.c:1534
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
DWORD Execs
If exec events should be logged.
Definition: lixprocess.c:30
LIX_TASK_OBJECT * IntLixTaskFindByPid(DWORD Pid)
Finds the Linux process having the provided PID.
Definition: lixprocess.c:1049
enum _INTRO_OBJECT_TYPE INTRO_OBJECT_TYPE
The type of the object protected by an EPT hook.
static LIST_HEAD gLixTasks
The list with all tasks inside the guest OS.
Definition: lixprocess.c:74
#define _Inout_
Definition: intro_sal.h:20
#define LIX_MM_PROT_MASK
The bit used to mark a memory space as protected.
Definition: lixprocess.c:19
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1612
QWORD Current
The currently used protection flags.
Definition: lixguest.h:32
struct _LIX_TASK_LOG LIX_TASK_LOG
This structure contains control bits for linux process logging.
INTSTATUS IntKernVirtMemFetchDword(QWORD GuestVirtualAddress, DWORD *Data)
Reads 4 bytes from the guest kernel memory.
Definition: introcore.c:829
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1314
INTSTATUS IntCamiUpdateProcessProtectionInfo(void *ProtectedProcess)
Update a process&#39; protection flags using the ones from CAMI.
#define _Out_opt_
Definition: intro_sal.h:30
EVENT_MEMCOPY_VIOLATION Injection
Definition: alerts.h:21
QWORD DataStart
The guest virtual address where the data starts.
Definition: lixguest.h:497
#define INITIAL_CRC_VALUE
Definition: introdefs.h:221
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
INTSTATUS IntLixGetInitTask(QWORD *InitTask)
Finds the guest virtual address of the "init_task".
Definition: lixprocess.c:401
#define IS_KERNEL_POINTER_LIX(p)
Definition: lixguest.h:11
static INTSTATUS IntLixTaskCreateInitTask(QWORD TaskGva, LIX_TASK_OBJECT **Task)
Creates the init task object.
Definition: lixprocess.c:3856
DWORD ProtForks
If forks performed by protected processes should be logged.
Definition: lixprocess.c:33
INTSTATUS IntKernVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD *Data)
Reads 8 bytes from the guest kernel memory.
Definition: introcore.c:811
#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
EVENT_PROCESS_EVENT Process
Definition: alerts.h:25
static INTSTATUS _IntLixTaskRead(DWORD Offset, DWORD Size, void *Buffer)
Perform a read from the previously mapped "task_struct" structure.
Definition: lixprocess.c:605
#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
static void IntLixTaskDeactivateProtection(LIX_TASK_OBJECT *Task)
Deactivates protection for a Linux process.
Definition: lixprocess.c:1496
#define INTRO_OPT_EVENT_PROCESSES
Enable process creation and termination events (generates introEventProcessEvent events).
Definition: intro_types.h:427
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
The parent of a process has a stolen access token when it created the child.
Definition: intro_types.h:1528
QWORD Original
The original protection flags as received from integrator.
Definition: lixguest.h:31
#define memzero(a, s)
Definition: introcrt.h:35
#define INTRO_OPT_PROT_UM_MISC_PROCS
Definition: intro_types.h:419
static void IntLixTaskGetPath(QWORD FileGva, QWORD DPathGva, LIX_TASK_OBJECT *Task)
Read and set the path for a Linux process.
Definition: lixprocess.c:1697
static void IntLixTaskSendTaskEvent(LIX_TASK_OBJECT *Task, DWORD ExitCode, BOOLEAN Created, BOOLEAN Crashed, BOOLEAN StaticDetected)
Sends a process event.
Definition: lixprocess.c:1914
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:232
INTSTATUS IntLixTaskHandlePtrace(void *Detour)
Handles the ptrace() system call.
Definition: lixprocess.c:3386
static LIX_TASK_PATH * IntLixTaskPathGetByDentry(QWORD FileGva, QWORD PathGva, QWORD DentryGva)
Get the LIX_TASK_PATH object associated with a given path.
Definition: lixprocess.c:189
static void IntLixTaskSendInjectionEvent(LIX_TASK_OBJECT *Source, LIX_TASK_OBJECT *Victim, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Sends an injection event.
Definition: lixprocess.c:3167
static INTSTATUS IntLixTaskIterateThreadNode(QWORD TaskStructGva, PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the threads of a Linux process based on the thread node..
Definition: lixprocess.c:3476
INTRO_PROCESS CurrentProcess
The currently active process.
Definition: intro_types.h:1767
DWORD Exec
TRUE if the process did exec at least once.
Definition: lixprocess.h:95
static void IntLixValidateExecStack(LIX_TASK_OBJECT *ParentTask, LIX_TASK_OBJECT *CurrentTask)
Validates the user mode stack of a process upon an exec() system call.
Definition: lixprocess.c:2890
Informational event sent when the remediation tool is injected or terminated. See EVENT_AGENT_EVENT...
Definition: intro_types.h:104
INTRO_PROCESS Child
The process that is being created or terminated.
Definition: intro_types.h:1770
BOOLEAN Valid
TRUE if the values inside this structure are valid.
Definition: lixprocess.h:111
DWORD IsThread
TRUE if it&#39;s a thread, not a process.
Definition: lixprocess.h:96
INTSTATUS IntTranslateVirtualAddress(QWORD Gva, QWORD Cr3, QWORD *PhysicalAddress)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1999
INTSTATUS IntLixTaskHandleFork(void *Detour)
Handles the fork() system call performed by a linux process.
Definition: lixprocess.c:3142
#define NSEC_PER_SEC
Definition: introdefs.h:93
#define SIGABRT
Definition: lixddefs.h:200
#define TRUE
Definition: intro_types.h:30
INTSTATUS(* PFUNC_IterateListCallback)(QWORD Node, QWORD Aux)
Definition: introtypes.h:71
#define IC_TAG_PCMD
Process command line.
Definition: memtags.h:78
#define PROC_OPT_KILL_ON_EXPLOIT
Definition: intro_types.h:361
The parent of a process had a pivoted stack when it created the child.
Definition: intro_types.h:1525
#define LIX_FIELD(Structure, Field)
Macro used to access fields inside the LIX_OPAQUE_FIELDS structure.
Definition: lixguest.h:426
#define TRACE(fmt,...)
Definition: glue.h:58
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
INTSTATUS IntGsRead(DWORD CpuNumber, QWORD *GsValue)
Reads the IA32_GS_BASE guest MSR.
Definition: introcpu.c:289
#define INT_STATUS_INVALID_DATA_STATE
Definition: introstatus.h:183
#define VM_WRITE
Definition: lixddefs.h:42
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
QWORD Context
The context supplied in the protection policy.
Definition: lixguest.h:26
#define VM_EXEC
Definition: lixddefs.h:43
Memory access violations that cross a process boundary.
Definition: intro_types.h:1312
DWORD AgentTag
Unique agent tag. See INTRO_DEP_AG_TAGS.
Definition: intro_types.h:2185
static LIX_TASK_PATH * IntLixTaskPathGetRef(LIX_TASK_PATH *Path)
Increases the reference counter for a LIX_TASK_PATH object.
Definition: lixprocess.c:168
static void InsertAfterList(LIST_ENTRY *Pivot, LIST_ENTRY *Item)
Definition: introlists.h:169
DWORD Forks
If forks should be logged.
Definition: lixprocess.c:29
Informational event sent when a process is created or terminated by the guest. See EVENT_PROCESS_EVEN...
Definition: intro_types.h:102
static void IntLixValidateProcessCreationRights(LIX_TASK_OBJECT *ChildTask, LIX_TASK_OBJECT *ParentTask, INTRO_OBJECT_TYPE ObjectType, INTRO_ACTION *Action, INTRO_ACTION_REASON *Reason)
Validates process creation rights (both PC and DPI).
Definition: lixprocess.c:2671
BOOLEAN Protected
True if the process is protected.
Definition: intro_types.h:1752
String will be encoded in utf-8.
Definition: update_guests.h:54
DWORD ProtExecs
If an exec performed by a protected process should be logged.
Definition: lixprocess.c:34
QWORD Context
Context from integrator.
Definition: lixprocess.h:90
INTSTATUS IntLixTaskGetAgentsAsCli(char *CommandLine, DWORD Length)
Returns a string with the command lines of all active agents.
Definition: lixprocess.c:4488
INTSTATUS IntLixTaskHandleExec(void *Detour)
Handles the exec() system call of a linux process.
Definition: lixprocess.c:2927
INTSTATUS IntLixTaskAdd(QWORD TaskGva, QWORD StaticDetected)
Creates and adds a Linux process in the internal list.
Definition: lixprocess.c:3959
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
DWORD IntLixTaskGetExecCount(void)
Returns the number of processes that have performed an exec.
Definition: lixprocess.c:1075
size_t strlcpy(char *dst, const char *src, size_t dest_size)
Definition: introcrt.c:1093
CHAR CommPattern[16]
Process name pattern (supports glob patterns). Will be used if there is no path.
Definition: lixguest.h:22
LIST_ENTRY ExploitProtProcLink
Linkage in the protected processes list.
Definition: lixprocess.h:67
INTSTATUS IntLixTaskGetCurrentTaskStruct(DWORD CpuNumber, QWORD *TaskStruct)
Reads the guest virtual address of the task currently running on a CPU.
Definition: lixprocess.c:793
#define WARNING(fmt,...)
Definition: glue.h:60
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.
INTSTATUS IntLixFileGetDentry(QWORD File, QWORD *Dentry)
Reads the value of the dentry field of the &#39;struct file&#39;.
Definition: lixfiles.c:195
#define ALERT_FLAG_NOT_RING0
If set, the alert was triggered in ring 1, 2 or 3.
Definition: intro_types.h:636
static void InitializeListHead(LIST_ENTRY *ListHead)
Definition: introlists.h:69
static DWORD IntLixTaskGetDpiMitreId(DWORD Flags)
Returns the MITRE ID for the process creation violation flag.
Definition: lixprocess.c:2562
INTSTATUS IntVirtMemSafeWrite(QWORD Cr3, QWORD VirtualAddress, DWORD Size, void *Buffer, DWORD Ring)
Safely modify guest memory.
Definition: kernvm.c:498
#define PAGE_SIZE
Definition: common.h:53
DWORD KernelMode
TRUE if this process/thread is inside kernel mode.
Definition: lixprocess.h:97
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
#define PF_EXITING
Definition: lixddefs.h:121
char * Name
The path base name.
Definition: lixprocess.h:27
void * InstructionCache
The currently used instructions cache.
Definition: guests.h:400
#define SIGKILL
Definition: lixddefs.h:204
#define INT_STATUS_DATA_BUFFER_TOO_SMALL
Definition: introstatus.h:194
#define __forceinline
Definition: introtypes.h:61
static void IntLixTaskMarkAgent(LIX_TASK_OBJECT *Task)
Marks a Linux process as being an Introcore agent.
Definition: lixprocess.c:2441
EVENT_AGENT_EVENT Agent
Definition: alerts.h:29
LIST_ENTRY Link
Entry inside the gLixProtectedTasks list.
Definition: lixguest.h:19
uint32_t DWORD
Definition: intro_types.h:49
#define PROC_OPT_PROT_CORE_HOOKS
Blocks hooks being set on core user-mode DLLs.
Definition: intro_types.h:333
size_t NameLength
The size of the base name.
Definition: lixprocess.h:32
#define strlen_s(s, n)
Definition: introcrt.h:34
#define PROC_OPT_PROT_PREVENT_CHILD_CREATION
Prevent the process from creating child processes (other than instances of itself).
Definition: intro_types.h:349
INTSTATUS IntLixTaskHandleDoExit(void *Detour)
Handles the exit() system call.
Definition: lixprocess.c:3444
static INTSTATUS IntLixTaskChangeProtectionFlags(LIX_TASK_OBJECT *Task, QWORD NewProtection, QWORD NewRootProtection, QWORD Context)
Adjust the protection of a Linux process based on a new set of rules.
Definition: lixprocess.c:4069
static INTSTATUS IntLixTaskActivateExploitProtection(LIX_TASK_OBJECT *Task)
Activates exploit protection for a Linux task.
Definition: lixprocess.c:1271
LIX_CREDS * Creds
The LIX_CREDS reference for the credentials of this process.
Definition: lixprocess.h:105
INTRO_PC_VIOLATION_TYPE PcType
The type of process creation violation.
Definition: intro_types.h:1652
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
static QWORD gTaskMapped
Definition: lixprocess.c:568
static QWORD IntLixKernelToUserPgd(QWORD Pgd)
Translates the value of a kernel page global directory to it&#39;s corresponding user value when KPTI is ...
Definition: lixprocess.c:901
#define LIX_PROCESSES_MAX_COUNT
The maximum number of processes allowed.
Definition: lixprocess.h:15
char * Interpreter
If this was a script executed through an interpretor.
Definition: lixprocess.h:46
QWORD DentryGva
The guest virtual address of the "dentry" structure associated with this path.
Definition: lixprocess.h:29
LIX_AGENT_TAG IntLixAgentIncProcRef(const char *Name)
Checks if a process is an agent or not, and increments the ref count of that name.
Definition: lixagent.c:1863
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
INTSTATUS IntLixTaskHandleVmRw(void *Detour)
Handles the process_vm_writev() system call.
Definition: lixprocess.c:3344
#define IC_TAG_NAME
Object name.
Definition: memtags.h:56
LIX_TASK_OBJECT * IntLixTaskFindByMm(QWORD MmGva)
Finds the Linux process having the provided mm guest virtual address.
Definition: lixprocess.c:997
#define MAX(a, b)
Definition: introdefs.h:151
__must_check INTSTATUS IntVirtMemMap(QWORD Gva, DWORD Length, QWORD Cr3, DWORD Flags, void **HostPtr)
Maps a guest virtual memory range inside Introcore virtual address space.
Definition: introcore.c:2134
This structure contains control bits for linux process logging.
Definition: lixprocess.c:24
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:370
size_t PathLength
The size of the path.
Definition: lixprocess.h:31
char Comm[LIX_COMM_SIZE]
The short name of the executable.
Definition: lixprocess.h:44
#define LIX_COMM_SIZE
The maximum size of the process comm.
Definition: lixprocess.h:13
static INTSTATUS IntLixTaskActivateProtection(LIX_TASK_OBJECT *Task, LIX_TASK_OBJECT *Parent)
Activates protection for a Linux process.
Definition: lixprocess.c:1378
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:48
LIX_TASK_OBJECT * IntLixTaskFindByCr3(QWORD Cr3)
Finds the Linux process having the provided Cr3.
Definition: lixprocess.c:940
QWORD MmGva
The guest virtual address of the "mm_struct".
Definition: lixprocess.h:69
void IntLixMmListVmas(QWORD Mm, LIX_TASK_OBJECT *Process)
Definition: lixmm.c:1671
LIST_ENTRY Link
The list node.
Definition: lixprocess.h:24
#define PF_KTHREAD
Definition: lixddefs.h:140
#define PTRACE_POKEDATA
Definition: lixddefs.h:99
LIST_ENTRY Link
Linkage in the global task list.
Definition: lixprocess.h:40
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...
DWORD ProtUmThreads
If events related to threads created by protected process should be logged.
Definition: lixprocess.c:32
static LIX_PROTECTED_PROCESS * IntLixTaskShouldProtect(const LIX_TASK_OBJECT *Task)
Checks whether a Linux task should be protected or not.
Definition: lixprocess.c:1140
INTRO_PROCESS Originator
The process that attempted the violation.
Definition: intro_types.h:1646
LIX_TASK_LOG gLixTaskLogLevel
The global structure controlling linux process logging.
Definition: lixprocess.c:44
DWORD Pid
The task PID.
Definition: lixprocess.h:72
BOOLEAN IntLixTaskGuestTerminating(void)
Check whether the guest OS is terminating or not.
Definition: lixprocess.c:4881
#define PF_FORKNOEXEC
Definition: lixddefs.h:125
#define PROC_OPT_BETA
Process is monitored, but in log-only mode so no actions will be blocked.
Definition: intro_types.h:363
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1083
Definition: lixmm.h:14
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
#define PROC_OPT_PROT_WRITE_MEM
Blocks foreign write inside the target process.
Definition: intro_types.h:337
INTSTATUS IntLixMmPopulateVmas(LIX_TASK_OBJECT *Task)
Populate the Introcore VMAs linked list by iterating the one inside the guest.
Definition: lixmm.c:1510
LIX_TASK_OBJECT * IntLixTaskGetCurrent(DWORD CpuNumber)
Finds the task that is currently running on the given CPU.
Definition: lixprocess.c:856
#define NLOG(fmt,...)
Definition: glue.h:43
static void IntLixTaskDestroy(LIX_TASK_OBJECT *Task, DWORD ExitCode)
Destroys a Linux process after protection for it is removed.
Definition: lixprocess.c:2471
DWORD Protected
TRUE if the process is protected.
Definition: lixprocess.h:99
struct _LINUX_GUEST::@123 Layout
#define LIST_HEAD_INIT(Name)
Definition: introlists.h:39
INTSTATUS IntKernVirtMemPatchQword(QWORD GuestVirtualAddress, QWORD Data)
Writes 8 bytes in the guest kernel memory.
Definition: introcore.c:932
QWORD Beta
Flags that were forced to beta mode.
Definition: lixguest.h:34
#define __likely(x)
Definition: common.h:46
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
DWORD KmThreads
If the kernel threads events should be logged.
Definition: lixprocess.c:26
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1086
#define PROC_OPT_REMEDIATE
Any event inside the process will trigger the injection of the remediation tool.
Definition: intro_types.h:356
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:57
BOOLEAN Crashed
True if the process crashed.
Definition: intro_types.h:1758
static INTSTATUS IntLixTaskIterateThreads(QWORD TaskStructGva, PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the threads of a Linux process.
Definition: lixprocess.c:3729
The action was blocked because there was no exception for it.
Definition: intro_types.h:189
DWORD Crc32String(const char *String, DWORD InitialCrc)
Computes the CRC for a NULL-terminated utf-8 string.
Definition: crc32.c:200
INTSTATUS IntLixCredAdd(QWORD CredsGva, LIX_CREDS **Creds)
Adds a cred structure in the integrity protected credentials list.
Definition: lixcred.c:365
#define SIGSEGV
Definition: lixddefs.h:206
INTSTATUS IntIcFlushVaSpace(PINS_CACHE Cache, QWORD Cr3)
Flush an entire virtual address space.
Definition: icache.c:797
char * Path
The full path string.
Definition: lixprocess.h:26
The agent process started execution.
Definition: intro_types.h:1938
static INTSTATUS IntLixTaskIterateThreadGroup(QWORD TaskStructGva, PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the threads of a Linux process based on the thread group.
Definition: lixprocess.c:3609
void IntLixTaskDumpProtected(void)
Dumps the list with processes that Introcore should protect.
Definition: lixprocess.c:4827
#define PTRACE_SETFPXREGS
Definition: lixddefs.h:110
#define CRITICAL(fmt,...)
Definition: glue.h:63
#define VM_READ
Definition: lixddefs.h:41
static INTSTATUS IntLixTaskCreateFromBinprm(LIX_TASK_OBJECT *OriginalTask, QWORD BinprmGva, QWORD PathGva, LIX_TASK_OBJECT *UpdatedTask)
Updates the contents of a previously forked process from it&#39;s new linux_binprm (used by the loader)...
Definition: lixprocess.c:1730
#define ONE_GIGABYTE
Definition: introdefs.h:91
BOOLEAN BugCheckInProgress
Definition: guests.h:329
struct _LIX_TASK_OBJECT::@134 UserStack
User stack information.
INTRO_PROCESS Parent
The parent of the process.
Definition: intro_types.h:1772
QWORD SourceVirtualAddress
The virtual address of the source buffer.
Definition: intro_types.h:1342
void IntAlertFillLixCurrentProcess(INTRO_PROCESS *EventProcess)
Saves the current Linux process inside an event.
Definition: alerts.c:1307
#define SIGILL
Definition: lixddefs.h:198
INTSTATUS IntLixTaskIterateTasks(PFUNC_LixTaskIterateTasks Callback)
Call the Callback parameter for each task saved internally.
Definition: lixprocess.c:4850
QWORD DestinationVirtualAddress
The virtual address of the destination buffer.
Definition: intro_types.h:1352
Exploitation for Client Execution.
Definition: intro_types.h:1046
BYTE Version
The version field of the version string.
Definition: lixguest.h:484
QWORD ActualParent
The parent, based on tgid. Only relevant for threads.
Definition: lixprocess.h:58
static void IntLixTaskSetProcName(LIX_TASK_OBJECT *Task)
Sets the name for a Linux process.
Definition: lixprocess.c:1672
INTSTATUS IntLixTaskIterateGuestTasks(PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the guest process list and calls the provided callback for each process and thread found...
Definition: lixprocess.c:3762
QWORD IntKsymFindByName(const char *Name, QWORD *SymEnd)
Searches the given Name in kallsyms and returns the Start & End offset.
Definition: lixksym.c:1361
static DWORD IntLixTaskGetDpiViolationFlags(LIX_TASK_OBJECT *Task)
Returns the DPI flags for a Linux process.
Definition: lixprocess.c:2643
static void _IntLixTaskFinishMap(void)
Unmaps a previously mapped "task_struct".
Definition: lixprocess.c:668
#define for_each_task(_var_name)
list_for_each wrapper used to iterate Linux tasks.
Definition: lixprocess.c:95
#define list_for_each(_head, _struct_type, _var)
Definition: introlists.h:41
#define for_each_task_to_protect(_var_name)
list_for_each wrapper used to iterate tasks that should be protected.
Definition: lixprocess.c:106
DWORD CurrentTaskOffset
The offset of the current task from GS.
Definition: lixguest.h:410
INTSTATUS IntLixTaskRemoveProtected(const char *ProcessName)
Removes a pattern of processes to be protected.
Definition: lixprocess.c:4402
void IntAlertFillLixProcess(const LIX_TASK_OBJECT *Task, INTRO_PROCESS *EventProcess)
Saves information about a Linux process inside an event.
Definition: alerts.c:1261
#define for_next_task(_task, _var_name)
list_for_next wrapper used to iterate tasks from a given node.
Definition: lixprocess.c:90
#define PAGE_MASK
Definition: pgtable.h:35
#define ZONE_WRITE
Used for write violation.
Definition: exceptions.h:698
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
#define PTRACE_SETFPREGS
Definition: lixddefs.h:108
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:267
LIST_HEAD Vmas
The list head for the VMAs from the memory space of this process.
Definition: lixprocess.h:80
INTSTATUS IntHookObjectCreate(DWORD ObjectType, QWORD Cr3, void **Object)
Create a new hook object.
Definition: hook_object.c:81
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
struct _LIX_TASK_OBJECT::@133 Protection
Protection specific flags.
LINUX_GUEST * gLixGuest
Global variable holding the state of a Linux guest.
Definition: lixguest.c:29
#define INT_STATUS_INVALID_DATA_SIZE
Definition: introstatus.h:142
INTSTATUS IntLixFileGetPath(QWORD FileStructGva, char **Path, DWORD *Length)
Gets the path that corresponds to the provided FileStructGva (guest virtual address of the &#39;struct fi...
Definition: lixfiles.c:352
DWORD StaticDetected
TRUE if the process was detected using a static scan (during static init).
Definition: lixprocess.h:94
static void IntLixTaskDumpKernelThreadTree(LIX_TASK_OBJECT *Thread, DWORD Level)
Dumps the kthreads tree.
Definition: lixprocess.c:4674
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
QWORD Mask
The protection flags enabled for this process.
Definition: lixprocess.h:84
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68
LIX_AGENT_TAG AgentTag
The agent tag, if this process is an agent.
Definition: lixprocess.h:103