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 #include "lixcmdline.h"
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) + 8 > 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) + 8 > 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) + 8 > 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) + 8 > 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", sizeof("swapper") - 1))
544  {
545  continue;
546  }
547 
548  comm[sizeof(comm) - 1] = 0;
549 
550  TRACE("[LIXTASK] Found init_task @ 0x%016llx with name %s\n", ts, comm);
551 
552  *InitTask = ts;
553 
554  found = TRUE;
555  break;
556  }
557 
558  IntVirtMemUnmap(&p);
559 
560  if (found)
561  {
562  return INT_STATUS_SUCCESS;
563  }
564  }
565 
566  return INT_STATUS_NOT_FOUND;
567 }
568 
569 
570 static QWORD gTaskMapped = 0;
571 static BYTE *gTaskPtr1 = NULL;
572 static BYTE *gTaskPtr2 = NULL;
573 
574 
575 static INTSTATUS
577  _In_ QWORD TaskGva
578  )
590 {
591  INTSTATUS status;
592 
593  gTaskMapped = TaskGva;
594 
596  if (!INT_SUCCESS(status))
597  {
598  ERROR("[ERROR] IntVirtMemMap failed for %llx: %08x\n", gTaskMapped, status);
599  return status;
600  }
601 
602  return INT_STATUS_SUCCESS;
603 }
604 
605 
606 static INTSTATUS
608  _In_ DWORD Offset,
609  _In_ DWORD Size,
610  _Out_ void *Buffer)
611 
612 {
624  {
626  }
627 
628  // NOTE: Should we validate the offset ?! I think that's beyond the scope of this function...
629 
630  QWORD gva = gTaskMapped + Offset;
631 
632  if (PAGE_COUNT(gTaskMapped, (QWORD)Offset + Size) > 1)
633  {
634  DWORD remaining = Size;
635 
637  {
638  DWORD toRead = PAGE_REMAINING(gva);
639 
640  memcpy(Buffer, gTaskPtr1 + Offset, toRead);
641 
642  remaining -= toRead;
643  }
644 
645  if (NULL == gTaskPtr2)
646  {
647  QWORD nextPage = (gTaskMapped + PAGE_SIZE) & PAGE_MASK;
648 
649  INTSTATUS status = IntVirtMemMap(nextPage, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &gTaskPtr2);
650  if (!INT_SUCCESS(status))
651  {
652  ERROR("[ERROR] IntVirtMemMap failed for %llx: %08x\n", nextPage, status);
653  return status;
654  }
655  }
656 
657  memcpy((BYTE *)Buffer + (Size - remaining), gTaskPtr2 + ((gva + Size - remaining) & PAGE_OFFSET), remaining);
658  }
659  else
660  {
661  // The whole it's in the first page
662  memcpy(Buffer, gTaskPtr1 + Offset, Size);
663  }
664 
665  return INT_STATUS_SUCCESS;
666 }
667 
668 
669 static void
671  void
672  )
676 {
678 
679  if (gTaskPtr2)
680  {
682  }
683 
684  gTaskPtr1 = NULL;
685  gTaskPtr2 = NULL;
686 
687  gTaskMapped = 0;
688 }
689 
690 
691 static INTSTATUS
693  _In_opt_ QWORD MmStruct,
694  _In_ LIX_TASK_OBJECT *Task,
695  _In_opt_ LIX_TASK_OBJECT *Parent
696  )
720 {
721  INTSTATUS status;
722  QWORD mmGva, pgd;
723 
724  if (Task->KernelMode)
725  {
727  }
728 
729  if (0 == MmStruct)
730  {
731  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Mm), sizeof(mmGva), &mmGva);
732  if (!INT_SUCCESS(status))
733  {
734  ERROR("[ERROR] Fetching mm failed in task 0x%016llx: 0x%08x\n", Task->Gva, status);
735  return status;
736  }
737  }
738  else
739  {
740  mmGva = MmStruct;
741  }
742 
743  if (!IS_KERNEL_POINTER_LIX(mmGva))
744  {
746  }
747 
748  Task->MmGva = mmGva;
749 
750  if (Parent && Parent->MmGva == mmGva)
751  {
752  Task->Cr3 = Parent->Cr3;
753 
754  return INT_STATUS_SUCCESS;
755  }
756 
757  status = IntKernVirtMemFetchQword(mmGva + LIX_FIELD(MmStruct, Pgd), &pgd);
758  if (!INT_SUCCESS(status))
759  {
760  ERROR("[ERROR] IntKernVirtMemFetchQword failed for GVA 0x%016llx: 0x%08x\n",
761  mmGva + LIX_FIELD(MmStruct, Pgd), status);
762  goto _cleanup_and_exit;
763  }
764 
765  if (!IS_KERNEL_POINTER_LIX(pgd))
766  {
767  ERROR("[ERROR] The PGD 0x%016llx @ 0x%016llx (offset %x) is not a valid one!\n",
768  pgd, mmGva + LIX_FIELD(MmStruct, Pgd), LIX_FIELD(MmStruct, Pgd));
769 
771 
772  goto _cleanup_and_exit;
773  }
774 
775  status = IntTranslateVirtualAddress(pgd, gGuest.Mm.SystemCr3, &Task->Cr3);
776  if (!INT_SUCCESS(status))
777  {
778  ERROR("[ERROR] Failed translating PGD 0x%016llx: 0x%08x\n", pgd, status);
779  Task->Cr3 = 0;
780  }
781 
782  status = INT_STATUS_SUCCESS;
783 
784 _cleanup_and_exit:
785  if (!INT_SUCCESS(status))
786  {
787  Task->Cr3 = 0;
788  }
789 
790  return status;
791 }
792 
793 
794 INTSTATUS
796  _In_ DWORD CpuNumber,
797  _Out_ QWORD *TaskStruct
798  )
809 {
810  INTSTATUS status;
811  QWORD gsBase;
812  QWORD current;
813 
814  if (TaskStruct == NULL)
815  {
817  }
818 
819  if (CpuNumber == IG_CURRENT_VCPU)
820  {
821  CpuNumber = gVcpu->Index;
822  }
823 
824  status = IntGsRead(CpuNumber, &gsBase);
825  if (!INT_SUCCESS(status))
826  {
827  ERROR("[ERROR] IntGsRead failed for cpu %d: 0x%08x.\n", CpuNumber, status);
828  return status;
829  }
830 
831  if (!IS_KERNEL_POINTER_LIX(gsBase))
832  {
833  WARNING("[WARNING] 'gs' for cpu %d is not in kernel mode: %llx\n", CpuNumber, gsBase);
835  }
836 
838  if (!INT_SUCCESS(status))
839  {
840  ERROR("[ERROR] IntKernVirtMemPatchQword failed for gva 0x%llx with status: 0x%08x\n",
841  gsBase + gLixGuest->OsSpecificFields.CurrentTaskOffset, status);
842  return status;
843  }
844 
845  if (__unlikely(!IS_KERNEL_POINTER_LIX(current) || (current % 8)))
846  {
847  ERROR("[ERROR] Current task 0x%016llx is not valid!\n", current);
849  }
850 
851  *TaskStruct = current;
852 
853  return INT_STATUS_SUCCESS;
854 }
855 
856 
859  _In_ DWORD CpuNumber
860  )
873 {
874  QWORD currentTs;
875 
876  INTSTATUS status = IntLixTaskGetCurrentTaskStruct(CpuNumber, &currentTs);
877  if (!INT_SUCCESS(status))
878  {
879  return NULL;
880  }
881 
882  return IntLixTaskFindByGva(currentTs);
883 }
884 
885 
886 static __forceinline QWORD
888  _In_ QWORD Pgd
889  )
897 {
898  return Pgd & ~(BIT(LIX_PTI_PGTABLE_SWITCH_BIT));
899 }
900 
901 
902 static __forceinline QWORD
904  _In_ QWORD Pgd
905  )
913 {
914  return Pgd | BIT(LIX_PTI_PGTABLE_SWITCH_BIT);
915 }
916 
917 
918 QWORD
920  _In_ QWORD Cr3
921  )
929 {
930  Cr3 &= CR3_LONG_MODE_MASK;
931 
932  if (!gGuest.KptiActive)
933  {
934  return Cr3;
935  }
936 
937  return IntLixUserToKernelPgd(Cr3);
938 }
939 
940 
943  _In_ QWORD Cr3
944  )
953 {
954  if (0 == Cr3)
955  {
956  return NULL;
957  }
958 
960 
961  for_each_task(pTask)
962  {
963  if (pTask->Cr3 == Cr3)
964  {
965  return pTask;
966  }
967  }
968 
969  return NULL;
970 }
971 
972 
975  _In_ QWORD MmGva
976  )
985 {
987  {
988  if (pTask->MmGva == MmGva)
989  {
990  return pTask;
991  }
992  }
993 
994  return NULL;
995 }
996 
997 
1000  _In_ QWORD MmGva
1001  )
1010 
1011 {
1012  for_each_task(pTask)
1013  {
1014  if (pTask->MmGva == MmGva)
1015  {
1016  return pTask;
1017  }
1018  }
1019 
1020  return NULL;
1021 }
1022 
1023 
1026  _In_ QWORD TaskStruct
1027  )
1036 
1037 {
1038  for_each_task(pTask)
1039  {
1040  if (pTask->Gva == TaskStruct)
1041  {
1042  return pTask;
1043  }
1044  }
1045 
1046  return NULL;
1047 }
1048 
1049 
1052  _In_ DWORD Pid
1053  )
1062 
1063 {
1064  for_each_task(pTask)
1065  {
1066  if (pTask->Pid == Pid)
1067  {
1068  return pTask;
1069  }
1070  }
1071 
1072  return NULL;
1073 }
1074 
1075 
1076 DWORD
1078  void
1079  )
1083 {
1084  DWORD count = 0;
1085  for_each_task(pTask)
1086  {
1087  if (pTask->Exec)
1088  {
1089  ++count;
1090  }
1091  }
1092 
1093  return count;
1094 }
1095 
1096 
1097 INTSTATUS
1099  _In_ const LIX_TASK_OBJECT *Task,
1100  _Out_ LIX_TRAP_FRAME *TrapFrame
1101  )
1121 {
1122  INTSTATUS status;
1123  QWORD trapFrameGva, kmStack;
1124 
1125  status = IntKernVirtMemFetchQword(Task->Gva + LIX_FIELD(TaskStruct, Stack), &kmStack);
1126  if (!INT_SUCCESS(status))
1127  {
1128  ERROR("[ERROR] Failed to read km stack pointer for task %s (%d 0x%llx). Status: 0x%08x\n",
1129  Task->Comm, Task->Pid, Task->Gva, status);
1130  return status;
1131  }
1132 
1133  trapFrameGva = kmStack + LIX_FIELD(Info, ThreadSize) - sizeof(*TrapFrame);
1134 
1135  memzero(TrapFrame, sizeof(*TrapFrame));
1136 
1137  return IntKernVirtMemRead(trapFrameGva, sizeof(*TrapFrame), TrapFrame, NULL);
1138 }
1139 
1140 
1141 static LIX_PROTECTED_PROCESS *
1143  _In_ const LIX_TASK_OBJECT *Task
1144  )
1153 {
1155  {
1156  return NULL;
1157  }
1158 
1160  {
1161  if ((pProt->NamePattern && Task->Path && IntMatchPatternUtf8(pProt->NamePattern, Task->Path->Name, 0)) ||
1162  IntMatchPatternUtf8(pProt->CommPattern, Task->Comm, INTRO_MATCH_TRUNCATED))
1163  {
1164  return pProt;
1165  }
1166  }
1167 
1168  return NULL;
1169 }
1170 
1171 
1172 void
1174  _In_ const void *Name,
1175  _In_ const CAMI_STRING_ENCODING Encoding,
1176  _In_ const CAMI_PROT_OPTIONS *Options
1177  )
1186 {
1187  if (Encoding != CAMI_STRING_ENCODING_UTF8)
1188  {
1189  WARNING("[WARNING] Unsupported string encoding: %d\n", Encoding);
1190  return;
1191  }
1192 
1193  for_each_task_to_protect(pProcess)
1194  {
1195  if (IntMatchPatternUtf8(Name, pProcess->CommPattern, 0))
1196  {
1197  pProcess->Protection.Current = pProcess->Protection.Original & ~(Options->ForceOff);
1198  pProcess->Protection.Beta = Options->ForceBeta;
1199  pProcess->Protection.Feedback = Options->ForceFeedback;
1200 
1201  TRACE("[CAMI] Protected process info updated. Original : 0x%llx, Current : 0x%llx, "
1202  "Beta : 0x%llx, Feedback : 0x%llx", pProcess->Protection.Original, pProcess->Protection.Current,
1203  pProcess->Protection.Beta, pProcess->Protection.Feedback);
1204  }
1205  }
1206 }
1207 
1208 
1209 static INTSTATUS
1211  _In_ LIX_TASK_OBJECT *Task
1212  )
1221 {
1222  INTSTATUS status;
1223  QWORD flags;
1224 
1225  if (!(Task->Protection.Mask & PROC_OPT_PROT_EXPLOIT))
1226  {
1228  }
1229 
1230  // Invalidate all the entries inside the ICACHE associated to this process,
1231  // since the CR3 will not be hooked anymore.
1232  status = IntIcFlushVaSpace(gGuest.InstructionCache, Task->Cr3);
1233  if (!INT_SUCCESS(status))
1234  {
1235  ERROR("[ERROR] IntIcFlushVaSpace failed: 0x%08x\n", status);
1236  }
1237 
1238  IntLixMmDestroyVmas(Task);
1239 
1240  status = IntKernVirtMemFetchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), &flags);
1241  if (!INT_SUCCESS(status))
1242  {
1243  CRITICAL("[ERROR] IntKernVirtMemFetchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1244  }
1245  else
1246  {
1247  flags &= ~LIX_MM_PROT_MASK;
1248 
1249  // NOTE: IntVirtMemSafeWrite should be used instead, but it will induce significant performance penalty.
1250  status = IntKernVirtMemPatchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), flags);
1251  if (!INT_SUCCESS(status))
1252  {
1253  ERROR("[ERROR] IntKernVirtMemPatchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1254  }
1255  }
1256 
1257  if (Task->HookObject)
1258  {
1259  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&Task->HookObject, 0);
1260  if (!INT_SUCCESS(status))
1261  {
1262  ERROR("[ERROR] IntHookObjectDestroy failed: %08x", status);
1263  }
1264  }
1265 
1266  RemoveEntryList(&Task->ExploitProtProcLink);
1267 
1268  return INT_STATUS_SUCCESS;
1269 }
1270 
1271 
1272 static INTSTATUS
1274  _In_ LIX_TASK_OBJECT *Task
1275  )
1288 {
1289  INTSTATUS status;
1290  QWORD flags;
1291 
1292  if (!(Task->Protection.Mask & PROC_OPT_PROT_EXPLOIT))
1293  {
1295  }
1296 
1297  if (0 == Task->Cr3)
1298  {
1299  ERROR("[ERROR] Requesting to protect process '%s' (%d, %llx, %llx)but it has no CR3 (mm 0x%016llx)!\n",
1300  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, Task->MmGva);
1302  }
1303 
1304  if (Task->StaticDetected)
1305  {
1306  // This shouldn't happen since we ignore tasks which have PF_EXITING flag set.
1307  // But it's better to do it anyway.
1308  DWORD mmUsers = 0, mmCount = 0;
1309 
1310  status = IntKernVirtMemFetchDword(Task->MmGva + LIX_FIELD(MmStruct, MmUsers), &mmUsers);
1311  if (!INT_SUCCESS(status))
1312  {
1313  ERROR("[ERROR] Failed getting mm_users: %08x\n", status);
1314  goto _protect_task;
1315  }
1316 
1317  status = IntKernVirtMemFetchDword(Task->MmGva + LIX_FIELD(MmStruct, MmCount), &mmCount);
1318  if (!INT_SUCCESS(status))
1319  {
1320  ERROR("[ERROR] Failed getting mm_count: %08x\n", status);
1321  goto _protect_task;
1322  }
1323 
1324  if (0 == mmUsers || 0 == mmCount)
1325  {
1326  WARNING("[WARNING] Process %s (%d, %llx, %llx) has a dying mm @ %llx: (%d, %d)!\n",
1327  Task->Comm, Task->Pid, Task->Gva, Task->Cr3, Task->MmGva, mmUsers, mmCount);
1328 
1330  }
1331  }
1332 
1333 _protect_task:
1334  status = IntHookObjectCreate(introObjectTypeUmGenericNxZone, Task->Cr3, &Task->HookObject);
1335  if (!INT_SUCCESS(status))
1336  {
1337  ERROR("[ERROR] IntHookObjectCreate failed: %08x\n", status);
1338  return status;
1339  }
1340 
1341  status = IntLixMmPopulateVmas(Task);
1342  if (!INT_SUCCESS(status))
1343  {
1344  ERROR("[ERROR] IntLixMmPopulateVmas failed: 0x%08x\n", status);
1345  goto _free_and_exit;
1346  }
1347 
1348  InsertTailList(&gLixProtectedTasks, &Task->ExploitProtProcLink);
1349 
1350  status = IntKernVirtMemFetchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), &flags);
1351  if (!INT_SUCCESS(status))
1352  {
1353  ERROR("[ERROR] IntKernVirtMemFetchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1354  goto _free_and_exit;
1355  }
1356 
1357  flags |= LIX_MM_PROT_MASK;
1358 
1359  // NOTE: IntVirtMemSafeWrite should be used instead, but it will induce significant performance penalty.
1360  status = IntKernVirtMemPatchQword(Task->MmGva + LIX_FIELD(MmStruct, Flags), flags);
1361  if (!INT_SUCCESS(status))
1362  {
1363  ERROR("[ERROR] IntKernVirtMemPatchQword failed for mm %llx: %08x\n", Task->MmGva, status);
1364  goto _free_and_exit;
1365  }
1366 
1367  status = INT_STATUS_SUCCESS;
1368 
1369 _free_and_exit:
1370  if (!INT_SUCCESS(status))
1371  {
1373  }
1374 
1375  return status;
1376 }
1377 
1378 
1379 static INTSTATUS
1381  _In_ LIX_TASK_OBJECT *Task,
1382  _In_opt_ LIX_TASK_OBJECT *Parent
1383  )
1394 {
1395  INTSTATUS status;
1396  BOOLEAN sameCr3;
1397 
1398  if (Task->Interpreter)
1399  {
1401  }
1402 
1404  {
1406  }
1407 
1408  // No point in checking the list if the Task is actually a thread/fork. The task name can be
1409  // anything, so the threads/forks should inherit the parent's protection.
1410  if (Task->Exec)
1411  {
1412  const LIX_PROTECTED_PROCESS *pProt = IntLixTaskShouldProtect(Task);
1413 
1414  if (NULL != pProt)
1415  {
1416  Task->Protection.Mask = pProt->Protection.Current;
1417  Task->Protection.Beta = pProt->Protection.Beta;
1418  Task->Protection.Feedback = pProt->Protection.Feedback;
1419  Task->Context = pProt->Context;
1420  }
1421  else
1422  {
1423  Task->Protection.Mask = 0;
1424  Task->Context = 0;
1425  }
1426 
1427  Task->RootProtectionMask = Task->Protection.Mask;
1428  }
1429  else if (Parent)
1430  {
1431  // A thread/fork should inherit the parent's root protection.
1432  // This is for a special case where a thread does fork, because
1433  // the thread protection mask will not include PROC_OPT_PROT_EXPLOIT
1434  Task->Protection.Mask = Parent->RootProtectionMask;
1435  Task->Protection.Beta = Parent->Protection.Beta;
1436  Task->Protection.Feedback = Parent->Protection.Feedback;
1437 
1438  Task->RootProtectionMask = Parent->RootProtectionMask;
1439  Task->Context = Parent->Context;
1440  }
1441 
1442  if (0 == Task->Protection.Mask)
1443  {
1445  }
1446 
1447  sameCr3 = Parent && (Task->Cr3 == Parent->Cr3);
1448 
1449  // Clean the protection flags (for example, if the parent & child share the same CR3, then
1450  // it's pointless to activate protection again on the same CR3).
1451  if (sameCr3)
1452  {
1454  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
1455  Task->Protection.Mask &= ~PROC_OPT_PROT_CORE_HOOKS;
1456  }
1457 
1458  status = IntLixTaskActivateExploitProtection(Task);
1459  if (!INT_SUCCESS(status))
1460  {
1461  ERROR("[ERROR] Process '%s' (%d, %llx, %llx) will not be exploit-protected: %08x!\n",
1462  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
1463 
1464  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
1465 
1466  status = INT_STATUS_SUCCESS;
1467  }
1468 
1469  // Keep the threads as protected... But mark the processes that failed to activate
1470  // the given protection as !protected
1471  if (!sameCr3 && (Task->Protection.Mask == 0))
1472  {
1473  ERROR("[ERROR] Task %s, 0x%016llx failed to activate any protection!\n", Task->Comm, Task->Gva);
1474 
1475  Task->Protected = FALSE;
1476 
1477  if (INT_SUCCESS(status))
1478  {
1480  }
1481 
1482  return status;
1483  }
1484 
1485  if (IntLixTaskMustLog(Task, TRUE))
1486  {
1487  TRACE("[PROC] Activated protection %llx for '%s' (%d, %llx, %llx)\n",
1488  Task->Protection.Mask, Task->ProcName, Task->Pid, Task->Gva, Task->Cr3);
1489  }
1490 
1491  Task->Protected = TRUE;
1492 
1493  return INT_STATUS_SUCCESS;
1494 }
1495 
1496 
1497 static void
1499  _In_ LIX_TASK_OBJECT *Task
1500  )
1506 {
1507  INTSTATUS status;
1508 
1509  if (!Task->Protected)
1510  {
1511  return;
1512  }
1513 
1515  if (!INT_SUCCESS(status))
1516  {
1517  WARNING("[WARNING] Process '%s' (%d, %llx, %llx) failed to deactivate protection: %08x\n",
1518  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
1519  }
1520 
1521  if (IntLixTaskMustLog(Task, TRUE))
1522  {
1523  LOG("[PROC] Deactivated protection for %s '%s' (%d, %llx, %llx)!\n",
1524  Task->IsThread ? "thread" : "process", Task->ProcName,
1525  Task->Pid, Task->Gva, Task->Cr3);
1526  }
1527 
1528  Task->Protection.Mask = 0;
1529  Task->RootProtectionMask = 0;
1530  Task->Context = 0;
1531  Task->Protected = FALSE;
1532 }
1533 
1534 
1535 static INTSTATUS
1537  _In_ LIX_TASK_OBJECT *Process,
1538  _In_ QWORD BinprmGva
1539  )
1550 {
1551  INTSTATUS status;
1552  QWORD vmaStart, vmaEnd, iter, file;
1553  DWORD argc, curLength, allocationSize;
1554  BYTE *pMapping = NULL;
1555 
1556  status = IntVirtMemMap(BinprmGva, LIX_FIELD(Binprm, Sizeof), gGuest.Mm.SystemCr3, 0, &pMapping);
1557  if (!INT_SUCCESS(status))
1558  {
1559  ERROR("[ERROR] IntVirtMemMap failed for %llx: 0x%08x\n", BinprmGva, status);
1560  return status;
1561  }
1562 
1563  argc = *(DWORD *)(pMapping + LIX_FIELD(Binprm, Argc));
1564  file = *(QWORD *)(pMapping + LIX_FIELD(Binprm, Vma));
1565 
1566  IntVirtMemUnmap(&pMapping);
1567 
1568  status = IntVirtMemMap(file,
1569  MAX(LIX_FIELD(Vma, VmaStart), LIX_FIELD(Vma, VmaEnd)) + sizeof(QWORD),
1571  0,
1572  &pMapping);
1573  if (!INT_SUCCESS(status))
1574  {
1575  ERROR("[ERROR] IntVirtMemMap failed for %llx: 0x%08x\n", file, status);
1576  return status;
1577  }
1578 
1579  vmaStart = *(QWORD *)(pMapping + LIX_FIELD(Vma, VmaStart));
1580  vmaEnd = *(QWORD *)(pMapping + LIX_FIELD(Vma, VmaEnd));
1581 
1582  IntVirtMemUnmap(&pMapping);
1583 
1584  if ((vmaStart | vmaEnd) & PAGE_OFFSET)
1585  {
1586  ERROR("[ERROR] VMA limits are not PAGE_SIZE aligned: start=0x%llx end = 0x%llx", vmaStart, vmaEnd);
1588  }
1589 
1590  if (vmaStart >= vmaEnd)
1591  {
1592  ERROR("[ERROR] Start of vma_struct %llx is bigger or equal that the end %llx!\n", vmaStart, vmaEnd);
1594  }
1595 
1596  if (vmaEnd - vmaStart > ONE_GIGABYTE)
1597  {
1598  ERROR("[ERROR] Argpage VMA is to big: 0x%llu. Cmdline will not be fetched\n", vmaEnd - vmaStart);
1600  }
1601 
1602  // Skip pages which are not present
1603  for (iter = vmaStart;
1604  iter < vmaEnd && (!INT_SUCCESS(IntVirtMemMap(iter, PAGE_SIZE, Process->Cr3, 0, &pMapping)));
1605  iter += PAGE_SIZE) { }
1606 
1607  if ((iter >= vmaEnd) || NULL == pMapping)
1608  {
1609  ERROR("[ERROR] Could not read cmdline. Not a single page from VMA 0x%llx to 0x%llx is present!\n",
1610  vmaStart, vmaEnd);
1611  return INT_STATUS_NOT_FOUND;
1612  }
1613 
1614  // vmaEnd - vmaStart is always <= 1GB
1615  allocationSize = (DWORD)(vmaEnd - iter);
1616 
1617  Process->CmdLine = HpAllocWithTag(allocationSize, IC_TAG_PCMD);
1618  if (NULL == Process->CmdLine)
1619  {
1620  IntVirtMemUnmap(&pMapping);
1622  }
1623 
1624  curLength = 0;
1625  for (;;)
1626  {
1627  DWORD parsed = 0;
1628  while ((0 == curLength) && (parsed < PAGE_SIZE) && (0 == pMapping[parsed]))
1629  {
1630  parsed++;
1631  }
1632 
1633  while (parsed < PAGE_SIZE)
1634  {
1635  if (0 == pMapping[parsed])
1636  {
1637  argc--;
1638  if (0 == argc)
1639  {
1640  break;
1641  }
1642  Process->CmdLine[curLength] = ' ';
1643  }
1644  else
1645  {
1646  Process->CmdLine[curLength] = pMapping[parsed];
1647  }
1648 
1649  parsed++;
1650  curLength++;
1651  }
1652 
1653  IntVirtMemUnmap(&pMapping);
1654 
1655  if (0 == argc)
1656  {
1657  break;
1658  }
1659 
1660  iter += PAGE_SIZE;
1661  if (iter >= vmaEnd)
1662  {
1663  WARNING("[WARNING] Reached end of vma, but there are %d more args to be read!\n", argc);
1664  break;
1665  }
1666 
1667  status = IntVirtMemMap(iter, PAGE_SIZE, Process->Cr3, 0, &pMapping);
1668  if (!INT_SUCCESS(status))
1669  {
1670  ERROR("[ERROR] IntVirtMemMap failed: %08x\n", status);
1671  break;
1672  }
1673  }
1674 
1675  // Avoid the off-by-one if the commandline page is corrupted.
1676  if (curLength >= allocationSize)
1677  {
1678  curLength = allocationSize - 1;
1679  }
1680 
1681  Process->CmdLine[curLength] = 0;
1682  Process->CmdLineLength = curLength;
1683 
1684  return INT_STATUS_SUCCESS;
1685 }
1686 
1687 
1688 static void
1690  _Inout_ LIX_TASK_OBJECT *Task
1691  )
1699 {
1700  if (Task->Path)
1701  {
1702  Task->ProcName = Task->Path->Path;
1703  Task->ProcNameLength = (DWORD)Task->Path->PathLength;
1704  }
1705  else
1706  {
1707  Task->ProcName = Task->Comm;
1708  Task->ProcNameLength = strlen(Task->Comm);
1709  }
1710 }
1711 
1712 
1713 static void
1715  _In_ QWORD FileGva,
1716  _In_ QWORD DPathGva,
1717  _Out_ LIX_TASK_OBJECT *Task
1718  )
1726 {
1727  INTSTATUS status = IntLixFileGetDentry(FileGva, &Task->ExeFileDentry);
1728  if (!INT_SUCCESS(status))
1729  {
1730  ERROR("[ERROR] IntLixFileGetDentry failed for %llx: %08x\n", FileGva, status);
1731  return;
1732  }
1733 
1734  if (!IS_ERR(DPathGva) && DPathGva)
1735  {
1736  Task->Path = IntLixTaskPathGetByPath(DPathGva, Task->ExeFileDentry);
1737  }
1738 
1739  if (NULL == Task->Path)
1740  {
1741  Task->Path = IntLixTaskPathGetByDentry(FileGva, 0, Task->ExeFileDentry);
1742  }
1743 }
1744 
1745 
1746 static INTSTATUS
1748  _In_ LIX_TASK_OBJECT *OriginalTask,
1749  _In_ QWORD BinprmGva,
1750  _In_ QWORD PathGva,
1751  _Out_ LIX_TASK_OBJECT *UpdatedTask
1752  )
1765 {
1766  INTSTATUS status;
1767  QWORD pathGva, interpGva;
1768  DWORD pid;
1769 
1770  BYTE *pBinprm;
1771  int exitSignal;
1772 
1773  if (!IS_KERNEL_POINTER_LIX(BinprmGva))
1774  {
1776  }
1777 
1778  UpdatedTask->ActualParent = OriginalTask->Parent;
1779  UpdatedTask->AgentTag = OriginalTask->AgentTag;
1780  UpdatedTask->Context = OriginalTask->Context;
1781  UpdatedTask->CreationTime = OriginalTask->CreationTime;
1782  UpdatedTask->Gva = OriginalTask->Gva;
1783  UpdatedTask->IsThread = FALSE;
1784  UpdatedTask->KernelMode = FALSE;
1785  UpdatedTask->Parent = OriginalTask->Parent;
1786  UpdatedTask->RealParent = OriginalTask->RealParent;
1787  UpdatedTask->Tgid = OriginalTask->Tgid;
1788  UpdatedTask->Pid = OriginalTask->Pid;
1789 
1790  memcpy(UpdatedTask->Comm, OriginalTask->Comm, sizeof(UpdatedTask->Comm));
1791 
1792  pBinprm = NULL;
1793 
1794  UpdatedTask->ReExecToSelf = FALSE;
1795 
1796  status = IntVirtMemMap(BinprmGva, LIX_FIELD(Binprm, Sizeof), gGuest.Mm.SystemCr3, 0, &pBinprm);
1797  if (!INT_SUCCESS(status))
1798  {
1799  ERROR("[ERROR] IntVirtMemMap failed for GVA 0x%016llx: 0x%08x\n", BinprmGva, status);
1800  goto _cleanup_and_exit;
1801  }
1802 
1803  status = _IntLixTaskStartMap(UpdatedTask->Gva);
1804  if (!INT_SUCCESS(status))
1805  {
1806  ERROR("[ERROR] _IntLixTaskStartMap failed for task %llx: 0x%08x\n", UpdatedTask->Gva, status);
1807  goto _cleanup_and_exit;
1808  }
1809 
1810  {
1811  DWORD in;
1812 
1813  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, InExecve), sizeof(in), &in);
1814  if (!INT_SUCCESS(status))
1815  {
1816  ERROR("[ERROR] _IntLixTaskRead failed for %llx: %08x\n",
1817  UpdatedTask->Gva + LIX_FIELD(TaskStruct, InExecve), status);
1818  }
1819  else if (0 == (in & BIT(LIX_FIELD(TaskStruct, InExecveBit))))
1820  {
1821  ERROR("[ERROR][CRITICAL] in_execve is not in fact set: 0x%02x\n", in);
1822  }
1823  }
1824 
1825  status = IntLixTaskFetchMm(*(QWORD *)(pBinprm + LIX_FIELD(Binprm, Mm)), UpdatedTask, NULL);
1826  if (!INT_SUCCESS(status))
1827  {
1828  ERROR("[ERROR] IntLixTaskFetchMm failed for %s: 0x%08x. The process cannot be protected!\n",
1829  UpdatedTask->ProcName, status);
1830  }
1831 
1832  IntLixTaskGetPath(*(QWORD *)(pBinprm + LIX_FIELD(Binprm, File)), PathGva, UpdatedTask);
1833 
1834  pathGva = *(QWORD *)(pBinprm + LIX_FIELD(Binprm, Filename));
1835  interpGva = *(QWORD *)(pBinprm + LIX_FIELD(Binprm, Interp));
1836 
1837  if (interpGva && interpGva != pathGva)
1838  {
1839  status = IntReadString(interpGva, 2, FALSE, &UpdatedTask->Interpreter, &UpdatedTask->InterpLength);
1840  if (!INT_SUCCESS(status))
1841  {
1842  UpdatedTask->Interpreter = NULL;
1843  UpdatedTask->InterpLength = 0;
1844  }
1845  }
1846 
1847  // The new exec process can change PID if it assumes the role of group leader
1848  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, ExitSignal), sizeof(exitSignal), &exitSignal);
1849  if (!INT_SUCCESS(status))
1850  {
1851  ERROR("[ERROR] Failed getting the PID from task_struct %llx: %08x\n",
1852  UpdatedTask->Gva + LIX_FIELD(TaskStruct, Pid), status);
1853  }
1854  else if (exitSignal < 0)
1855  {
1856  // This means that a thread is doing an exec, so get the PID of the group leader (which can be dead by now)
1857  QWORD groupLeader;
1858 
1859  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, GroupLeader), sizeof(groupLeader), &groupLeader);
1860  if (!INT_SUCCESS(status))
1861  {
1862  ERROR("[ERROR] Failed getting the group_leader from task_struct %llx: %08x\n",
1863  UpdatedTask->Gva + LIX_FIELD(TaskStruct, GroupLeader), status);
1864  }
1865  else
1866  {
1867  LIX_TASK_OBJECT *pGroupLeader = IntLixTaskFindByGva(groupLeader);
1868  if (NULL == pGroupLeader)
1869  {
1870  status = IntKernVirtMemFetchDword(groupLeader + LIX_FIELD(TaskStruct, Pid), &pid);
1871  if (!INT_SUCCESS(status))
1872  {
1873  ERROR("[ERROR] IntKernVirtMemFetchDword failed for %llx: %08x\n",
1874  groupLeader + LIX_FIELD(TaskStruct, Pid), status);
1875  }
1876  }
1877  else
1878  {
1879  pid = pGroupLeader->Pid;
1880  }
1881 
1882  if (UpdatedTask->Pid != pid)
1883  {
1884  TRACE("[INFO] Process '%s' changes PID from %d to %d\n", UpdatedTask->ProcName, UpdatedTask->Pid, pid);
1885 
1886  UpdatedTask->Pid = pid;
1887  }
1888  }
1889  }
1890 
1891  if (OriginalTask->ExeFileDentry == UpdatedTask->ExeFileDentry)
1892  {
1893  UpdatedTask->ReExecToSelf = TRUE;
1894  }
1895 
1896  if ((!UpdatedTask->ReExecToSelf) && OriginalTask->Interpreter && UpdatedTask->Interpreter)
1897  {
1898  if (OriginalTask->InterpLength == UpdatedTask->InterpLength &&
1899  0 == strcmp(OriginalTask->Interpreter, UpdatedTask->Interpreter))
1900  {
1901  UpdatedTask->ReExecToSelf = TRUE;
1902  }
1903  }
1904 
1905  IntLixTaskSetProcName(UpdatedTask);
1906 
1907  InitializeListHead(&UpdatedTask->Vmas);
1908 
1909  InitializeListHead(&UpdatedTask->ExploitProtProcLink);
1910 
1911  if (!LIX_FIELD(Info, CredAltered))
1912  {
1913  status = IntLixCredAdd(*(QWORD *)(pBinprm + LIX_FIELD(Binprm, Cred)), &UpdatedTask->Creds);
1914  if (!INT_SUCCESS(status))
1915  {
1916  ERROR("[ERROR] IntLixCredAdd failed for %s (%d 0x%llx). Status: 0x%08x\n",
1917  UpdatedTask->Comm, UpdatedTask->Pid, UpdatedTask->Gva, status);
1918  UpdatedTask->Creds = NULL;
1919  }
1920  }
1921 
1923 
1924  status = INT_STATUS_SUCCESS;
1925 
1926 _cleanup_and_exit:
1927  IntVirtMemUnmap(&pBinprm);
1928 
1929  return status;
1930 }
1931 
1932 
1933 static void
1935  _In_ LIX_TASK_OBJECT *Task,
1936  _In_ DWORD ExitCode,
1937  _In_ BOOLEAN Created,
1938  _In_ BOOLEAN Crashed,
1939  _In_ BOOLEAN StaticDetected
1940  )
1950 {
1951  INTSTATUS status;
1952  EVENT_PROCESS_EVENT *pProcEvent;
1953  LIX_TASK_OBJECT *pParent;
1954 
1956  {
1957  return;
1958  }
1959 
1960  if (Created && Task->ReExecToSelf)
1961  {
1962  return;
1963  }
1964 
1965  pProcEvent = &gAlert.Process;
1966  memzero(pProcEvent, sizeof(*pProcEvent));
1967 
1968  pProcEvent->Created = Created;
1969  pProcEvent->Protected = Task->Protected != 0;
1970  pProcEvent->Crashed = Crashed;
1971  pProcEvent->ExitStatus = ExitCode;
1972 
1973  if (!StaticDetected)
1974  {
1976  }
1977  else
1978  {
1979  pProcEvent->CurrentProcess.Valid = FALSE;
1980  }
1981 
1982  IntAlertFillLixProcess(Task, &pProcEvent->Child);
1983 
1984  pParent = IntLixTaskFindByGva(Task->Parent);
1985  if (pParent)
1986  {
1987  IntAlertFillLixProcess(pParent, &pProcEvent->Parent);
1988  }
1989  else
1990  {
1991  pProcEvent->Parent.Valid = FALSE;
1992  }
1993 
1994  status = IntNotifyIntroEvent(introEventProcessEvent, pProcEvent, sizeof(*pProcEvent));
1995  if (!INT_SUCCESS(status))
1996  {
1997  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
1998  }
1999 }
2000 
2001 
2002 static void
2004  _In_ LIX_TASK_OBJECT *Task,
2005  _In_ DWORD ExitCode,
2006  _In_ BOOLEAN Created
2007  )
2015 {
2016  INTSTATUS status = INT_STATUS_SUCCESS;
2017  EVENT_AGENT_EVENT *pAgentEvent;
2018 
2019  if (!Task->AgentTag)
2020  {
2021  return;
2022  }
2023 
2024  pAgentEvent = &gAlert.Agent;
2025  memzero(pAgentEvent, sizeof(*pAgentEvent));
2026 
2027  IntAlertFillLixProcess(Task, &pAgentEvent->CurrentProcess);
2028 
2029  pAgentEvent->ErrorCode = 0;
2030 
2031  pAgentEvent->Event = Created ? agentStarted : agentTerminated;
2032  pAgentEvent->AgentTag = Task->AgentTag;
2033  pAgentEvent->ErrorCode = ExitCode;
2034 
2035  status = IntNotifyIntroEvent(introEventAgentEvent, pAgentEvent, sizeof(*pAgentEvent));
2036  if (!INT_SUCCESS(status))
2037  {
2038  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
2039  }
2040 }
2041 
2042 
2043 INTSTATUS
2045  _In_ LIX_TASK_OBJECT *Task,
2046  _Out_opt_ QWORD *StackPointer,
2047  _Out_opt_ QWORD *StackBase,
2048  _Out_opt_ QWORD *StackLimit
2049  )
2060 {
2061  INTSTATUS status;
2062  LIX_TRAP_FRAME trapFrame;
2063  QWORD base, limit;
2064 
2065  status = IntLixTaskGetTrapFrame(Task, &trapFrame);
2066  if (!INT_SUCCESS(status))
2067  {
2068  ERROR("[ERROR] IntLixTaskGetTrapFrame failed: %08x\n", status);
2069  return status;
2070  }
2071 
2072  if (NULL != StackBase || NULL != StackLimit)
2073  {
2074  status = IntLixMmFindVmaRange(trapFrame.Rsp, Task, &limit, &base);
2075  if (!INT_SUCCESS(status))
2076  {
2077  ERROR("[ERROR] Failed to find stack limits for process %s (%d 0x%llx), rsp: 0x%llx. Status:0x%08x\n",
2078  Task->Comm, Task->Pid, Task->Gva, trapFrame.Rsp, status);
2079  return status;
2080  }
2081 
2082  if (NULL != StackBase)
2083  {
2084  *StackBase = base;
2085  }
2086 
2087  if (NULL != StackLimit)
2088  {
2089  *StackLimit = limit;
2090  }
2091  }
2092 
2093  if (NULL != StackPointer)
2094  {
2095  *StackPointer = trapFrame.Rsp;
2096  }
2097 
2098  return INT_STATUS_SUCCESS;
2099 }
2100 
2101 
2102 static INTSTATUS
2104  _In_ LIX_TASK_OBJECT *Parent,
2105  _In_ LIX_TASK_OBJECT *RealParent,
2106  _In_ QWORD TaskStruct,
2107  _In_ BOOLEAN StaticDetected,
2108  _Out_opt_ LIX_TASK_OBJECT **Task
2109  )
2126 {
2127  INTSTATUS status;
2128  LIX_TASK_OBJECT *pTask, *pActualParent;
2129  DWORD flags = 0;
2130  QWORD file = 0;
2131 
2132  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Flags), sizeof(flags), &flags);
2133  if (!INT_SUCCESS(status))
2134  {
2135  ERROR("[ERROR] Failed getting the flags in task 0x%016llx: 0x%08x\n", TaskStruct, status);
2136  return status;
2137  }
2138 
2139  if ((flags & PF_EXITING) != 0)
2140  {
2141  TRACE("[INFO] Task with 0x%llx is dying while initializing (static: %d)... Will ignore.\n",
2142  TaskStruct, StaticDetected);
2143  if (NULL != Task)
2144  {
2145  *Task = NULL;
2146  }
2148  }
2149 
2150  pTask = HpAllocWithTag(sizeof(*pTask), IC_TAG_POBJ);
2151  if (NULL == pTask)
2152  {
2154  }
2155 
2156  pTask->Gva = TaskStruct;
2157 
2158  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Pid), sizeof(pTask->Pid), &pTask->Pid);
2159  if (!INT_SUCCESS(status))
2160  {
2161  ERROR("[ERROR] Failed getting the 'PID' in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2162  goto _free_and_exit;
2163  }
2164 
2165  if (!StaticDetected)
2166  {
2167  LIX_TASK_OBJECT *pExistingTask = IntLixTaskFindByPid(pTask->Pid);
2168  if (pExistingTask)
2169  {
2170  //
2171  // For now, just log an error, this is a cache issue 99.999%
2172  //
2173  ERROR("[ERROR] [CRITICAL] There is already an existing task with PID %d\n", pTask->Pid);
2174 
2175  LOG("[ERROR] %s%s %s (%d/%d, %16llx)]\n",
2176  pExistingTask->IsThread ? "Thread" : "Process",
2177  pExistingTask->KernelMode ? "(KM)" : "",
2178  pExistingTask->ProcName,
2179  pExistingTask->Pid, pExistingTask->Tgid, pExistingTask->Gva);
2180  }
2181  }
2182 
2183  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Tgid), sizeof(pTask->Tgid), &pTask->Tgid);
2184  if (!INT_SUCCESS(status))
2185  {
2186  ERROR("[ERROR] Failed getting the 'tgid' in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2187  goto _free_and_exit;
2188  }
2189 
2191  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Comm), sizeof(pTask->Comm), pTask->Comm);
2192  if (!INT_SUCCESS(status))
2193  {
2194  ERROR("[ERROR] Failed reading 'comm' in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2195  goto _free_and_exit;
2196  }
2197 
2198  pTask->CommHash = Crc32String(pTask->Comm, INITIAL_CRC_VALUE);
2199 
2200  pTask->IsThread = pTask->Tgid != pTask->Pid;
2201 
2202  if (flags & PF_KTHREAD)
2203  {
2204  pTask->IsThread = TRUE;
2205  pTask->KernelMode = TRUE;
2206  }
2207 
2208  if (StaticDetected && !pTask->KernelMode)
2209  {
2210  // The actual way to determine if a thread was already executed (PF_FORKNOEXEC).
2211  // But there is another special case:
2212  // 1. exec /usr/bin/squid (new PID = 32)
2213  // 2. squid does a fork() (new PID = 33)
2214  // 3. original squid (PID 32) dies
2215  // the parent of squid (PID 33) becomes PID 1
2216  // but this should be considered an exec by us
2217  if (!(flags & PF_FORKNOEXEC) ||
2218  (!pTask->IsThread && RealParent->Pid == 1))
2219  {
2220  pTask->IsThread = FALSE;
2221  pTask->Exec = TRUE;
2222  }
2223  }
2224 
2225  // When activating protection, we care about the TGID, since that's the CR3
2226  // NOTE: in case this changes in future versions, then get the CR3 and search by that
2227  pActualParent = RealParent;
2228  if (!pTask->KernelMode && pTask->IsThread && RealParent->Pid != pTask->Tgid)
2229  {
2230  pActualParent = IntLixTaskFindByPid(pTask->Tgid);
2231  if (NULL == pActualParent)
2232  {
2233  WARNING("[WARNING] Task with TGID %d is dead\n", pTask->Tgid);
2234  pActualParent = RealParent;
2235  }
2236  }
2237 
2238  pTask->ActualParent = pActualParent->Gva;
2239  pTask->RealParent = RealParent->Gva;
2240  pTask->Parent = Parent->Gva;
2241 
2242  if (gGuest.OSVersion < LIX_CREATE_VERSION(3, 17, 0))
2243  {
2244  QWORD time[2] = {0};
2245 
2246  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, StartTime), sizeof(time), time);
2247  if (!INT_SUCCESS(status))
2248  {
2249  ERROR("[ERROR] Failed getting the start time in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2250  goto _free_and_exit;
2251  }
2252 
2253  // Convert to nanoseconds
2254  pTask->CreationTime = time[0] * NSEC_PER_SEC + time[1];
2255  }
2256  else
2257  {
2258  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, StartTime),
2259  sizeof(pTask->CreationTime),
2260  &pTask->CreationTime);
2261  if (!INT_SUCCESS(status))
2262  {
2263  ERROR("[ERROR] Failed getting the start time in task 0x%016llx: 0x%08x\n", pTask->Gva, status);
2264  goto _free_and_exit;
2265  }
2266  }
2267 
2268  // Forked processes should not have their own memory allocated for the path (if
2269  // you want a path, get the from the parent). Also, that helps us in no way, since we
2270  // protect every sub-process (at least until it's doing an exec).
2271  pTask->StaticDetected = StaticDetected;
2272 
2273  status = IntLixTaskFetchMm(0, pTask, pTask->IsThread ? pActualParent : NULL);
2274  if (!INT_SUCCESS(status))
2275  {
2276  ERROR("[ERROR] IntLixTaskFetchMm failed: 0x%08x. This task (%s) cannot be protected!\n",
2277  status, pTask->Comm);
2278  }
2279 
2280  if (pTask->IsThread || !pTask->StaticDetected || !IS_KERNEL_POINTER_LIX(pTask->MmGva))
2281  {
2282  // 1. If we have a thread, it's simple => just inherit the parent name
2283  // 2. If the process is not found at a static scan => we already got the filename for the task, no
2284  // point in doing it again
2285  // 3. An error occurred and we have no mm => nothing we can, so inherit parent
2286  pTask->ExeFileDentry = pActualParent->ExeFileDentry;
2287 
2288  pTask->Path = IntLixTaskPathGetRef(pActualParent->Path);
2289  }
2290  else
2291  {
2292  status = IntKernVirtMemFetchQword(pTask->MmGva + LIX_FIELD(MmStruct, ExeFile), &file);
2293  if (!INT_SUCCESS(status))
2294  {
2295  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: %08x\n",
2296  pTask->MmGva + LIX_FIELD(MmStruct, ExeFile), status);
2297  goto _initialize_and_prot;
2298  }
2299 
2300  status = IntLixFileGetDentry(file, &pTask->ExeFileDentry);
2301  if (!INT_SUCCESS(status))
2302  {
2303  ERROR("[ERROR] IntLixFileGetDentry failed for %llx: %08x\n", file, status);
2304  goto _initialize_and_prot;
2305  }
2306 
2307  pTask->Path = IntLixTaskPathGetByDentry(file, 0, pTask->ExeFileDentry);
2308  }
2309 
2310 _initialize_and_prot:
2311 
2312  InitializeListHead(&pTask->Vmas);
2314 
2315  InsertTailList(&gLixTasks, &pTask->Link);
2316 
2317  IntLixTaskSetProcName(pTask);
2318 
2319  if (StaticDetected)
2320  {
2321  pTask->IsPreviousAgent = pTask->Comm[14] == '?';
2322  }
2323 
2324  status = IntLixTaskActivateProtection(pTask, pActualParent);
2325  if (!INT_SUCCESS(status))
2326  {
2327  ERROR("[ERROR] IntLixTaskActivateProtection failed for %s (%llx): 0x%08x\n", pTask->Comm, pTask->Gva, status);
2328 
2330  }
2331 
2332  if (!pTask->KernelMode)
2333  {
2334  QWORD creds;
2335 
2336  // On static detected processes, use the real_cred pointer. We may catch the creds inside a
2337  // override_creds - revert_creds flow, and the creds won't be reliable.
2338  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Cred), sizeof(creds), &creds);
2339  if (INT_SUCCESS(status))
2340  {
2341  status = IntLixCredAdd(creds, &pTask->Creds);
2342  if (!INT_SUCCESS(status))
2343  {
2344  ERROR("[ERROR] IntLixCredAdd failed for task %s (%d 0x%llx) with status: 0x%08x!. Creds gva: 0x%llx\n",
2345  pTask->Comm, pTask->Pid, pTask->Gva, status, creds);
2346  }
2347  }
2348  else
2349  {
2350  ERROR("[ERROR] _IntLixTaskRead failed for task %s (%d 0x%llx): 0x%08x\n",
2351  pTask->Comm, pTask->Pid, pTask->Gva, status);
2352  }
2353 
2354  pTask->UserStack.Valid = TRUE;
2355 
2356  status = IntLixTaskGetUserStack(pTask, NULL, &pTask->UserStack.Base, &pTask->UserStack.Limit);
2357  if (!INT_SUCCESS(status))
2358  {
2359  ERROR("[ERROR] Failed to get user mode stack for process %s (%d, 0x%llx). Status: 0x%08x\n",
2360  pTask->Comm, pTask->Pid, pTask->Gva, status);
2361 
2362  pTask->UserStack.Valid = FALSE;
2363  }
2364  }
2365 
2366  pTask->Dpi.StolenTokens = RealParent->Dpi.StolenTokens || Parent->Dpi.StolenTokens;
2367 
2368  if (IntLixTaskMustLog(pTask, pTask->Protected != 0))
2369  {
2370  if (!pTask->IsThread)
2371  {
2372  LOG("[%s]%s %s (%s), (%d/%d, %llx, %llx) [from %s%s %s (%d, %16llx)]\n",
2373  pTask->Exec ? "EXEC" : "FORK",
2374  pTask->Protected ? "[PROT]" : "",
2375  pTask->ProcName,
2376  pTask->Comm,
2377  pTask->Pid, pTask->Tgid,
2378  pTask->Cr3, pTask->Gva,
2379  pActualParent->IsThread ? "Thread" : "Process",
2380  pActualParent->KernelMode ? "(KM)" : "",
2381  pActualParent->ProcName,
2382  pActualParent->Pid, pActualParent->Gva);
2383  }
2384  else
2385  {
2386  LOG("[THREAD]%s %s, (%d/%d, %llx, %llx)\n",
2387  pTask->Protected ? "[PROT]" : "",
2388  pTask->Comm,
2389  pTask->Pid, pTask->Tgid,
2390  pTask->Cr3, pTask->Gva);
2391  }
2392  }
2393 
2394  if (pActualParent->AgentTag)
2395  {
2396  // Mark this one as agent too
2397  pTask->AgentTag = IntLixAgentIncProcRef(pTask->Comm);
2398  }
2399 
2400  if (StaticDetected && pTask->Exec)
2401  {
2402  if (NULL == gLixGuest->InitProcessObj && pTask->Pid == 1)
2403  {
2404  gLixGuest->InitProcessObj = pTask;
2405  }
2406 
2407  IntLixTaskSendTaskEvent(pTask, 0, TRUE, FALSE, StaticDetected);
2408  }
2409 
2410  if (NULL != Task)
2411  {
2412  *Task = pTask;
2413  }
2414 
2415  return INT_STATUS_SUCCESS;
2416 
2417 _free_and_exit:
2418  if (NULL != pTask)
2419  {
2421  }
2422 
2423  return status;
2424 }
2425 
2426 
2427 static void
2429  _In_ LIX_TASK_OBJECT *Task
2430  )
2436 {
2437  if (Task->Protected)
2438  {
2440  }
2441 
2442  IntLixCredRemove(&Task->Creds);
2443 
2444  IntLixTaskPathFree(&Task->Path);
2445 
2446  if (NULL != Task->Interpreter)
2447  {
2448  HpFreeAndNullWithTag(&Task->Interpreter, IC_TAG_NAME);
2449  }
2450 
2451  if (NULL != Task->CmdLine)
2452  {
2453  HpFreeAndNullWithTag(&Task->CmdLine, IC_TAG_PCMD);
2454  }
2455 
2457 }
2458 
2459 
2460 static void
2462  _In_ LIX_TASK_OBJECT *Task
2463  )
2469 {
2470  INTSTATUS status;
2471  char specialChar = '?';
2472 
2473  if (!Task->AgentTag || gGuest.ShutDown)
2474  {
2475  return;
2476  }
2477 
2479  Task->Gva + LIX_FIELD(TaskStruct, Comm) + 14,
2480  1,
2481  &specialChar,
2482  IG_CS_RING_0);
2483  if (!INT_SUCCESS(status))
2484  {
2485  ERROR("[ERROR] IntVirtMemSafeWrite failed for task %llx: 0x%08x\n", Task->Gva, status);
2486  }
2487 }
2488 
2489 
2490 static void
2492  _In_ LIX_TASK_OBJECT *Task,
2493  _In_ DWORD ExitCode
2494  )
2501 {
2502  INTSTATUS status;
2503  DWORD signal;
2504  BOOLEAN crashed = FALSE, lastAgent = FALSE, wasProtected = FALSE;
2505 
2506  if (Task->Protected)
2507  {
2509  wasProtected = TRUE;
2510  }
2511 
2512  signal = ExitCode & 0x7f;
2513 
2514  if ((ExitCode & 0x80) ||
2515  signal == SIGQUIT ||
2516  signal == SIGILL ||
2517  signal == SIGTRAP ||
2518  signal == SIGABRT ||
2519  signal == SIGBUS ||
2520  signal == SIGFPE ||
2521  signal == SIGSEGV)
2522  {
2523  IntLixTaskSendExceptionEvent(signal, Task);
2524 
2525  crashed = TRUE;
2526  }
2527 
2528  if (__unlikely(Task->MustKill && signal != SIGKILL))
2529  {
2530  WARNING("[WARNING] Task %s (%d, %llx, %llx) was marked to be killed, but the signal it received is %d",
2531  Task->ProcName, Task->Pid, Task->Cr3, Task->Gva, signal);
2532  }
2533 
2534  Task->AgentTag = IntLixAgentDecProcRef(Task->Comm, &lastAgent);
2535 
2536  if (Task->Exec)
2537  {
2538  IntLixTaskSendTaskEvent(Task, ExitCode, FALSE, crashed, FALSE);
2539  }
2540 
2541  if (lastAgent)
2542  {
2543  IntLixTaskSendAgentEvent(Task, ExitCode, FALSE);
2544  }
2545 
2546  RemoveEntryList(&Task->Link);
2547 
2548  if (!Task->IsThread && !Task->KernelMode && Task->Cr3 != 0)
2549  {
2550  // Invalidate all the entries inside the ICACHE associated to this process.
2551  status = IntIcFlushVaSpace(gGuest.InstructionCache, Task->Cr3);
2552  if (!INT_SUCCESS(status))
2553  {
2554  ERROR("[ERROR] IntIcFlushVaSpace failed: 0x%08x\n", status);
2555  }
2556  }
2557 
2558  // One last check on process exit
2559 
2560  IntLixCredsVerify(Task);
2561 
2562  IntLixCredRemove(&Task->Creds);
2563 
2564  if (IntLixTaskMustLog(Task, wasProtected))
2565  {
2566  LOG("[EXIT] %s %s (%d, %llx, %llx), crashed: %d, signal: %d\n",
2567  Task->IsThread ? "Thread" : "Process", Task->ProcName, Task->Pid,
2568  Task->Cr3, Task->Gva, crashed, signal);
2569  }
2570 
2571  if (!Task->IsThread)
2572  {
2573  IntUDRemoveAllEntriesForCr3(Task->Cr3);
2574  }
2575 
2576  IntLixTaskRemoveEntry(Task);
2577 }
2578 
2579 
2581 static DWORD
2583  _In_ DWORD Flags
2584  )
2592 {
2594  {
2595  return idAccessToken;
2596  }
2597 
2599  {
2600  return idExploitClientExec;
2601  }
2602 
2603  ERROR("[ERROR] We do not have any known DPI flag set -> Flags:0x%x\n", Flags);
2604  return 0;
2605 }
2606 
2607 
2608 static void
2610  _In_ LIX_TASK_OBJECT *OldTask,
2611  _In_ LIX_TASK_OBJECT *NewTask,
2612  _In_ INTRO_ACTION Action,
2613  _In_ INTRO_ACTION_REASON Reason,
2614  _In_ DWORD PcType
2615  )
2625 {
2626  INTSTATUS status;
2628 
2629  pEvent = &gAlert.ProcessCreation;
2630  memzero(pEvent, sizeof(*pEvent));
2631 
2632  pEvent->Header.Action = Action;
2633  pEvent->Header.Reason = Reason;
2634  pEvent->Header.MitreID = idExecApi;
2635 
2637 
2638  // Fill with OldTask info, since that is the current process (it's more relevant this way)
2639  IntAlertFillLixProcess(OldTask, &pEvent->Header.CurrentProcess);
2640 
2642  IntAlertFillVersionInfo(&pEvent->Header);
2643 
2644  IntAlertFillLixProcess(NewTask, &pEvent->Originator);
2645  IntAlertFillLixProcess(OldTask, &pEvent->Victim);
2646 
2647  if (PcType)
2648  {
2649  pEvent->PcType = PcType;
2650  pEvent->Header.MitreID = IntLixTaskGetDpiMitreId(PcType);
2651  }
2652 
2653  status = IntNotifyIntroEvent(introEventProcessCreationViolation, pEvent, sizeof(*pEvent));
2654  if (!INT_SUCCESS(status))
2655  {
2656  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
2657  }
2658 }
2659 
2660 
2662 static DWORD
2664  _In_ LIX_TASK_OBJECT *Task
2665  )
2673 {
2674  DWORD flags = 0;
2675 
2676  if (Task->Dpi.IsPivoted)
2677  {
2679  }
2680 
2681  if (Task->Dpi.StolenTokens)
2682  {
2684  }
2685 
2686  return flags;
2687 }
2688 
2689 
2690 static void
2692  _In_ LIX_TASK_OBJECT *ChildTask,
2693  _In_ LIX_TASK_OBJECT *ParentTask,
2694  _In_ INTRO_OBJECT_TYPE ObjectType,
2695  _Out_ INTRO_ACTION *Action,
2696  _Out_ INTRO_ACTION_REASON *Reason
2697  )
2708 {
2709  INTSTATUS status = INT_STATUS_SUCCESS;
2710  EXCEPTION_UM_ORIGINATOR originator = { 0 };
2711  EXCEPTION_VICTIM_ZONE victim = { 0 };
2712 
2713  *Action = introGuestAllowed;
2714  *Reason = introReasonAllowed;
2715 
2716  if (ObjectType != introObjectTypeProcessCreation && ObjectType != introObjectTypeProcessCreationDpi)
2717  {
2718  ERROR("[ERROR] IntLixValidateProcessCreationRights called with object type %d!\n", ObjectType);
2719  return;
2720  }
2721 
2722  // If the process re-executes to self, then we'll allow it
2723  if (ChildTask->ReExecToSelf)
2724  {
2725  return;
2726  }
2727 
2728  // Ignore kthreads
2729  if (ParentTask->KernelMode)
2730  {
2731  return;
2732  }
2733 
2734  if (ObjectType == introObjectTypeProcessCreationDpi)
2735  {
2736  originator.PcType = IntLixTaskGetDpiViolationFlags(ChildTask);
2737 
2738  if (originator.PcType == 0)
2739  {
2740  *Action = introGuestAllowed;
2741  *Reason = introReasonAllowed;
2742 
2743  return;
2744  }
2745  }
2746 
2747  status = IntExceptUserGetOriginator(ChildTask, FALSE, 0, NULL, &originator);
2748  if (!INT_SUCCESS(status))
2749  {
2750  ERROR("[ERROR] IntExceptUserGetOriginator failed with status: 0x%08x.\n", status);
2751  *Action = introGuestNotAllowed;
2752  *Reason = introReasonInternalError;
2753  return;
2754  }
2755 
2756  status = IntExceptGetVictimProcessCreation(ParentTask, ObjectType, &victim);
2757  if (!INT_SUCCESS(status))
2758  {
2759  ERROR("[ERROR] IntExceptGetVictimProcessCreation failed with status: 0x%08x.\n", status);
2760  *Action = introGuestNotAllowed;
2761  *Reason = introReasonInternalError;
2762  return;
2763  }
2764 
2765  IntExcept(&victim, &originator, exceptionTypeUm, Action, Reason, introEventProcessCreationViolation);
2766 
2767  if (ObjectType == introObjectTypeProcessCreation)
2768  {
2769  if (IntPolicyProcTakeAction(PROC_OPT_PROT_PREVENT_CHILD_CREATION, ParentTask, Action, Reason))
2770  {
2771  LOG("[PROCESS CREATION] Process creation blocked. Process `%s` tried to start using process `%s`",
2772  ChildTask->Comm, ParentTask->Comm);
2773 
2774  IntLixTaskSendBlockedEvent(ParentTask, ChildTask, *Action, *Reason, originator.PcType);
2775 
2777  }
2778  }
2779  else
2780  {
2781  if (IntPolicyCoreTakeAction(INTRO_OPT_PROT_DPI, Action, Reason))
2782  {
2783  LOG("[PROCESS CREATION] Process creation blocked. Process `%s` tried to start with DPI using process `%s`",
2784  ChildTask->Comm, ParentTask->Comm);
2785 
2786  IntLixTaskSendBlockedEvent(ParentTask, ChildTask, *Action, *Reason, originator.PcType);
2787 
2789  }
2790  }
2791 }
2792 
2793 
2794 INTSTATUS
2796  _In_ LIX_TASK_OBJECT *Task,
2797  _In_ QWORD Ptr,
2798  _Out_ BOOLEAN *IsPivoted
2799  )
2813 {
2814  INTSTATUS status;
2815  QWORD base, limit;
2816 
2817  if (NULL == Task || Task->KernelMode)
2818  {
2820  }
2821 
2822  if (IS_KERNEL_POINTER_LIX(Ptr))
2823  {
2825  }
2826 
2827  if (NULL == IsPivoted)
2828  {
2830  }
2831 
2832  if (Task->UserStack.Valid)
2833  {
2834  if (__likely(IN_RANGE_INCLUSIVE(Ptr, Task->UserStack.Limit, Task->UserStack.Base)))
2835  {
2836  *IsPivoted = FALSE;
2837 
2838  return INT_STATUS_SUCCESS;
2839  }
2840  }
2841 
2842  if (!Task->IsThread)
2843  {
2844  if (__unlikely(0 == Task->MmGva))
2845  {
2846  WARNING("[WARNING] Parent task %s (%d 0x%llx) is not a thread and doesn't have a valid mm pointer!\n",
2847  Task->Comm, Task->Pid, Task->Gva);
2848 
2849  goto _check_altstack;
2850  }
2851 
2852  status = IntKernVirtMemFetchQword(Task->MmGva + LIX_FIELD(MmStruct, StartStack), &base);
2853  if (!INT_SUCCESS(status))
2854  {
2855  ERROR("[ERROR] Failed to read start_stack from mm. Task %s (%d 0x%llx), MmGva 0x%llx, status: 0x%08x\n",
2856  Task->Comm, Task->Pid, Task->Gva, Task->MmGva, status);
2857  return status;
2858  }
2859 
2860  status = IntLixMmFindVmaRange(base, Task, &limit, &base);
2861  if (!INT_SUCCESS(status))
2862  {
2863  ERROR("[ERROR] Failed to find VAD for task->mm.start_stack(0x%llx) for process %s (%d 0x%llx). "
2864  "Status: 0x%08x\\n", base, Task->Comm, Task->Pid, Task->Gva, status);
2865  return status;
2866  }
2867 
2868  if (__likely(IN_RANGE_INCLUSIVE(Ptr, limit, base)))
2869  {
2870  *IsPivoted = FALSE;
2871  return INT_STATUS_SUCCESS;
2872  }
2873  }
2874 
2875 _check_altstack:
2876  status = IntKernVirtMemFetchQword(Task->Gva + LIX_FIELD(TaskStruct, AltStackSp), &base);
2877  if (!INT_SUCCESS(status))
2878  {
2879  ERROR("[ERROR] Failed to read alt stack. Task %s (%d 0x%llx), status: 0x%08x\n",
2880  Task->Comm, Task->Pid, Task->Gva, status);
2881 
2882  return status;
2883  }
2884 
2885  if (0 != base)
2886  {
2887  status = IntLixMmFindVmaRange(base, Task, &limit, &base);
2888  if (!INT_SUCCESS(status))
2889  {
2890  ERROR("[ERROR] Failed to find alt stack (0x%llx) VAD for process %s (%d 0x%llx). Status: 0x%08x\n",
2891  base, Task->Comm, Task->Pid, Task->Gva, status);
2892  return status;
2893  }
2894 
2895  if (IN_RANGE_INCLUSIVE(Ptr, limit, base))
2896  {
2897  *IsPivoted = FALSE;
2898  return INT_STATUS_SUCCESS;
2899  }
2900  }
2901 
2902  // NOTE: Should we set this right after the check_altstack label?
2903  *IsPivoted = TRUE;
2904 
2905  return INT_STATUS_SUCCESS;
2906 }
2907 
2908 
2909 static void
2911  _In_ LIX_TASK_OBJECT *ParentTask,
2912  _In_ LIX_TASK_OBJECT *CurrentTask
2913  )
2920 {
2921  INTSTATUS status;
2922 
2923  QWORD stackPointer;
2924 
2925  if (ParentTask->KernelMode)
2926  {
2927  return;
2928  }
2929 
2930  status = IntLixTaskGetUserStack(ParentTask, &stackPointer, NULL, NULL);
2931  if (!INT_SUCCESS(status))
2932  {
2933  ERROR("[ERROR] Failed to get user mode stack pointer for parent task %s (%d 0x%llx). Status: 0x%08x\n",
2934  ParentTask->Comm, ParentTask->Pid, ParentTask->Gva, status);
2935  return;
2936  }
2937 
2938  status = IntLixTaskIsUserStackPivoted(ParentTask, stackPointer, &CurrentTask->Dpi.IsPivoted);
2939  if (!INT_SUCCESS(status))
2940  {
2941  ERROR("[ERROR] IntLixTaskIsStackPivoted failed for stack ptr 0x%llx with status 0x%08x.", stackPointer, status);
2942  }
2943 }
2944 
2945 
2946 INTSTATUS
2948  _In_ void *Detour
2949  )
2958 {
2959  INTSTATUS status;
2960  QWORD binprm, dPathResult;
2961  BOOLEAN lastAgent;
2962  LIX_TASK_OBJECT *pTask;
2963  LIX_TASK_OBJECT *pOldTask;
2966  QWORD oldProtectionMask;
2967  static DWORD taskCount = 0;
2968 
2969  UNREFERENCED_PARAMETER(Detour);
2970 
2971  pOldTask = IntLixTaskFindByGva(gVcpu->Regs.R8);
2972  if (NULL == pOldTask)
2973  {
2974  ERROR("[ERROR] No task on for exec!\n");
2976  }
2977 
2978  lastAgent = FALSE;
2979  binprm = gVcpu->Regs.R9;
2980  dPathResult = gVcpu->Regs.R10;
2981 
2982  // Keep the old protection mask in order to validate the process creation rights
2983  oldProtectionMask = pOldTask->Protection.Mask;
2984 
2985  // It's certain that the CR3 will change, so disable the protection (doing a full cleanup).
2986  // It will be activated again after we get the new CR3 (if it's still a protected process)
2988 
2989  // no point in keeping it anymore
2990  pOldTask->IsPreviousAgent = FALSE;
2991 
2992  pTask = HpAllocWithTag(sizeof(LIX_TASK_OBJECT), IC_TAG_POBJ);
2993  if (NULL == pTask)
2994  {
2996  }
2997 
2998  status = IntLixTaskCreateFromBinprm(pOldTask, binprm, dPathResult, pTask);
2999  if (!INT_SUCCESS(status))
3000  {
3001  ERROR("[ERROR] Failed updating process contents from the linux_binprm @ %16llx: 0x%08x\n",
3002  binprm, status);
3003  // Add it to the list, still...
3004  }
3005 
3006  // Only now set the comm to whatever we have. Let's hope the #IntLixTaskUpdateFromBinprm didn't fail to
3007  // read the name/path. In that case, there is nothing we can do.
3008  if (pTask->Path)
3009  {
3010  strlcpy(pTask->Comm, pTask->Path->Name, sizeof(pTask->Comm));
3011 
3012  pTask->CommHash = Crc32String(pTask->Comm, INITIAL_CRC_VALUE);
3013  }
3014  else
3015  {
3016  ERROR("[ERROR] We couldn't get path for process, the comm will be the old one!\n");
3017  }
3018 
3019  // Only update here! We check this flag when we update from the binprm.
3020  pTask->Exec = TRUE;
3021 
3022  // Check if this is the init process, and save the new pointer to it.
3023  if (__unlikely((NULL == gLixGuest->InitProcessObj && pTask->Pid == 1) ||
3024  gLixGuest->InitProcessObj == pOldTask))
3025  {
3026  if (gLixGuest->InitProcessObj == pOldTask)
3027  {
3028  TRACE("[INIT] Init process re-executes itself\n");
3029  }
3030 
3031  gLixGuest->InitProcessObj = pTask;
3032  }
3033 
3034  IntLixCredsVerify(pOldTask);
3035 
3037  {
3038  IntLixValidateExecStack(pOldTask, pTask);
3039  }
3040 
3042  {
3043  IntLixValidateProcessCreationRights(pTask, pOldTask, introObjectTypeProcessCreationDpi, &action, &reason);
3044 
3045  if (action == introGuestNotAllowed)
3046  {
3047  goto _action_not_allowed;
3048  }
3049  }
3050 
3051  if (oldProtectionMask & PROC_OPT_PROT_PREVENT_CHILD_CREATION)
3052  {
3053  IntLixValidateProcessCreationRights(pTask, pOldTask, introObjectTypeProcessCreation, &action, &reason);
3054  }
3055 
3056 _action_not_allowed:
3057  if (action == introGuestNotAllowed)
3058  {
3059  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, (QWORD) - EACCES);
3060  if (!INT_SUCCESS(status))
3061  {
3062  ERROR("[ERROR] IntDetSetReturnValue failed: %08x\n", status);
3063  }
3064 
3065  IntLixTaskRemoveEntry(pTask);
3066 
3067  return INT_STATUS_SUCCESS;
3068  }
3069 
3070 
3071  InsertAfterList(&pOldTask->Link, &pTask->Link);
3072  RemoveEntryList(&pOldTask->Link);
3073 
3074  // Now it's safe to reactivate the protection. The new CR3 is in place.
3075  // We also don't depend on the parent anymore.
3076  status = IntLixTaskActivateProtection(pTask, NULL);
3077  if (!INT_SUCCESS(status))
3078  {
3079  ERROR("[ERROR] IntLixTaskActivateProtection failed for %s: 0x%08x\n", pTask->Comm, status);
3080 
3082 
3083  pTask->Protected = FALSE;
3084  }
3085 
3087  {
3088  status = IntLixTaskFetchCmdLine(pTask, binprm);
3089  if (!INT_SUCCESS(status))
3090  {
3091  ERROR("[ERROR] IntLixTaskFetchCmdLine failed with status: 0%08x\n", status);
3092  }
3093  else
3094  {
3095  status = IntLixCmdLineInspect(pTask);
3096  if (!INT_SUCCESS(status))
3097  {
3098  ERROR("[ERROR] IntLixCmdLineInspect failed with status: 0%08x\n", status);
3099  }
3100  }
3101  }
3102 
3103  if (IntLixTaskMustLog(pTask, pTask->Protected != 0))
3104  {
3105  if (pTask->Interpreter != NULL)
3106  {
3107  LOG("[EXEC] %s %s (%d, %llx, %llx) exec to %s (interp: %s)\n",
3108  pOldTask->IsThread ? "Thread" : "Process", pOldTask->Comm, pTask->Pid, pTask->Cr3, pTask->Gva,
3109  pTask->ProcName, pTask->Interpreter);
3110  }
3111  else
3112  {
3113  LOG("[EXEC] %s %s (%d, %llx, %llx) exec to %s\n",
3114  pOldTask->IsThread ? "Thread" : "Process", pOldTask->Comm, pTask->Pid, pTask->Cr3,
3115  pTask->Gva, pTask->ProcName);
3116  }
3117 
3118  if (pTask->CmdLine)
3119  {
3120  LOG("[EXEC] Task '%s' has command line '%s'\n", pTask->ProcName, pTask->CmdLine);
3121  }
3122  }
3123 
3124  if (pOldTask->AgentTag)
3125  {
3126  size_t oldLen = strlen_s(pTask->Comm, sizeof(pTask->Comm));
3127  size_t newLen = strlen_s(pOldTask->Comm, sizeof(pOldTask->Comm));
3128 
3129  // If it changed the name, then we need to decrement the old agent refcount
3130  // If it didn't change the name, then we must leave it marked as agent (and don't decrement!)
3131  if ((oldLen != newLen) || (0 != memcmp(pOldTask->Comm, pTask->Comm, oldLen)))
3132  {
3133  pTask->AgentTag = IntLixAgentDecProcRef(pOldTask->Comm, &lastAgent);
3134  }
3135  }
3136  else
3137  {
3138  // What if it executed to a new agent !? Simple, we mark this as an agent too...
3139  pTask->AgentTag = IntLixAgentIncProcRef(pTask->Comm);
3140  }
3141 
3142  if (lastAgent)
3143  {
3144  IntLixTaskSendAgentEvent(pTask, 0, FALSE);
3145  }
3146  else if (!pOldTask->AgentTag && pTask->AgentTag)
3147  {
3148  IntLixTaskSendAgentEvent(pTask, 0, TRUE);
3149  }
3150 
3151  IntLixTaskSendTaskEvent(pTask, 0, TRUE, FALSE, FALSE);
3152 
3153  IntLixTaskRemoveEntry(pOldTask);
3154 
3155  if (taskCount == 1)
3156  {
3158  {
3159  status = IntLixVdsoDynamicProtect();
3160  if (!INT_SUCCESS(status))
3161  {
3162  ERROR("[ERROR] IntLixVdsoDynamicProtect failed with status: 0x%08x.", status);
3163  }
3164  }
3165  }
3166  taskCount++;
3167 
3168  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, 0);
3169  if (!INT_SUCCESS(status))
3170  {
3171  ERROR("[ERROR] IntDetSetReturnValue failed: %08x\n", status);
3172  }
3173 
3174  return INT_STATUS_SUCCESS;
3175 }
3176 
3177 
3178 INTSTATUS
3180  _In_ void *Detour
3181  )
3189 {
3190  UNREFERENCED_PARAMETER(Detour);
3191 
3192  INTSTATUS status = IntLixTaskAdd(gVcpu->Regs.R9, FALSE);
3193  if (!INT_SUCCESS(status))
3194  {
3195  ERROR("[ERROR] IntLixTaskAdd failed for %llx on cpu %d: 0x%08x\n",
3196  gVcpu->Regs.R9, gVcpu->Index, status);
3197  }
3198 
3199  return INT_STATUS_SUCCESS;
3200 }
3201 
3202 
3203 static void
3205  _In_ LIX_TASK_OBJECT *Source,
3206  _In_ LIX_TASK_OBJECT *Victim,
3207  _In_ INTRO_ACTION Action,
3208  _In_ INTRO_ACTION_REASON Reason
3209  )
3218 {
3219  INTSTATUS status;
3220  EVENT_MEMCOPY_VIOLATION *pInjEvent;
3221 
3222  pInjEvent = &gAlert.Injection;
3223 
3224  memzero(pInjEvent, sizeof(*pInjEvent));
3225 
3226  pInjEvent->Header.Action = Action;
3227  pInjEvent->Header.Reason = Reason;
3228  pInjEvent->Header.MitreID = idProcInject;
3229 
3231 
3232  pInjEvent->DestinationVirtualAddress = 0;
3233  pInjEvent->SourceVirtualAddress = 0;
3234 
3235  IntAlertFillLixProcess(Source, &pInjEvent->Originator.Process);
3236  IntAlertFillLixProcess(Victim, &pInjEvent->Victim.Process);
3237 
3239 
3240  pInjEvent->Header.Flags = IntAlertProcGetFlags(PROC_OPT_PROT_WRITE_MEM, Victim, Reason, 0);
3241 
3242  pInjEvent->Header.Flags |= ALERT_FLAG_LINUX;
3243  pInjEvent->Header.Flags |= ALERT_FLAG_NOT_RING0;
3244 
3245  IntAlertFillVersionInfo(&pInjEvent->Header);
3246 
3247  status = IntNotifyIntroEvent(introEventInjectionViolation, pInjEvent, sizeof(*pInjEvent));
3248  if (!INT_SUCCESS(status))
3249  {
3250  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
3251  }
3252 }
3253 
3254 
3255 static INTSTATUS
3257  _In_ QWORD Victim,
3258  _In_ BOOLEAN Pid,
3259  _In_ QWORD InjectionFlag,
3260  _Out_ BOOLEAN *Block
3261  )
3272 {
3273  INTSTATUS status;
3274  QWORD currentTask;
3275  LIX_TASK_OBJECT *pSource, *pVictim;
3276  INTRO_ACTION action;
3277  INTRO_ACTION_REASON reason;
3278  EXCEPTION_UM_ORIGINATOR originator;
3279  EXCEPTION_VICTIM_ZONE victim;
3280 
3281  *Block = FALSE;
3282  pSource = pVictim = NULL;
3283 
3284  status = IntLixTaskGetCurrentTaskStruct(gVcpu->Index, &currentTask);
3285  if (!INT_SUCCESS(status))
3286  {
3287  ERROR("[ERROR] IntLixTaskGetCurrentStruct failed: 0x%08x\n", status);
3288  return status;
3289  }
3290 
3291  for_each_task(pProc)
3292  {
3293  if ((Pid && pProc->Pid == (DWORD)Victim) || (!Pid && pProc->Gva == Victim))
3294  {
3295  pVictim = pProc;
3296  }
3297 
3298  if (pProc->Gva == currentTask)
3299  {
3300  pSource = pProc;
3301  }
3302 
3303  if ((NULL != pVictim) && (NULL != pSource))
3304  {
3305  break;
3306  }
3307  }
3308 
3309  if (NULL == pVictim || NULL == pSource)
3310  {
3311  // We don't know these processes
3312  return INT_STATUS_SUCCESS;
3313  }
3314 
3315  if (!(pVictim->Protection.Mask & InjectionFlag))
3316  {
3317  LOG("[PTRACE] Injection from %s (%d, %llx) into %s (%d, %llx)\n",
3318  pSource->ProcName, pSource->Pid, pSource->Gva,
3319  pVictim->ProcName, pVictim->Pid, pVictim->Gva);
3320  return INT_STATUS_SUCCESS;
3321  }
3322 
3324 
3325  // from now on, block by default
3326  action = introGuestNotAllowed;
3327  reason = introReasonUnknown;
3328 
3329  memzero(&originator, sizeof(originator));
3330  memzero(&victim, sizeof(victim));
3331 
3332  status = IntExceptUserGetOriginator(pSource, FALSE, 0, NULL, &originator);
3333  if (!INT_SUCCESS(status))
3334  {
3335  reason = introReasonInternalError;
3336  action = introGuestNotAllowed;
3337 
3338  ERROR("[ERROR] Failed getting originator: 0x%08x\n", status);
3339  goto _send_notification;
3340  }
3341 
3342  status = IntExceptGetVictimProcess(pVictim, 0, 0, ZONE_WRITE, &victim);
3343 
3344  if (!INT_SUCCESS(status))
3345  {
3346  reason = introReasonInternalError;
3347  action = introGuestNotAllowed;
3348 
3349  ERROR("[ERROR] Failed getting modified zone: 0x%08x\n", status);
3350  goto _send_notification;
3351  }
3352 
3353  IntExcept(&victim, &originator, exceptionTypeUm, &action, &reason, introEventInjectionViolation);
3354 
3355 _send_notification:
3357 
3358  if (IntPolicyProcTakeAction(PROC_OPT_PROT_WRITE_MEM, pVictim, &action, &reason))
3359  {
3360  LOG("[INJECTION] Block injection from process %s (%d) into process %s (%d)\n",
3361  pSource->Comm, pSource->Pid, pVictim->Comm, pVictim->Pid);
3362 
3364  }
3365  else
3366  {
3367  LOG("[INJECTION] Allow injection from process %s into process %s\n",
3368  pSource->Comm, pVictim->Comm);
3369  }
3370 
3371 
3373 
3374  *Block = action == introGuestNotAllowed;
3375 
3376  return INT_STATUS_SUCCESS;
3377 }
3378 
3379 
3380 INTSTATUS
3382  _In_ void *Detour
3383  )
3388 //
3397 {
3398  INTSTATUS status;
3399 
3400  BOOLEAN block = FALSE;
3401 
3402  DWORD pid = (DWORD)gVcpu->Regs.R9;
3403 
3404  status = IntLixTaskHandleInjection(pid, TRUE, PROC_OPT_PROT_WRITE_MEM, &block);
3405  if (!INT_SUCCESS(status))
3406  {
3407  ERROR("[ERROR] IntLixTaskHandleInjection failed: %08x\n", status);
3408  goto _emulate_and_leave;
3409  }
3410 
3411 _emulate_and_leave:
3412  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, block ? -EACCES : 0);
3413  if (!INT_SUCCESS(status))
3414  {
3415  ERROR("[ERROR] IntDetoursGstSetReturnValue failed: 0x%08x\n", status);
3416  }
3417 
3418  return INT_STATUS_SUCCESS;
3419 }
3420 
3421 
3422 INTSTATUS
3424  _In_ void *Detour
3425  )
3435 {
3436  INTSTATUS status;
3437 
3438  BOOLEAN block = FALSE;
3439 
3440  QWORD child = gVcpu->Regs.R8;
3441  QWORD request = gVcpu->Regs.R9;
3442 
3443  if (PTRACE_POKEDATA == request || PTRACE_POKETEXT == request)
3444  {
3445  status = IntLixTaskHandleInjection(child, FALSE, PROC_OPT_PROT_WRITE_MEM, &block);
3446  if (!INT_SUCCESS(status))
3447  {
3448  ERROR("[ERROR] IntLixTaskHandleInjection failed: %08x\n", status);
3449  goto _emulate_and_leave;
3450  }
3451  }
3452  else if (PTRACE_SETFPREGS == request || PTRACE_SETFPXREGS == request || PTRACE_SETREGS == request)
3453  {
3454  status = IntLixTaskHandleInjection(child, FALSE, PROC_OPT_PROT_PTRACE, &block);
3455  if (!INT_SUCCESS(status))
3456  {
3457  ERROR("[ERROR] IntLixTaskHandleSetRegs failed: %08x\n", status);
3458  goto _emulate_and_leave;
3459  }
3460  }
3461  else
3462  {
3463  WARNING("[WARNING] The request argument (%llx) for 'ptrace' is allowed ...\n", request);
3464  block = FALSE;
3465  goto _emulate_and_leave;
3466  }
3467 
3468 _emulate_and_leave:
3469 
3470  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, block ? -EACCES : 0);
3471  if (!INT_SUCCESS(status))
3472  {
3473  ERROR("[ERROR] IntDetoursGstSetReturnValue failed: 0x%08x\n", status);
3474  }
3475 
3476  return INT_STATUS_SUCCESS;
3477 }
3478 
3479 
3480 INTSTATUS
3482  _In_ void *Detour
3483  )
3491 {
3492  UNREFERENCED_PARAMETER(Detour);
3493 
3495  if (NULL == pTask)
3496  {
3497  TRACE("[INFO] Task dying without being in the list... Ignore it!");
3499  }
3500 
3501  if (__unlikely(pTask == gLixGuest->InitProcessObj))
3502  {
3503  LOG("[ERROR] Init task is exiting, something isn't right...\n");
3504  }
3505 
3506  IntLixTaskDestroy(pTask, (DWORD)gVcpu->Regs.R9);
3507 
3508  return INT_STATUS_SUCCESS;
3509 }
3510 
3511 
3512 static INTSTATUS
3514  _In_ QWORD TaskStructGva,
3516  _In_ QWORD Aux
3517  )
3530 
3531 {
3532  INTSTATUS status;
3533  QWORD currentThread;
3534  QWORD signal = 0;
3535  QWORD signalListHead = 0;
3536  int nrThreads = 0;
3537  DWORD count = 0;
3538 
3539  if (gLixGuest->Version.Version >= 3)
3540  {
3541  status = IntKernVirtMemFetchQword(TaskStructGva + LIX_FIELD(TaskStruct, Signal), &signal);
3542  if (!INT_SUCCESS(status))
3543  {
3544  ERROR("[ERROR] Failed reading the signal struct: 0x%08x\n", status);
3545  return status;
3546  }
3547 
3548  if (!IS_KERNEL_POINTER_LIX(signal))
3549  {
3550  ERROR("[ERROR] task->signal value (0x%llx) does not point to a valid kernel memory location.", signal);
3552  }
3553 
3554  status = IntKernVirtMemFetchDword(signal + LIX_FIELD(Ungrouped, SignalNrThreads), (DWORD *)&nrThreads);
3555  if (!INT_SUCCESS(status))
3556  {
3557  ERROR("[ERROR] Failed reading from the signal struct: 0x%08x\n", status);
3558  return status;
3559  }
3560 
3561  // We must subtract the entry that's TaskStructGva
3562  nrThreads--;
3563 
3564  // Only one thread in this signal struct, so skip it
3565  if (nrThreads == 0)
3566  {
3568  }
3569  else if (nrThreads < 0)
3570  {
3571  ERROR("[ERROR] Negative number of threads: %d\n", nrThreads);
3573  }
3574  }
3575 
3576  signalListHead = signal + LIX_FIELD(Ungrouped, SignalListHead);
3577 
3578  status = IntKernVirtMemFetchQword(signalListHead, &currentThread);
3579  if (!INT_SUCCESS(status))
3580  {
3581  ERROR("[ERROR] Failed getting the first task from signal 0x%016llx\n", signalListHead);
3582  return status;
3583  }
3584 
3585  currentThread -= LIX_FIELD(TaskStruct, ThreadNode);
3586 
3587  // This one was already added, so skip it and go to the next one
3588  status = IntKernVirtMemFetchQword(currentThread + LIX_FIELD(TaskStruct, ThreadNode), &currentThread);
3589  if (!INT_SUCCESS(status))
3590  {
3591  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3592  currentThread + LIX_FIELD(TaskStruct, ThreadNode));
3593  return status;
3594  }
3595 
3596  while ((currentThread != signalListHead) && (count++ < LIX_PROCESSES_MAX_COUNT))
3597  {
3598  if (!IS_KERNEL_POINTER_LIX(currentThread))
3599  {
3600  ERROR("[ERROR] Thread 0x%llx does not point to a valid kernel memory location.\n", currentThread);
3602  }
3603 
3604  currentThread -= LIX_FIELD(TaskStruct, ThreadNode);
3605 
3606  status = Callback(currentThread, Aux);
3607  if (!INT_SUCCESS(status))
3608  {
3609  return status;
3610  }
3611  else if (INT_STATUS_BREAK_ITERATION == status)
3612  {
3613  return INT_STATUS_SUCCESS;
3614  }
3615 
3616  nrThreads--;
3617 
3618  status = IntKernVirtMemFetchQword(currentThread + LIX_FIELD(TaskStruct, ThreadNode), &currentThread);
3619  if (!INT_SUCCESS(status))
3620  {
3621  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3622  currentThread + LIX_FIELD(TaskStruct, ThreadNode));
3623  break;
3624  }
3625  }
3626 
3627  if (gLixGuest->Version.Version >= 3)
3628  {
3629  if (nrThreads > 0)
3630  {
3631  ERROR("[ERROR] We didn't processed enough threads. Remaining: %d\n", nrThreads);
3633  }
3634  else if (nrThreads < 0)
3635  {
3636  ERROR("[ERROR] We processed more threads. Over: %d\n", nrThreads);
3638  }
3639  }
3640 
3641  return INT_STATUS_SUCCESS;
3642 }
3643 
3644 
3645 static INTSTATUS
3647  _In_ QWORD TaskStructGva,
3649  _In_ QWORD Aux
3650  )
3663 {
3664  INTSTATUS status;
3665  QWORD currentThread;
3666  QWORD signal, threadListHead;
3667  DWORD count = 0;
3668  int nrThreads = 0;
3669 
3670  if (gLixGuest->Version.Version >= 3)
3671  {
3672  status = IntKernVirtMemFetchQword(TaskStructGva + LIX_FIELD(TaskStruct, Signal), &signal);
3673  if (!INT_SUCCESS(status))
3674  {
3675  ERROR("[ERROR] Failed reading the signal struct: 0x%08x\n", status);
3676  return status;
3677  }
3678 
3679  if (!IS_KERNEL_POINTER_LIX(signal))
3680  {
3681  ERROR("[ERROR] task->signal value (0x%llx) does not point to a valid kernel memory location.", signal);
3683  }
3684 
3685  status = IntKernVirtMemFetchDword(signal + LIX_FIELD(Ungrouped, SignalNrThreads), (DWORD *)&nrThreads);
3686  if (!INT_SUCCESS(status))
3687  {
3688  ERROR("[ERROR] Failed reading from the signal struct: 0x%08x\n", status);
3689  return status;
3690  }
3691 
3692  // We must subtract the entry that's TaskStructGva
3693  nrThreads--;
3694 
3695  // Only one thread in this signal struct, so skip it
3696  if (nrThreads == 0)
3697  {
3699  }
3700  else if (nrThreads < 0)
3701  {
3702  ERROR("[ERROR] Negative number of threads: %d\n", nrThreads);
3704  }
3705  }
3706 
3707  threadListHead = TaskStructGva + LIX_FIELD(TaskStruct, ThreadGroup);
3708 
3709  status = IntKernVirtMemFetchQword(threadListHead, &currentThread);
3710  if (!INT_SUCCESS(status))
3711  {
3712  ERROR("[ERROR] Failed getting the first task from signal 0x%016llx\n", threadListHead);
3713  return status;
3714  }
3715 
3716  while (currentThread && (currentThread != threadListHead) && (count++ < LIX_PROCESSES_MAX_COUNT))
3717  {
3718  if (!IS_KERNEL_POINTER_LIX(currentThread))
3719  {
3720  ERROR("[ERROR] Thread 0x%llx does not point to a valid kernel memory location.\n", currentThread);
3722  }
3723 
3724  currentThread -= LIX_FIELD(TaskStruct, ThreadGroup);
3725 
3726  status = Callback(currentThread, Aux);
3727  if (!INT_SUCCESS(status))
3728  {
3729  return status;
3730  }
3731  else if (INT_STATUS_BREAK_ITERATION == status)
3732  {
3733  return INT_STATUS_SUCCESS;
3734  }
3735 
3736  nrThreads--;
3737 
3738  status = IntKernVirtMemFetchQword(currentThread + LIX_FIELD(TaskStruct, ThreadGroup), &currentThread);
3739  if (!INT_SUCCESS(status))
3740  {
3741  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3742  currentThread + LIX_FIELD(TaskStruct, ThreadGroup));
3743  break;
3744  }
3745  }
3746 
3747  if (gLixGuest->Version.Version >= 3)
3748  {
3749  if (nrThreads > 0)
3750  {
3751  ERROR("[ERROR] We didn't processed enough threads. Remaining: %d\n", nrThreads);
3753  }
3754  else if (nrThreads < 0)
3755  {
3756  ERROR("[ERROR] We processed more threads. Over: %d\n", nrThreads);
3758  }
3759  }
3760 
3761  return INT_STATUS_SUCCESS;
3762 }
3763 
3764 
3765 static INTSTATUS
3767  _In_ QWORD TaskStructGva,
3769  _In_ QWORD Aux
3770  )
3781 {
3782  if (0 != LIX_FIELD(TaskStruct, ThreadGroup))
3783  {
3784  return IntLixTaskIterateThreadGroup(TaskStructGva, Callback, Aux);
3785  }
3786  else if (0 != LIX_FIELD(TaskStruct, ThreadNode))
3787  {
3788  return IntLixTaskIterateThreadNode(TaskStructGva, Callback, Aux);
3789  }
3790  else
3791  {
3792  // WARNING("[WARNING] IntLixTaskIterateThreads while not a single process was created!\n");
3794  }
3795 }
3796 
3797 
3798 INTSTATUS
3801  _In_ QWORD Aux
3802  )
3812 {
3813  INTSTATUS status;
3814  QWORD initGva, currentTask;
3815  DWORD count = 0;
3816 
3817  status = IntLixGetInitTask(&initGva);
3818  if (!INT_SUCCESS(status))
3819  {
3820  ERROR("[ERROR] Failed finding the init_task: 0x%08x\n", status);
3821  return status;
3822  }
3823 
3824  //
3825  // IMPORTANT: We must call the callback for the init_task too!
3826  //
3827  status = Callback(initGva, Aux);
3828  if (!INT_SUCCESS(status))
3829  {
3830  return status;
3831  }
3832  else if (INT_STATUS_BREAK_ITERATION == status)
3833  {
3834  return INT_STATUS_SUCCESS;
3835  }
3836 
3837  status = IntLixTaskIterateThreads(initGva, Callback, Aux);
3838  if (!INT_SUCCESS(status))
3839  {
3840  return status;
3841  }
3842 
3843  status = IntKernVirtMemFetchQword(initGva + LIX_FIELD(TaskStruct, Tasks), &currentTask);
3844  if (!INT_SUCCESS(status))
3845  {
3846  ERROR("[ERROR] Failed getting the first task from 0x%016llx\n", initGva + LIX_FIELD(TaskStruct, Tasks));
3847  return status;
3848  }
3849 
3850  currentTask -= LIX_FIELD(TaskStruct, Tasks);
3851 
3852  while (currentTask != initGva && (count++ < LIX_PROCESSES_MAX_COUNT))
3853  {
3854  if (!IS_KERNEL_POINTER_LIX(currentTask))
3855  {
3856  ERROR("[ERROR] task_struct 0x%llx does not point to a valid kernel memory location.\n", currentTask);
3858  }
3859 
3860  status = Callback(currentTask, Aux);
3861  if (!INT_SUCCESS(status))
3862  {
3863  return status;
3864  }
3865  else if (INT_STATUS_BREAK_ITERATION == status)
3866  {
3867  return INT_STATUS_SUCCESS;
3868  }
3869 
3870  IntLixTaskIterateThreads(currentTask, Callback, Aux);
3871 
3872  status = IntKernVirtMemFetchQword(currentTask + LIX_FIELD(TaskStruct, Tasks), &currentTask);
3873  if (!INT_SUCCESS(status))
3874  {
3875  ERROR("[ERROR] Failed getting the next task from 0x%016llx\n",
3876  currentTask + LIX_FIELD(TaskStruct, Tasks));
3877  break;
3878  }
3879 
3880  currentTask -= LIX_FIELD(TaskStruct, Tasks);
3881  }
3882 
3883  if (count >= LIX_PROCESSES_MAX_COUNT)
3884  {
3885  return INT_STATUS_NOT_SUPPORTED;
3886  }
3887 
3888  return INT_STATUS_SUCCESS;
3889 }
3890 
3891 
3892 static INTSTATUS
3894  _In_ QWORD TaskGva,
3895  _Out_opt_ LIX_TASK_OBJECT **Task
3896  )
3906 {
3907  INTSTATUS status;
3908  LIX_TASK_OBJECT *pInitTask;
3909 
3910  pInitTask = HpAllocWithTag(sizeof(*pInitTask), IC_TAG_POBJ);
3911  if (NULL == pInitTask)
3912  {
3914  }
3915 
3916  pInitTask->Gva = TaskGva;
3917  TRACE("[LIXTASK] Init task @ 0x%016llx\n", pInitTask->Gva);
3918 
3919  // Linux convention...
3920  pInitTask->Parent = TaskGva;
3921  pInitTask->RealParent = TaskGva;
3922 
3923  pInitTask->Path = NULL;
3924  pInitTask->Interpreter = NULL;
3925  pInitTask->IsThread = TRUE;
3926  pInitTask->KernelMode = TRUE;
3927  pInitTask->Protection.Mask = 0;
3928  pInitTask->Protected = FALSE;
3929  pInitTask->Context = 0;
3930 
3931  status = IntKernVirtMemFetchDword(pInitTask->Gva + LIX_FIELD(TaskStruct, Pid), &pInitTask->Pid);
3932  if (!INT_SUCCESS(status))
3933  {
3934  ERROR("[ERROR] Failed getting PID of the init process @0x%016llx: 0x%08x\n",
3935  pInitTask->Gva, status);
3936  }
3937 
3938  status = IntKernVirtMemRead(pInitTask->Gva + LIX_FIELD(TaskStruct, Comm),
3939  sizeof(pInitTask->Comm),
3940  pInitTask->Comm,
3941  NULL);
3942 
3943  if (!INT_SUCCESS(status))
3944  {
3945  ERROR("[ERROR] Failed reading init process name @ 0x%016llx: 0x%08x\n",
3946  pInitTask->Gva + LIX_FIELD(TaskStruct, Comm), status);
3947  pInitTask->Comm[0] = 0;
3948  }
3949 
3950  // Make sure the NULL terminator is there.
3951  pInitTask->Comm[sizeof(pInitTask->Comm) - 1] = 0;
3952 
3953  pInitTask->CommHash = Crc32String(pInitTask->Comm, INITIAL_CRC_VALUE);
3954 
3955  if ((0 == LIX_FIELD(TaskStruct, ThreadGroup)) && (0 == LIX_FIELD(TaskStruct, ThreadNode)))
3956  {
3957  QWORD signal, flink;
3958 
3959  status = IntKernVirtMemFetchQword(TaskGva + LIX_FIELD(TaskStruct, Signal), &signal);
3960  if (!INT_SUCCESS(status))
3961  {
3962  ERROR("[ERROR] Failed reading the init's signal struct: 0x%08x\n", status);
3963  return status;
3964  }
3965 
3966  status = IntKernVirtMemFetchQword(signal + LIX_FIELD(Ungrouped, SignalListHead), &flink);
3967  if (!INT_SUCCESS(status))
3968  {
3969  ERROR("[ERROR] Failed reading from init's signal struct: 0x%08x\n", status);
3970  return status;
3971  }
3972 
3973  // Sanity check: 'signal' field is below sizeof(task_struct), which for now we assume that of PAGE_SIZE
3974  if (flink - TaskGva > PAGE_SIZE)
3975  {
3976  ERROR("[ERROR] Signal's struct is not good: 0x%016llx 0x%016llx 0x%016llx\n", flink, signal, TaskGva);
3977  return INT_STATUS_NOT_SUPPORTED;
3978  }
3979 
3980  // Detect dynamically the thread node
3981  LIX_FIELD(TaskStruct, ThreadNode) = (DWORD)(flink - TaskGva);
3982  }
3983 
3984  InsertTailList(&gLixTasks, &pInitTask->Link);
3985 
3986  if (Task)
3987  {
3988  *Task = pInitTask;
3989  }
3990 
3991  return INT_STATUS_SUCCESS;
3992 }
3993 
3994 
3995 INTSTATUS
3997  _In_ QWORD TaskGva,
3998  _In_ QWORD StaticDetected
3999  )
4009 {
4010  INTSTATUS status;
4011 
4012  if (!IS_KERNEL_POINTER_LIX(TaskGva))
4013  {
4015  }
4016 
4017  // This is the first call to this function, so create the init task
4018  if (__unlikely(StaticDetected && IsListEmpty(&gLixTasks)))
4019  {
4020  status = IntLixTaskCreateInitTask(TaskGva, NULL);
4021  if (!INT_SUCCESS(status))
4022  {
4023  ERROR("[ERROR] IntLixTaskCreateInitTask failed: 0x%08x\n", status);
4024  return status;
4025  }
4026  }
4027  else
4028  {
4029  QWORD parentTs, realParentTs;
4030  LIX_TASK_OBJECT *pParent, *pRealParent;
4031 
4032  parentTs = realParentTs = 0;
4033 
4034  status = _IntLixTaskStartMap(TaskGva);
4035  if (!INT_SUCCESS(status))
4036  {
4037  ERROR("[ERROR] _IntLixTaskStartMap failed for %llx: %08x\n", TaskGva, status);
4038  return status;
4039  }
4040 
4041  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, RealParent), sizeof(realParentTs), &realParentTs);
4042  if (!INT_SUCCESS(status))
4043  {
4044  ERROR("[ERROR] Failed getting the real parent: %08x\n", status);
4045  goto _finish_task;
4046  }
4047 
4048  status = _IntLixTaskRead(LIX_FIELD(TaskStruct, Parent), sizeof(parentTs), &parentTs);
4049  if (!INT_SUCCESS(status))
4050  {
4051  ERROR("[ERROR] Failed getting the parent: %08x\n", status);
4052  goto _finish_task;
4053  }
4054 
4055  pParent = IntLixTaskFindByGva(parentTs);
4056  if (NULL == pParent)
4057  {
4058  WARNING("[WARNING] IntLixTaskFindByGva failed for parent 0x%016llx\n", parentTs);
4059 
4060  pParent = IntLixTaskFindByPid(1);
4061  if (NULL == pParent)
4062  {
4063  ERROR("[ERROR] IntLixTaskFindByPid failed for PID 1!\n");
4064  goto _finish_task;
4065  }
4066  }
4067  else
4068  {
4069  IntLixCredsVerify(pParent);
4070  }
4071 
4072  if (parentTs == realParentTs)
4073  {
4074  pRealParent = pParent;
4075  }
4076  else
4077  {
4078  pRealParent = IntLixTaskFindByGva(realParentTs);
4079 
4080  if (NULL == pRealParent)
4081  {
4082  WARNING("[WARNING] IntLixTaskFindByGva failed for real parent 0x%016llx\n", realParentTs);
4083  pRealParent = pParent;
4084  }
4085  else
4086  {
4087  IntLixCredsVerify(pRealParent);
4088  }
4089  }
4090 
4091  status = IntLixTaskCreate(pParent, pRealParent, TaskGva, StaticDetected != 0, NULL);
4092  if (!INT_SUCCESS(status))
4093  {
4094  ERROR("[ERROR] IntLixTaskCreate failed: 0x%08x\n", status);
4095  }
4096 
4097 _finish_task:
4099  }
4100 
4101  return status;
4102 }
4103 
4104 
4105 static INTSTATUS
4107  _In_ LIX_TASK_OBJECT *Task,
4108  _In_ QWORD NewProtection,
4109  _In_ QWORD NewRootProtection,
4110  _In_ QWORD Context
4111  )
4123 {
4124  INTSTATUS status;
4125  QWORD oldProtection;
4126 
4127  oldProtection = Task->Protection.Mask;
4128 
4129  if (NewProtection == oldProtection)
4130  {
4132  }
4133 
4134  if (0 == NewProtection)
4135  {
4136  LOG("[PROT] Removing %s %s (%llx, %llx, %d) from protection", Task->Exec ? "exec process" : "fork process",
4137  Task->Comm, Task->Gva, Task->Cr3, Task->Pid);
4138 
4140 
4141  Task->Protected = FALSE;
4142 
4143  return INT_STATUS_SUCCESS;
4144  }
4145 
4146  LOG("[PROT] Changing protection flags for `%s` (Pid %d, ts 0x%016llx): 0x%llx -> 0x%llx\n",
4147  Task->Comm, Task->Pid, Task->Gva, oldProtection, NewProtection);
4148 
4149  if ((PROC_OPT_PROT_EXPLOIT & NewProtection) != (PROC_OPT_PROT_EXPLOIT & oldProtection))
4150  {
4151  if (0 != (PROC_OPT_PROT_EXPLOIT & NewProtection))
4152  {
4153  LOG("[PROT] PROC_OPT_PROT_EXPLOIT disabled -> enabled for %s (%llx, %d)\n",
4154  Task->Comm, Task->Cr3, Task->Pid);
4155 
4156  Task->Protection.Mask |= PROC_OPT_PROT_EXPLOIT;
4157 
4158  status = IntLixTaskActivateExploitProtection(Task);
4159  if (!INT_SUCCESS(status))
4160  {
4161  WARNING("[WARNING] Process '%s' (%d, %llx, %llx) will not be exploit-protected: %08x!\n",
4162  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
4163 
4164  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
4165  }
4166  }
4167  else
4168  {
4169  LOG("[PROT] PROC_OPT_PROT_EXPLOIT enabled -> disabled for %s (%llx, %d)\n",
4170  Task->Comm, Task->Cr3, Task->Pid);
4171 
4173  if (!INT_SUCCESS(status))
4174  {
4175  ERROR("[ERROR] Process '%s' (%d, %llx, %llx) failed to deactivate protection: %08x\n",
4176  Task->ProcName, Task->Pid, Task->Gva, Task->Cr3, status);
4177  }
4178 
4179  Task->Protection.Mask &= ~PROC_OPT_PROT_EXPLOIT;
4180  }
4181  }
4182 
4183  if ((PROC_OPT_REMEDIATE & NewProtection) != (PROC_OPT_REMEDIATE & oldProtection))
4184  {
4185  LOG("[PROT] PROC_OPT_REMEDIATE %s for process %s, %d\n",
4186  0 != (PROC_OPT_REMEDIATE & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4187  Task->Comm, Task->Pid);
4188  }
4189 
4190  if ((PROC_OPT_KILL_ON_EXPLOIT & NewProtection) != (PROC_OPT_KILL_ON_EXPLOIT & oldProtection))
4191  {
4192  LOG("[PROCESS] PROC_OPT_KILL_ON_EXPLOIT %s for process %s, %d\n",
4193  0 != (PROC_OPT_KILL_ON_EXPLOIT & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4194  Task->Comm, Task->Pid);
4195  }
4196 
4197  if ((PROC_OPT_PROT_WRITE_MEM & NewProtection) != (PROC_OPT_PROT_WRITE_MEM & oldProtection))
4198  {
4199  LOG("[PROCESS] PROC_OPT_PROT_WRITE_MEM %s for process %s, %d\n",
4200  0 != (PROC_OPT_PROT_WRITE_MEM & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4201  Task->Comm, Task->Pid);
4202  }
4203 
4204  if ((PROC_OPT_PROT_PTRACE & NewProtection) != (PROC_OPT_PROT_PTRACE & oldProtection))
4205  {
4206  LOG("[PROCESS] PROC_OPT_PROT_PTRACE %s for process %s, %d\n",
4207  0 != (PROC_OPT_PROT_PTRACE & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4208  Task->Comm, Task->Pid);
4209  }
4210 
4211  if ((PROC_OPT_BETA & NewProtection) != (PROC_OPT_BETA & oldProtection))
4212  {
4213  LOG("[PROCESS] PROC_OPT_BETA %s for process %s, %d\n",
4214  0 != (PROC_OPT_BETA & NewProtection) ? "disabled -> enabled" : "enabled -> disabled",
4215  Task->Comm, Task->Pid);
4216  }
4217 
4218  Task->Protected = TRUE;
4219  Task->Protection.Mask = NewProtection;
4220  Task->RootProtectionMask = NewRootProtection;
4221  Task->Context = Context;
4222 
4223  return INT_STATUS_SUCCESS;
4224 }
4225 
4226 
4227 static INTSTATUS
4229  _In_ const LIX_PROTECTED_PROCESS *ProtProc,
4230  _In_ BOOLEAN Remove
4231  )
4241 {
4243  {
4245  }
4246 
4247  for_each_task(pTask)
4248  {
4249  INTSTATUS status;
4250  QWORD protMask, childProtMask, protBetaMask, protFeedbackMask;
4251  QWORD context;
4252 
4253  if (pTask->IsThread)
4254  {
4255  continue;
4256  }
4257 
4258  if (!(ProtProc->NamePattern && pTask->Path &&
4259  IntMatchPatternUtf8(ProtProc->NamePattern, pTask->Path->Name, 0)) &&
4260  (!IntMatchPatternUtf8(ProtProc->CommPattern, pTask->Comm, INTRO_MATCH_TRUNCATED)))
4261  {
4262  continue;
4263  }
4264 
4265  protMask = Remove ? 0 : ProtProc->Protection.Current;
4266  context = Remove ? 0 : ProtProc->Context;
4267  protBetaMask = Remove ? 0 : ProtProc->Protection.Beta;
4268  protFeedbackMask = Remove ? 0 : ProtProc->Protection.Feedback;
4269 
4270  if (pTask->Context != context)
4271  {
4272  pTask->Context = context;
4273  }
4274 
4275  pTask->Protection.Beta = protBetaMask;
4276  pTask->Protection.Feedback = protFeedbackMask;
4277 
4278  if (pTask->Protection.Mask == protMask)
4279  {
4280  continue;
4281  }
4282 
4283  status = IntLixTaskChangeProtectionFlags(pTask, protMask, protMask, context);
4284  if (!INT_SUCCESS(status))
4285  {
4286  ERROR("[ERROR] IntLixTaskChangeProtectionFlags failed for 0x%016llx (Cr3 0x%016llx): 0x%08x\n",
4287  pTask->Gva, pTask->Cr3, status);
4288  }
4289 
4290  // Children should be further down in list (not before this)
4291  for_next_task(pTask, pChild)
4292  {
4293  if (pChild->Exec || pChild->ActualParent != pTask->Gva)
4294  {
4295  continue;
4296  }
4297 
4298  if (Remove)
4299  {
4300  childProtMask = 0;
4301  pChild->Protection.Beta = 0;
4302  pChild->Protection.Feedback = 0;
4303  }
4304  else
4305  {
4306  // Need to reset it every time (it may change below for the current child)
4307  childProtMask = pTask->Protection.Mask;
4308  pChild->Protection.Beta = pTask->Protection.Beta;
4309  pChild->Protection.Feedback = pTask->Protection.Feedback;
4310 
4311  context = pTask->Context;
4312 
4313  if (pTask->Cr3 == pChild->Cr3 || 0 == pChild->Cr3)
4314  {
4315  childProtMask &= ~PROC_OPT_PROT_EXPLOIT;
4316  childProtMask &= ~PROC_OPT_PROT_CORE_HOOKS;
4317  }
4318  }
4319 
4320  status = IntLixTaskChangeProtectionFlags(pChild, childProtMask, pTask->RootProtectionMask, context);
4321  if (!INT_SUCCESS(status))
4322  {
4323  ERROR("[ERROR] IntLixTaskChangeProtectionFlags failed for 0x%016llx (Cr3 0x%016llx): 0x%08x\n",
4324  pChild->Gva, pChild->Cr3, status);
4325  }
4326  }
4327  }
4328 
4329  return INT_STATUS_SUCCESS;
4330 }
4331 
4332 
4333 INTSTATUS
4335  _In_ const char *ProcessName,
4336  _In_ QWORD ProtectionMask,
4337  _In_ QWORD Context
4338  )
4351 {
4352  size_t nameLen;
4353  INTSTATUS status;
4354  LIX_PROTECTED_PROCESS *pProt = NULL;
4355 
4356  if (NULL == ProcessName)
4357  {
4359  }
4360 
4361  nameLen = strlen(ProcessName);
4362 
4363  if (nameLen >= 64 * ONE_KILOBYTE)
4364  {
4365  ERROR("[ERROR] Names longer than 64K are not supported!\n");
4366  return INT_STATUS_NOT_SUPPORTED;
4367  }
4368 
4369  for_each_task_to_protect(pExtProt)
4370  {
4371  if ((pExtProt->NamePattern && 0 == strncasecmp(pExtProt->NamePattern, ProcessName, nameLen + 1)) ||
4372  0 == strncasecmp(pExtProt->CommPattern, ProcessName, MIN(LIX_COMM_SIZE, nameLen + 1)))
4373  {
4374  LOG("[PROT] Process %s already protected as %s with %llx... Update the protection to %llx\n",
4375  ProcessName, pExtProt->NamePattern ? pExtProt->NamePattern : pExtProt->CommPattern,
4376  pExtProt->Protection.Original, ProtectionMask);
4377 
4378  pExtProt->Protection.Original = ProtectionMask;
4379  pExtProt->Protection.Current = ProtectionMask;
4380  pExtProt->Protection.Beta = 0;
4381  pExtProt->Protection.Feedback = 0;
4382 
4383  pExtProt->Context = Context;
4384  pProt = pExtProt;
4385 
4386  break;
4387  }
4388  }
4389 
4390  // If not found, add a new entry
4391  if (NULL == pProt)
4392  {
4393  pProt = HpAllocWithTag(sizeof(*pProt), IC_TAG_POBJ);
4394  if (NULL == pProt)
4395  {
4397  }
4398 
4399  pProt->NamePattern = HpAllocWithTag(nameLen + 1, IC_TAG_NAME);
4400  if (NULL == pProt->NamePattern)
4401  {
4402  ERROR("[ERROR] Process '%s' will not be protected as there is not enough memory available\n", ProcessName);
4403 
4405 
4407  }
4408 
4409  strlcpy(pProt->NamePattern, ProcessName, nameLen + 1);
4410 
4411  strlcpy(pProt->CommPattern, ProcessName, sizeof(pProt->CommPattern));
4412 
4413  pProt->Protection.Original = ProtectionMask;
4414  pProt->Protection.Current = ProtectionMask;
4415  pProt->Protection.Beta = 0;
4416  pProt->Protection.Feedback = 0;
4417 
4418  pProt->Context = Context;
4419 
4421 
4422  LOG("[PROT] Process %s / %s protected with %llx\n",
4423  pProt->CommPattern, pProt->NamePattern, pProt->Protection.Original);
4424 
4425  InsertTailList(&gLixTasksToProtect, &pProt->Link);
4426  }
4427 
4428  status = IntLixTaskAdjustProtections(pProt, FALSE);
4429  if (!INT_SUCCESS(status))
4430  {
4431  ERROR("[ERROR] IntLixTaskAdjustProtection failed for '%s': %08x\n", pProt->CommPattern, status);
4432  }
4433 
4434  return INT_STATUS_SUCCESS;
4435 }
4436 
4437 
4438 INTSTATUS
4440  _In_ const char *ProcessName
4441  )
4450 {
4451  INTSTATUS status;
4452  size_t nameLen;
4453  LIX_PROTECTED_PROCESS *pProt = NULL;
4454 
4455  if (NULL == ProcessName)
4456  {
4458  }
4459 
4460  nameLen = strlen(ProcessName);
4461 
4462  for_each_task_to_protect(pExtProt)
4463  {
4464  if (0 == strncasecmp(pExtProt->NamePattern, ProcessName, nameLen + 1))
4465  {
4466  LOG("Remove process %s from protected list!\n", pExtProt->NamePattern);
4467 
4468  pProt = pExtProt;
4469  break;
4470  }
4471  }
4472 
4473  if (NULL == pProt)
4474  {
4475  return INT_STATUS_NOT_FOUND;
4476  }
4477 
4478  status = IntLixTaskAdjustProtections(pProt, TRUE);
4479  if (!INT_SUCCESS(status))
4480  {
4481  ERROR("[ERROR] IntLixTaskAdjustProtection failed for '%s': %08x\n", pProt->CommPattern, status);
4482  }
4483 
4484  RemoveEntryList(&pProt->Link);
4485 
4487 
4489 
4490  return INT_STATUS_SUCCESS;
4491 }
4492 
4493 
4494 void
4496  void
4497  )
4501 {
4502  INTSTATUS status;
4503 
4504  for_each_task(pTask)
4505  {
4506  const LIX_PROTECTED_PROCESS *pProt = IntLixTaskShouldProtect(pTask);
4507 
4508  if (NULL == pProt)
4509  {
4511  }
4512  else
4513  {
4514  status = IntLixTaskAdjustProtections(pProt, FALSE);
4515  if (!INT_SUCCESS(status))
4516  {
4517  ERROR("[ERROR] IntLixTaskAdjustProtection failed for '%s': %08x\n", pProt->CommPattern, status);
4518  }
4519  }
4520  }
4521 }
4522 
4523 
4524 INTSTATUS
4526  _Out_writes_bytes_(Length) char *CommandLine,
4527  _In_ DWORD Length
4528  )
4538 {
4539  char *cmd = CommandLine;
4540 
4541  for_each_task(pTask)
4542  {
4543  INT32 len;
4544 
4545  if (!pTask->AgentTag)
4546  {
4547  continue;
4548  }
4549 
4550  len = snprintf(cmd, Length, "%s %d ", pTask->Path ? pTask->Path->Name : pTask->Comm, pTask->Pid);
4551  if (len < 0)
4552  {
4554  }
4555 
4556  if ((DWORD)len >= Length)
4557  {
4559  }
4560 
4561  Length -= len;
4562  cmd += len;
4563  }
4564 
4565  return INT_STATUS_SUCCESS;
4566 }
4567 
4568 
4569 void
4571  void
4572  )
4576 {
4577  for_each_task(pTask)
4578  {
4579  IntLixTaskMarkAgent(pTask);
4580 
4581  RemoveEntryList(&pTask->Link);
4582 
4583  IntLixTaskRemoveEntry(pTask);
4584  }
4585 
4587  {
4588  RemoveEntryList(&pProt->Link);
4589 
4590  if (NULL != pProt->NamePattern)
4591  {
4592  HpFreeAndNullWithTag(&pProt->NamePattern, IC_TAG_NAME);
4593  }
4594 
4596  }
4597 }
4598 
4599 
4600 static void
4602  _In_opt_ LIX_TASK_OBJECT *Task,
4603  _In_ DWORD Level
4604  )
4611 {
4612  DWORD flags;
4613  INTSTATUS status;
4614 
4615  // Search the first process and start there
4616  if (NULL == Task)
4617  {
4618  for_each_task(pProc)
4619  {
4620  if (!pProc->IsThread)
4621  {
4622  Task = pProc;
4623  break;
4624  }
4625  }
4626  }
4627 
4628  if (NULL == Task)
4629  {
4630  LOG("We have no processes in the system!\n");
4631  return;
4632  }
4633 
4634  if (Level >= 2)
4635  {
4636  for (DWORD i = 0; i < Level - 1; i++)
4637  {
4638  NLOG("--");
4639  }
4640 
4641  NLOG("--| ");
4642  }
4643  else if (Level >= 1)
4644  {
4645  NLOG("| ");
4646  }
4647 
4648  status = IntKernVirtMemFetchDword(Task->Gva + LIX_FIELD(TaskStruct, Flags), &flags);
4649  if (!INT_SUCCESS(status))
4650  {
4651  flags = 0;
4652  }
4653 
4654  if (!Task->IsThread)
4655  {
4656  NLOG("%6d/%-6d: %s %-40s CR3: 0x%016llx, prot: %d/%llx, mm_struct: 0x%016llx, task_struct: 0x%016llx, "
4657  "flags: %08x%s%s, parent: 0x%016llx, real_parent: 0x%016llx\n",
4658  Task->Pid, Task->Tgid,
4659  Task->Exec ? "EXEC" : "FORK",
4660  Task->ProcName,
4661  Task->Cr3,
4662  Task->Protected, Task->Protection.Mask,
4663  Task->MmGva,
4664  Task->Gva,
4665  flags,
4666  Task->Interpreter ? ", by interpreter " : "",
4667  Task->Interpreter ? Task->Interpreter : "",
4668  Task->Parent, Task->RealParent);
4669  }
4670  else
4671  {
4672  char newComm[LIX_COMM_SIZE] = {0};
4673 
4674  status = IntKernVirtMemRead(Task->Gva + LIX_FIELD(TaskStruct, Comm),
4675  sizeof(newComm) - 1,
4676  newComm,
4677  NULL);
4678  if (!INT_SUCCESS(status))
4679  {
4680  newComm[0] = '\0';
4681  }
4682 
4683  NLOG("%6d/%-6d: %-16s / %-16s task_struct: 0x%016llx, prot: %d/%llx, flags: %08x, parent: 0x%016llx, "
4684  "real_parent: 0x%016llx\n",
4685  Task->Pid, Task->Tgid, Task->Comm, newComm, Task->Gva, Task->Protected,
4686  Task->Protection.Mask, flags, Task->Parent, Task->RealParent);
4687  }
4688 
4689  for_each_task(pProc)
4690  {
4691  if (pProc == Task)
4692  {
4693  continue;
4694  }
4695 
4696  if (!pProc->IsThread && pProc->Parent == Task->Gva)
4697  {
4698  IntLixTaskDumpTree(pProc, Level + 1);
4699  }
4700  else if (pProc->IsThread && pProc->Tgid == Task->Pid)
4701  {
4702  if (Task->IsThread)
4703  {
4704  IntLixTaskDumpTree(pProc, Level);
4705  }
4706  else
4707  {
4708  IntLixTaskDumpTree(pProc, Level + 1);
4709  }
4710  }
4711  }
4712 }
4713 
4714 
4715 static void
4717  _In_opt_ LIX_TASK_OBJECT *Thread,
4718  _In_ DWORD Level
4719  )
4729 {
4730  DWORD i, flags;
4731  INTSTATUS status;
4732 
4733  // Search the first kernel thread and start there
4734  if (NULL == Thread)
4735  {
4736  for_each_task(pThread)
4737  {
4738  if (pThread->IsThread && pThread->KernelMode)
4739  {
4740  Thread = pThread;
4741  break;
4742  }
4743  }
4744  }
4745 
4746  if (NULL == Thread || !Thread->IsThread || !Thread->KernelMode)
4747  {
4748  if (Level == 0)
4749  {
4750  LOG("We have no kernel thread in the system!\n");
4751  }
4752 
4753  return;
4754  }
4755 
4756  for (i = 0; i < Level; i++)
4757  {
4758  NLOG("--");
4759  }
4760 
4761  NLOG("> ");
4762 
4763  status = IntKernVirtMemFetchDword(Thread->Gva + 2 * sizeof(QWORD) + sizeof(DWORD), &flags);
4764  if (!INT_SUCCESS(status))
4765  {
4766  flags = 0;
4767  }
4768 
4769  NLOG("%6d/%-6d : %-16s task_struct: 0x%016llx, prot: %d/%llx, flags: %08x\n",
4770  Thread->Pid, Thread->Tgid, Thread->Comm, Thread->Gva,
4771  Thread->Protected, Thread->Protection.Mask, flags);
4772 
4773  for_each_task(pThread)
4774  {
4775  if (pThread->Parent == Thread->Gva)
4776  {
4777  IntLixTaskDumpKernelThreadTree(pThread, Level + 1);
4778  }
4779  }
4780 }
4781 
4782 
4783 void
4785  void
4786  )
4790 {
4791  IntLixTaskDumpTree(NULL, 0);
4793 }
4794 
4795 
4796 void
4798  void
4799  )
4803 {
4804  for_each_task(pTask)
4805  {
4806  if (!pTask->IsThread)
4807  {
4808  LOG("Process %s (%s), PID: %d, TS 0x%016llx, Mm 0x%016llx, Parent 0x%016llx, RealParent 0x%016llx, "
4809  "Protected: %d/%llx\n", !pTask->Exec ? "(no exec)" : pTask->Path ? pTask->Path->Path : "(no path)",
4810  pTask->Comm, pTask->Pid, pTask->Gva, pTask->MmGva,
4811  pTask->Parent, pTask->RealParent,
4812  pTask->Protected, pTask->Protection.Mask);
4813 
4814  if (pTask->Protection.Mask & PROC_OPT_PROT_EXPLOIT)
4815  {
4816  list_for_each(pTask->Vmas, LIX_VMA, pVma)
4817  {
4818  LOG(" [%016llx -> %016llx] : %08llx [file @%016llx] @ %016llx Hooked=%d (%c%c%c)\n",
4819  pVma->Start, pVma->End, pVma->Flags, pVma->File, pVma->Gva, pVma->Hook ? 1 : 0,
4820  (pVma->Flags & VM_EXEC) ? 'X' : '-',
4821  (pVma->Flags & VM_WRITE) ? 'W' : '-',
4822  (pVma->Flags & VM_READ) ? 'R' : '-');
4823  }
4824 
4825  IntLixMmListVmas(pTask->MmGva, pTask);
4826  }
4827 
4828  // Parse the list again for this process threads
4829  for_each_task(pThr)
4830  {
4831  if (!pThr->IsThread)
4832  {
4833  continue;
4834  }
4835 
4836  if (pThr->Tgid != pTask->Tgid)
4837  {
4838  continue;
4839  }
4840 
4841  LOG("----> Thread %s %d/%d @ 0x%016llx, Parent 0x%016llx, RealParent 0x%016llx, Protected: %d/%llx\n",
4842  pThr->Comm, pThr->Pid, pThr->Tgid, pThr->Gva,
4843  pThr->Parent, pThr->RealParent,
4844  pThr->Protected, pThr->Protection.Mask);
4845  }
4846  }
4847  }
4848 
4849  for_each_task(pKThread)
4850  {
4851  if (pKThread->IsThread && pKThread->KernelMode)
4852  {
4853  LOG("Kernel Thread %s, P(TG)ID %d/%d, CR3 0x%016llx, TS 0x%016llx, Parent 0x%016llx, "
4854  "RealParent 0x%016llx, ActualParent 0x%016llx, Protected: %d/%llx\n",
4855  pKThread->Comm, pKThread->Pid, pKThread->Tgid,
4856  pKThread->Cr3,
4857  pKThread->Gva,
4858  pKThread->Parent,
4859  pKThread->RealParent,
4860  pKThread->ActualParent,
4861  pKThread->Protected,
4862  pKThread->Protection.Mask);
4863  }
4864  }
4865 }
4866 
4867 
4868 void
4870  void
4871  )
4875 {
4876  DWORD i = 0;
4877 
4879  {
4880  LOG("# %04d %s, %llx, '%s'\n",
4881  i,
4882  pProt->CommPattern,
4883  pProt->Protection.Original,
4884  pProt->NamePattern ? pProt->NamePattern : "(none)");
4885 
4886  i++;
4887  }
4888 }
4889 
4890 
4891 INTSTATUS
4894  )
4903 {
4904  if (NULL == Callback)
4905  {
4907  }
4908 
4909  for_each_task(pTask)
4910  {
4911  INTSTATUS status = Callback(pTask);
4912  if (!INT_SUCCESS(status))
4913  {
4914  WARNING("[WARNING] Callback failed: 0x%08x\n", status);
4915  }
4916  }
4917 
4918  return INT_STATUS_SUCCESS;
4919 }
4920 
4921 
4922 BOOLEAN
4924  void
4925  )
4942 {
4943  LIX_TASK_OBJECT *pTerminateTask = NULL;
4944  WORD userModeTasks = 0;
4945  int systemState = IntLixGuestGetSystemState();
4946 
4947  if (systemState > (int)(LIX_FIELD(Ungrouped, Running)))
4948  {
4949  LOG("[LIX-GUEST] Found system state '%d'\n", systemState);
4950  return TRUE;
4951  }
4952 
4954  {
4955  return TRUE;
4956  }
4957 
4958  if (IsListEmpty(&gLixTasks))
4959  {
4960  return TRUE;
4961  }
4962 
4963  for_each_task(pTask)
4964  {
4965  if (pTask->KernelMode)
4966  {
4967  continue;
4968  }
4969 
4970  userModeTasks++;
4971 
4972  if (pTerminateTask != NULL)
4973  {
4974  continue;
4975  }
4976 
4977  for (DWORD index = 0; index < ARRAYSIZE(gLixTerminatingTasks); index++)
4978  {
4979  if (pTask->Path &&
4980  0 == strncmp(pTask->Path->Name, gLixTerminatingTasks[index], strlen(gLixTerminatingTasks[index]) + 1))
4981  {
4982  pTerminateTask = pTask;
4983  }
4984 
4985  if (0 == strncmp(pTask->Comm, gLixTerminatingTasks[index], sizeof(pTask->Comm)))
4986  {
4987  pTerminateTask = pTask;
4988  }
4989  }
4990  }
4991 
4992  if (pTerminateTask == NULL)
4993  {
4994  return FALSE;
4995  }
4996 
4997  LOG("[LIX-GUEST] Found shutdown/reboot task '%s'\n", pTerminateTask->Comm);
4998 
4999  if (userModeTasks > 6)
5000  {
5001  return FALSE;
5002  }
5003 
5004  return TRUE;
5005 }
5006 
5007 
5008 INTSTATUS
5010  _In_ void *Detour
5011  )
5023 {
5024  INTSTATUS status = INT_STATUS_SUCCESS;
5027  EXCEPTION_UM_ORIGINATOR originator = { 0 };
5028  EXCEPTION_VICTIM_ZONE victim = { 0 };
5031  QWORD dstAddress = gVcpu->Regs.R9;
5032  QWORD address = gVcpu->Regs.R10;
5033  DWORD length = (DWORD)(gVcpu->Regs.R11);
5034 
5035  UNREFERENCED_PARAMETER(Detour);
5036 
5038 
5039  if (!(pVictim->Protection.Mask & PROC_OPT_PROT_WRITE_MEM))
5040  {
5041  action = introGuestAllowed;
5042  goto _exit;
5043  }
5044 
5045  status = IntExceptUserGetOriginator(pOriginator, FALSE, address, NULL, &originator);
5046  if (!INT_SUCCESS(status))
5047  {
5048  reason = introReasonInternalError;
5049  action = introGuestNotAllowed;
5050 
5051  ERROR("[ERROR] IntExceptUserGetOriginator failed with status: 0x%08x\n", status);
5052  goto _exit;
5053  }
5054 
5055  status = IntExceptGetVictimProcess(pVictim, dstAddress, length, ZONE_WRITE, &victim);
5056  if (!INT_SUCCESS(status))
5057  {
5058  reason = introReasonInternalError;
5059  action = introGuestNotAllowed;
5060 
5061  ERROR("[ERROR] IntExceptGetVictimProcess failed with status: 0x%08x\n", status);
5062  goto _exit;
5063  }
5064 
5065  IntExcept(&victim, &originator, exceptionTypeUm, &action, &reason, introEventInjectionViolation);
5066 
5067 _exit:
5069 
5070  if (IntPolicyProcTakeAction(PROC_OPT_PROT_WRITE_MEM, pVictim, &action, &reason))
5071  {
5072  IntLixTaskSendInjectionEvent(pOriginator, pVictim, action, reason);
5073  }
5074 
5076 
5077  status = IntDetSetReturnValue(Detour, &gVcpu->Regs, action == introGuestAllowed ? 0 : -EACCES);
5078  if (!INT_SUCCESS(status))
5079  {
5080  ERROR("[ERROR] IntDetoursGstSetReturnValue failed: 0x%08x\n", status);
5081  }
5082 
5083  return INT_STATUS_SUCCESS;
5084 }
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:1098
struct _EVENT_MEMCOPY_VIOLATION::@297 Originator
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
LIX_OPAQUE_FIELDS OsSpecificFields
OS-dependent and specific information.
Definition: lixguest.h:579
#define PTRACE_SETREGS
Definition: lixddefs.h:106
#define __unlikely(x)
Definition: common.h:64
static INTSTATUS IntLixTaskFetchMm(QWORD MmStruct, LIX_TASK_OBJECT *Task, LIX_TASK_OBJECT *Parent)
Fetches the CR3 of a Linux task.
Definition: lixprocess.c:692
#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:2044
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:5009
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:904
void IntLixTaskUninit(void)
Uninitializes the Linux process subsystem.
Definition: lixprocess.c:4570
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
static BYTE * gTaskPtr2
Definition: lixprocess.c:572
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:4784
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:1788
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:3256
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:1913
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:490
void IntLixTaskDump(void)
Dumps the process list.
Definition: lixprocess.c:4797
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1199
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:887
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#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:1910
INTRO_PROCESS Process
The process that attempted the access.
Definition: intro_types.h:1440
Measures user mode exceptions checks.
Definition: stats.h:50
#define BIT(x)
Definition: common.h:68
#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:1767
#define STATS_EXIT(id)
Definition: stats.h:160
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:60
User-mode non executable zone.
Definition: intro_types.h:247
Process creation violation.
Definition: intro_types.h:262
#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:2347
LIX_TASK_OBJECT * IntLixTaskFindByGva(QWORD TaskStruct)
Finds Linux process with the provided "task_struct" guest virtual address.
Definition: lixprocess.c:1025
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:2003
#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:313
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:1528
Process Injection.
Definition: intro_types.h:1146
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:1907
#define CR3_LONG_MODE_MASK
Definition: pgtable.h:112
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1198
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:4334
Event structure for agent injection and termination.
Definition: intro_types.h:2345
void * InitProcessObj
The LIX_TASK_OBJECT of the &#39;init&#39; process.
Definition: lixguest.h:532
The agent process finished execution.
Definition: intro_types.h:2102
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:4495
struct _LIX_TASK_OBJECT::@137 UserStack
User stack information.
static INTSTATUS IntLixTaskDeactivateExploitProtection(LIX_TASK_OBJECT *Task)
Deactivates exploit protection for a Linux task.
Definition: lixprocess.c:1210
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:2795
Process creation violation DPI.
Definition: intro_types.h:265
#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:994
#define INTRO_OPT_PROT_DPI
Aggregates all the deep process inspection flags.
Definition: intro_types.h:534
static BYTE * gTaskPtr1
Definition: lixprocess.c:571
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:2103
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:356
LIX_TASK_OBJECT * IntLixTaskProtFindByMm(QWORD MmGva)
Finds the protected Linux process having the provided mm guest virtual address.
Definition: lixprocess.c:974
DWORD ExitStatus
The exit code of the process.
Definition: intro_types.h:1927
DWORD OSVersion
Os version.
Definition: guests.h:281
int IntLixGuestGetSystemState(void)
Get the system state of the Linux guest.
Definition: lixguest.c:2201
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:2355
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:1173
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:4228
#define ALERT_FLAG_LINUX
Definition: intro_types.h:676
#define LIX_PTI_PGTABLE_SWITCH_BIT
The bit marking whether the kernel memory is mapped in a PGD.
Definition: lixddefs.h:290
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:2349
#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:1041
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:500
#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:596
#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
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:919
BOOLEAN KptiActive
True if KPTI is enabled on this guest, False if it is not.
Definition: guests.h:291
#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:1151
Access Token Manipulation.
Definition: intro_types.h:1153
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
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:576
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:2609
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:2428
static void IntLixTaskDumpTree(LIX_TASK_OBJECT *Task, DWORD Level)
Dumps the user mode tasks tree.
Definition: lixprocess.c:4601
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:1536
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:1051
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:1769
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:1436
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:500
#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:3893
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:607
#define STATS_ENTER(id)
Definition: stats.h:153
struct _LINUX_GUEST::@126 Layout
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1196
static void IntLixTaskDeactivateProtection(LIX_TASK_OBJECT *Task)
Deactivates protection for a Linux process.
Definition: lixprocess.c:1498
#define INTRO_OPT_EVENT_PROCESSES
Enable process creation and termination events (generates introEventProcessEvent events).
Definition: intro_types.h:443
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:1663
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:435
static void IntLixTaskGetPath(QWORD FileGva, QWORD DPathGva, LIX_TASK_OBJECT *Task)
Read and set the path for a Linux process.
Definition: lixprocess.c:1714
static void IntLixTaskSendTaskEvent(LIX_TASK_OBJECT *Task, DWORD ExitCode, BOOLEAN Created, BOOLEAN Crashed, BOOLEAN StaticDetected)
Sends a process event.
Definition: lixprocess.c:1934
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:236
INTSTATUS IntLixTaskHandlePtrace(void *Detour)
Handles the ptrace() system call.
Definition: lixprocess.c:3423
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:3204
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:3513
INTRO_PROCESS CurrentProcess
The currently active process.
Definition: intro_types.h:1930
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:2910
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:1933
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:3179
#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:374
The parent of a process had a pivoted stack when it created the child.
Definition: intro_types.h:1660
#define LIX_FIELD(Structure, Field)
Macro used to access fields inside the LIX_OPAQUE_FIELDS structure.
Definition: lixguest.h:429
#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:1434
DWORD AgentTag
Unique agent tag. See INTRO_DEP_AG_TAGS.
Definition: intro_types.h:2348
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:2691
BOOLEAN Protected
True if the process is protected.
Definition: intro_types.h:1915
String will be encoded in utf-8.
Definition: update_guests.h:54
#define PROC_OPT_PROT_SCAN_CMD_LINE
Uses third party engines to scan the command line of a process.
Definition: intro_types.h:364
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:4525
INTSTATUS IntLixTaskHandleExec(void *Detour)
Handles the exec() system call of a linux process.
Definition: lixprocess.c:2947
INTSTATUS IntLixTaskAdd(QWORD TaskGva, QWORD StaticDetected)
Creates and adds a Linux process in the internal list.
Definition: lixprocess.c:3996
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:1077
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:795
#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:674
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:2582
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:70
DWORD KernelMode
TRUE if this process/thread is inside kernel mode.
Definition: lixprocess.h:97
Describes the modified zone.
Definition: exceptions.h:893
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
#define PROC_OPT_PROT_EXPLOIT
Blocks malicious execution attempts.
Definition: intro_types.h:352
#define PF_EXITING
Definition: lixddefs.h:121
char * Name
The path base name.
Definition: lixprocess.h:27
Exposes the functions used to schedule an asynchronous command line scan and receives its result...
void * InstructionCache
The currently used instructions cache.
Definition: guests.h:404
#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:2461
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:344
#define INT_STATUS_INVALID_DATA_VALUE
Definition: introstatus.h:136
size_t NameLength
The size of the base name.
Definition: lixprocess.h:32
#define strlen_s(s, n)
Definition: introcrt.h:34
INTSTATUS IntLixCmdLineInspect(LIX_TASK_OBJECT *Task)
Send a command line scan request to the scan engines.
Definition: lixcmdline.c:70
#define PROC_OPT_PROT_PREVENT_CHILD_CREATION
Prevent the process from creating child processes (other than instances of itself).
Definition: intro_types.h:360
INTSTATUS IntLixTaskHandleDoExit(void *Detour)
Handles the exit() system call.
Definition: lixprocess.c:3481
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:4106
static INTSTATUS IntLixTaskActivateExploitProtection(LIX_TASK_OBJECT *Task)
Activates exploit protection for a Linux task.
Definition: lixprocess.c:1273
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:1813
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:570
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:903
#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:1869
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
INTSTATUS IntLixTaskHandleVmRw(void *Detour)
Handles the process_vm_writev() system call.
Definition: lixprocess.c:3381
#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:999
#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:374
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:1380
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
LIX_TASK_OBJECT * IntLixTaskFindByCr3(QWORD Cr3)
Finds the Linux process having the provided Cr3.
Definition: lixprocess.c:942
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:1142
INTRO_PROCESS Originator
The process that attempted the violation.
Definition: intro_types.h:1807
LIX_TASK_LOG gLixTaskLogLevel
The global structure controlling linux process logging.
Definition: lixprocess.c:44
DWORD Pid
The task PID.
Definition: lixprocess.h:72
struct _EVENT_MEMCOPY_VIOLATION::@298 Victim
BOOLEAN IntLixTaskGuestTerminating(void)
Check whether the guest OS is terminating or not.
Definition: lixprocess.c:4923
#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:376
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1194
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
struct _LIX_TASK_OBJECT::@136 Protection
Protection specific flags.
#define PROC_OPT_PROT_WRITE_MEM
Blocks foreign write inside the target process.
Definition: intro_types.h:348
struct _LIX_TASK_OBJECT::@138 Dpi
DPI related information.
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:858
#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:2491
DWORD Protected
TRUE if the process is protected.
Definition: lixprocess.h:99
#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:63
#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:1197
#define PROC_OPT_REMEDIATE
Any event inside the process will trigger the injection of the remediation tool.
Definition: intro_types.h:369
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
BOOLEAN Crashed
True if the process crashed.
Definition: intro_types.h:1921
static INTSTATUS IntLixTaskIterateThreads(QWORD TaskStructGva, PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the threads of a Linux process.
Definition: lixprocess.c:3766
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:2101
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:3646
void IntLixTaskDumpProtected(void)
Dumps the list with processes that Introcore should protect.
Definition: lixprocess.c:4869
#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:1747
#define ONE_GIGABYTE
Definition: introdefs.h:91
BOOLEAN BugCheckInProgress
Definition: guests.h:333
INTRO_PROCESS Parent
The parent of the process.
Definition: intro_types.h:1935
QWORD SourceVirtualAddress
The virtual address of the source buffer.
Definition: intro_types.h:1464
void IntAlertFillLixCurrentProcess(INTRO_PROCESS *EventProcess)
Saves the current Linux process inside an event.
Definition: alerts.c:1310
#define SIGILL
Definition: lixddefs.h:198
struct _LIX_PROTECTED_PROCESS::@123 Protection
What protection policies should be applied.
INTSTATUS IntLixTaskIterateTasks(PFUNC_LixTaskIterateTasks Callback)
Call the Callback parameter for each task saved internally.
Definition: lixprocess.c:4892
QWORD DestinationVirtualAddress
The virtual address of the destination buffer.
Definition: intro_types.h:1474
Exploitation for Client Execution.
Definition: intro_types.h:1157
BYTE Version
The version field of the version string.
Definition: lixguest.h:487
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:1689
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:3799
QWORD IntKsymFindByName(const char *Name, QWORD *SymEnd)
Searches the given Name in kallsyms and returns the Start & End offset.
Definition: lixksym.c:1399
static DWORD IntLixTaskGetDpiViolationFlags(LIX_TASK_OBJECT *Task)
Returns the DPI flags for a Linux process.
Definition: lixprocess.c:2663
static void _IntLixTaskFinishMap(void)
Unmaps a previously mapped "task_struct".
Definition: lixprocess.c:670
#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:413
INTSTATUS IntLixTaskRemoveProtected(const char *ProcessName)
Removes a pattern of processes to be protected.
Definition: lixprocess.c:4439
void IntAlertFillLixProcess(const LIX_TASK_OBJECT *Task, INTRO_PROCESS *EventProcess)
Saves information about a Linux process inside an event.
Definition: alerts.c:1264
#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:734
#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:271
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:3357
LINUX_GUEST * gLixGuest
Global variable holding the state of a Linux guest.
Definition: lixguest.c:30
#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:4716
#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