Bitdefender Hypervisor Memory Introspection
lixmodule.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "lixmodule.h"
6 #include "alerts.h"
7 #include "crc32.h"
8 #include "decoder.h"
9 #include "hook.h"
10 #include "lixksym.h"
11 
12 
17 
22 
23 
27 typedef enum _MODULE_STATE
28 {
33 } MODULE_STATE;
34 
35 
39 #define LIX_MODULE_MAP_START 0xffffffffa0000000
40 #define LIX_MODULE_MAP_END 0xfffffffffeffffff
41 
42 #define LIX_MODULE_MAX_ITERATIONS 4096
43 
44 
45 static INTSTATUS
47  _In_ QWORD Driver
48  )
63 {
65  BYTE *pLixModule = NULL;
66  CHAR *pName = NULL;
67  QWORD moduleBase = 0;
68  DWORD coreSize = 0;
69  DWORD textSize = 0;
70  DWORD index = 0;
71 
72  status = IntVirtMemMap(Driver, LIX_FIELD(Module, Sizeof), gGuest.Mm.SystemCr3, 0, &pLixModule);
73  if (!INT_SUCCESS(status))
74  {
75  return status;
76  }
77 
78  if (*(DWORD *)(pLixModule + LIX_FIELD(Module, State)) > moduleStateUnformed)
79  {
81  goto _exit;
82  }
83 
84  pName = (CHAR *)(pLixModule + LIX_FIELD(Module, Name));
85  while (index < LIX_MODULE_NAME_LEN && pName[index])
86  {
87  if (pName[index] < ' ' || pName[index] > 'z')
88  {
90  goto _exit;
91  }
92 
93  index++;
94  }
95 
96  if (index == LIX_MODULE_NAME_LEN)
97  {
99  goto _exit;
100  }
101 
102  if (!LIX_FIELD(Info, HasModuleLayout))
103  {
104  moduleBase = *(QWORD *)(pLixModule + LIX_FIELD(Module, ModuleCore));
105  coreSize = *(DWORD *)(pLixModule + LIX_FIELD(Module, CoreSize));
106  textSize = *(DWORD *)(pLixModule + LIX_FIELD(Module, CoreTextSize));
107  }
108  else
109  {
110  moduleBase = *(QWORD *)(pLixModule + LIX_FIELD(Module, CoreLayout));
111  coreSize = *(DWORD *)(pLixModule + LIX_FIELD(Module, CoreLayout) + 0x08);
112  textSize = *(DWORD *)(pLixModule + LIX_FIELD(Module, CoreLayout) + 0x0c);
113  }
114 
115  if (!IN_RANGE(moduleBase, LIX_MODULE_MAP_START, LIX_MODULE_MAP_END) || moduleBase > Driver)
116  {
118  goto _exit;
119  }
120 
121  if (coreSize == 0 ||
122  coreSize >= (LIX_MODULE_MAP_END - moduleBase) ||
123  textSize == 0 ||
124  textSize >= (LIX_MODULE_MAP_END - moduleBase) ||
125  textSize >= coreSize)
126  {
128  goto _exit;
129  }
130 
131  TRACE("[LIXMODULE] Found module '%s' with base 0x%016llx and size %x and text size %x\n",
132  pName, moduleBase, coreSize, textSize);
133 
134  status = INT_STATUS_SUCCESS;
135 
136 _exit:
137  IntVirtMemUnmap(&pLixModule);
138 
139  return status;
140 }
141 
142 
143 static INTSTATUS
145  _In_ KERNEL_DRIVER *Driver
146  )
159 {
160  INTSTATUS status;
161 
163  {
165  }
166 
167  if (!Driver->Lix.Initialized)
168  {
169  WARNING("[WARNING]_IntLixDrvActivateProtection called but driver %s is not initialized yet!\n",
170  (char *)Driver->Name);
172  }
173 
174  if (Driver->Protected)
175  {
176  TRACE("[INFO] Driver %s is already protected.", (char *)Driver->Name);
178  }
179 
180  status = IntHookObjectCreate(introObjectTypeKmModule, 0, &Driver->HookObject);
181  if (!INT_SUCCESS(status))
182  {
183  ERROR("[ERROR] IntHookObjectCreate failed for driver %s. Protection will be disabled! \n",
184  (char *)Driver->Name);
185  return status;
186  }
187 
188  status = IntHookObjectHookRegion(Driver->HookObject, 0, Driver->Lix.CoreLayout.Base, Driver->Lix.CoreLayout.RoSize,
189  IG_EPT_HOOK_WRITE, IntLixDrvHandleWrite, Driver, 0, NULL);
190  if (!INT_SUCCESS(status))
191  {
192  ERROR("[ERROR] IntHookObjectHookRegion failed for Driver->Name -> %s Layout.Base -> 0x%llx "
193  "Layout.RoSize -> 0x%x\n",
194  (char *)Driver->Name, Driver->Lix.CoreLayout.Base, Driver->Lix.CoreLayout.RoSize);
195 
196  IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&Driver->HookObject, 0);
197 
198  Driver->Protected = FALSE;
199 
200  return status;
201  }
202 
203  Driver->Protected = TRUE;
204  Driver->ProtectionFlag = INTRO_OPT_PROT_KM_LX_MODULES;
205 
206  TRACE("[INFO] Driver %s successfully hooked. GVA: 0x%llx Size: 0x%x\n",
207  (char *)Driver->Name, Driver->Lix.CoreLayout.Base, Driver->Lix.CoreLayout.RoSize);
208 
209  return INT_STATUS_SUCCESS;
210 }
211 
212 
213 static void
215  _In_ KERNEL_DRIVER *Driver
216  )
224 {
225  INTSTATUS status = INT_STATUS_SUCCESS;
226 
227  if (!Driver->Protected || (!Driver->HookObject))
228  {
229  return;
230  }
231 
232  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&Driver->HookObject, 0);
233  if (!INT_SUCCESS(status))
234  {
235  ERROR("[ERROR] IntHookObjectDestroy failed for driver %s", (char *)Driver->Name);
236  }
237 
238  Driver->Protected = FALSE;
239  Driver->ProtectionFlag = 0;
240 }
241 
242 
243 INTSTATUS
245  _Out_ QWORD *Drivers
246  )
258 {
259  INTSTATUS status = INT_STATUS_SUCCESS;
260  QWORD *ptr = NULL;
261  BOOLEAN found = FALSE;
262 
263  for (QWORD startGva = gLixGuest->Layout.DataStart & PAGE_MASK;
264  startGva < gLixGuest->Layout.DataEnd;
265  startGva += PAGE_SIZE)
266  {
267  DWORD parsed = 0;
268  DWORD toParse = PAGE_SIZE / sizeof(QWORD);
269 
270  status = IntVirtMemMap(startGva, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &ptr);
271  if (!INT_SUCCESS(status))
272  {
273  ERROR("[ERROR] IntVirtMemMap failed for 0x%016llx: 0x%08x\n", startGva, status);
274  return status;
275  }
276 
277  // On the last page, ignore the last one (it can't start there)
278  if (startGva == gLixGuest->Layout.DataEnd - PAGE_SIZE)
279  {
280  --toParse;
281  }
282 
283  while (parsed < toParse)
284  {
285  QWORD next, prev;
286  QWORD current = startGva + parsed * sizeof(QWORD);
287 
288  next = ptr[parsed];
289 
290  // The pointers may point inside Linux module mapping space or to itself
291  if ((next < LIX_MODULE_MAP_START || next > LIX_MODULE_MAP_END) && next != current)
292  {
293  goto _next_ptr;
294  }
295 
296  // Make sure we don't overflow
297  if (parsed == PAGE_SIZE / sizeof(QWORD) - 1)
298  {
299  // It's safe, since on the last page it won't get to this case.
300  status = IntVirtMemFetchQword(startGva + PAGE_SIZE, gGuest.Mm.SystemCr3, &prev);
301  if (!INT_SUCCESS(status))
302  {
303  ERROR("[ERROR] Failed getting the prev pointer from 0x%016llx: 0x%08x\n",
304  startGva + PAGE_SIZE, status);
305  break;
306  }
307  }
308  else
309  {
310  prev = ptr[parsed + 1];
311  }
312 
313  if ((prev < LIX_MODULE_MAP_START || prev > LIX_MODULE_MAP_END) && prev != current)
314  {
315  goto _next_ptr;
316  }
317 
318  // It's an empty list we shall ignore it. If we have no modules loaded, then we don't care.
319  if (next == prev && next == current)
320  {
321  goto _next_ptr;
322  }
323 
324  // We found one candidate (a doubly linked list). Make sure the addresses are good and that they point
325  // to a 'struct module'.
326  if (next != current)
327  {
328  status = IntLixDrvValidate(next - LIX_FIELD(Module, List));
329  if (!INT_SUCCESS(status))
330  {
331  goto _next_ptr;
332  }
333  }
334 
335  if (prev != current)
336  {
337  status = IntLixDrvValidate(prev - LIX_FIELD(Module, List));
338  if (!INT_SUCCESS(status))
339  {
340  goto _next_ptr;
341  }
342  }
343 
344  // We can say that this is our list.
345  found = TRUE;
346  break;
347 
348 _next_ptr:
349  parsed++;
350  }
351 
352  IntVirtMemUnmap(&ptr);
353 
354  if (found)
355  {
356  *Drivers = startGva + parsed * sizeof(QWORD);
357  LOG("[MODULE] Found the 'modules' list at 0x%016llx\n", *Drivers);
358  break;
359  }
360  }
361 
362  if (!found)
363  {
364  return INT_STATUS_NOT_FOUND;
365  }
366 
367  return INT_STATUS_SUCCESS;
368 }
369 
370 
371 static INTSTATUS
373  _In_ void *Context,
374  _In_ QWORD VirtualAddress,
375  _In_ QWORD OldEntry,
376  _In_ QWORD NewEntry,
377  _In_ QWORD OldPageSize,
378  _In_ QWORD NewPageSize
379  )
396 {
397  UNREFERENCED_PARAMETER(OldPageSize);
398  UNREFERENCED_PARAMETER(NewPageSize);
399  UNREFERENCED_PARAMETER(VirtualAddress);
400 
401  // If the previous was present/W and the new is not present/W
402  if (((OldEntry & PD_P) && !(NewEntry & PD_P)) || (!(OldEntry & PD_RW) && (NewEntry & PD_RW)))
403  {
404  KERNEL_DRIVER *pDriver = Context;
405  INTSTATUS status;
406 
407  pDriver->Lix.Initialized = TRUE;
408 
409  status = IntLixDrvActivateProtection(pDriver);
410  if (!INT_SUCCESS(status))
411  {
412  ERROR("[ERROR] IntLixDrvActivateProtection failed for `%s` (0x%llx 0x%08x)\n",
413  (char *)pDriver->Name,
414  pDriver->Lix.CoreLayout.Base,
415  pDriver->Lix.CoreLayout.RoSize);
416  }
417 
418  if (NULL != pDriver->Lix.InitSwapHook)
419  {
420  status = IntHookGvaRemoveHook((HOOK_GVA **)&pDriver->Lix.InitSwapHook, 0);
421 
422  if (!INT_SUCCESS(status))
423  {
424  ERROR("[ERROR] IntHookGvaRemoveHook: 0x%08x\n", status);
426  }
427  }
428  }
429 
430  return INT_STATUS_SUCCESS;
431 }
432 
433 
434 static INTSTATUS
436  _In_ KERNEL_DRIVER *Driver,
437  _In_ BOOLEAN Loaded,
438  _In_ BOOLEAN StaticDetected
439  )
450 {
451  INTSTATUS status;
452  EVENT_MODULE_EVENT *pEvent;
453 
455  {
457  }
458 
459  pEvent = &gAlert.Module;
460  memzero(pEvent, sizeof(*pEvent));
461 
462  pEvent->Loaded = Loaded;
463  pEvent->Protected = TRUE;
464 
465  IntAlertFillLixKmModule(Driver, &pEvent->Module);
466 
467  if (!StaticDetected)
468  {
470  }
471  else
472  {
473  pEvent->CurrentProcess.Valid = FALSE;
474  }
475 
476  status = IntNotifyIntroEvent(introEventModuleEvent, pEvent, sizeof(*pEvent));
477  if (!INT_SUCCESS(status))
478  {
479  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
480  }
481 
482  return status;
483 }
484 
485 
486 void
488  void
489  )
493 {
494  list_for_each(gKernelDrivers, KERNEL_DRIVER, pDriver)
495  {
496  if (gGuest.KernelDriver == pDriver)
497  {
498  continue;
499  }
500 
502  {
504  }
505  else
506  {
508  }
509  }
510 }
511 
512 
513 static void
515  _In_ QWORD DriverGva
516  )
527 {
528  INTSTATUS status = INT_STATUS_SUCCESS;
529 
530  list_for_each(gKernelDrivers, KERNEL_DRIVER, pDriver)
531  {
532  if (pDriver->ObjectGva == DriverGva)
533  {
534  WARNING("[WARNING] Driver %s (%llx) already exists in our list...\n",
535  (char *)pDriver->Name, pDriver->ObjectGva);
536 
537  if (!pDriver->Lix.Initialized)
538  {
539  ERROR("[ERROR] Driver '%s' %llx is not initialized but already in our list...\n",
540  (char *)pDriver->Name, pDriver->ObjectGva);
541  }
542 
543  RemoveEntryList(&pDriver->Link);
544 
545  IntLixDrvSendEvent(pDriver, FALSE, FALSE);
546 
547  status = IntLixDrvRemoveEntry(pDriver);
548  if (!INT_SUCCESS(status))
549  {
550  ERROR("[ERROR] IntLixDrvRemoveEntry failed: 0x%08x\n", status);
551  }
552  }
553  }
554 }
555 
556 
557 static INTSTATUS
559  _In_ QWORD DriverGva,
560  _Out_ KERNEL_DRIVER **Object
561  )
571 {
572  INTSTATUS status = INT_STATUS_SUCCESS;
573  KERNEL_DRIVER *pDriver = NULL;
574  CHAR *pLixMod = NULL;
575 
576  pDriver = HpAllocWithTag(sizeof(*pDriver), IC_TAG_MODU);
577  if (NULL == pDriver)
578  {
580  }
581 
582  pDriver->ObjectGva = DriverGva;
583 
584  status = IntVirtMemMap(pDriver->ObjectGva, LIX_FIELD(Module, Sizeof), gGuest.Mm.SystemCr3, 0, &pLixMod);
585  if (!INT_SUCCESS(status))
586  {
587  ERROR("[ERROR] Failed mapping GVA 0x%016llx: 0x%08x\n", pDriver->ObjectGva, status);
588  goto _exit;
589  }
590 
591  // Don't assume the guest structure isn't corrupted!
592  pDriver->NameLength = strlen_s(pLixMod + LIX_FIELD(Module, Name), LIX_MODULE_NAME_LEN);
593  if (pDriver->NameLength >= LIX_MODULE_NAME_LEN)
594  {
595  pDriver->NameLength = LIX_MODULE_NAME_LEN - 1;
596  }
597 
598  // NameLength + 2 OK: NameLength is not longer than LIX_MODULE_NAME_LEN.
599  pDriver->Name = HpAllocWithTag(pDriver->NameLength + 2ull, IC_TAG_DRNU);
600  if (NULL == pDriver->Name)
601  {
603  goto _exit;
604  }
605 
606  memcpy(pDriver->Name, pLixMod + LIX_FIELD(Module, Name), pDriver->NameLength);
607  *((char *)pDriver->Name + pDriver->NameLength + 1) = 0;
608 
609  pDriver->NameHash = Crc32String(pDriver->Name, INITIAL_CRC_VALUE);
610 
611  pDriver->Lix.KernelSymbols = *(QWORD *)(pLixMod + LIX_FIELD(Module, Symbols));
612  pDriver->Lix.SymbolsCount = *(DWORD *)(pLixMod + LIX_FIELD(Module, NumberOfSymbols));
613 
614  pDriver->Lix.GplSymbols = *(QWORD *)(pLixMod + LIX_FIELD(Module, GplSymbols));
615  pDriver->Lix.GplSymbolsCount = *(DWORD *)(pLixMod + LIX_FIELD(Module, NumberOfGplSymbols));
616 
617  if (!LIX_FIELD(Info, HasModuleLayout))
618  {
619  // NOTE: module_layout isn't randomized
620  pDriver->Lix.InitLayout.Base = *(QWORD *)(pLixMod + LIX_FIELD(Module, ModuleInit));
621  pDriver->Lix.InitLayout.Size = *(DWORD *)(pLixMod + LIX_FIELD(Module, InitSize));
622  pDriver->Lix.InitLayout.TextSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, InitTextSize));
623  pDriver->Lix.InitLayout.RoSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, InitRoSize));
624 
625  pDriver->Lix.CoreLayout.Base = *(QWORD *)(pLixMod + LIX_FIELD(Module, ModuleCore));
626  pDriver->Lix.CoreLayout.Size = *(DWORD *)(pLixMod + LIX_FIELD(Module, CoreSize));
627  pDriver->Lix.CoreLayout.TextSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, CoreTextSize));
628  pDriver->Lix.CoreLayout.RoSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, CoreRoSize));
629  }
630  else
631  {
632  pDriver->Lix.InitLayout.Base = *(QWORD *)(pLixMod + LIX_FIELD(Module, InitLayout));
633  pDriver->Lix.InitLayout.Size = *(DWORD *)(pLixMod + LIX_FIELD(Module, InitLayout) + 0x08);
634  pDriver->Lix.InitLayout.TextSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, InitLayout) + 0x0c);
635  pDriver->Lix.InitLayout.RoSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, InitLayout) + 0x10);
636 
637  pDriver->Lix.CoreLayout.Base = *(QWORD *)(pLixMod + LIX_FIELD(Module, CoreLayout));
638  pDriver->Lix.CoreLayout.Size = *(DWORD *)(pLixMod + LIX_FIELD(Module, CoreLayout) + 0x08);
639  pDriver->Lix.CoreLayout.TextSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, CoreLayout) + 0x0c);
640  pDriver->Lix.CoreLayout.RoSize = *(DWORD *)(pLixMod + LIX_FIELD(Module, CoreLayout) + 0x10);
641  }
642 
643  pDriver->EntryPoint = *(QWORD *)(pLixMod + LIX_FIELD(Module, Init));
644  pDriver->BaseVa = pDriver->Lix.CoreLayout.Base;
645  pDriver->Size = pDriver->Lix.CoreLayout.Size;
646 
647  *Object = pDriver;
648 
649  return INT_STATUS_SUCCESS;
650 
651 _exit:
652  if (pDriver != NULL)
653  {
654  if (pDriver->Name != NULL)
655  {
657  }
658 
660  }
661 
662  if (pLixMod != NULL)
663  {
664  IntVirtMemUnmap(&pLixMod);
665  }
666 
667  return status;
668 }
669 
670 
671 static BOOLEAN
673  _In_ QWORD Gva
674  )
682 {
683  for (DWORD index = 0; index < ARRAYSIZE(gLixGuest->ActivePatch); index++)
684  {
685  if (IN_RANGE_LEN(Gva, gLixGuest->ActivePatch[index].Gva, gLixGuest->ActivePatch[index].Length))
686  {
687  return TRUE;
688  }
689  }
690 
691  return FALSE;
692 }
693 
694 
695 INTSTATUS
697  _In_ QWORD DriverGva,
698  _In_ QWORD StaticDetected
699  )
723 {
724  INTSTATUS status = INT_STATUS_SUCCESS;
725  KERNEL_DRIVER *pDriver = NULL;
726 
727  if (!StaticDetected)
728  {
729  IntLixDrvRemoveDuplicate(DriverGva);
730  }
731 
732  if (StaticDetected)
733  {
734  DWORD moduleState = 0;
735 
736  status = IntKernVirtMemFetchDword(DriverGva + LIX_FIELD(Module, State), &moduleState);
737  if (!INT_SUCCESS(status))
738  {
739  ERROR("[ERROR] IntKernVirtMemFetchDword failed for @ 0x%016llx with status: 0x%08x\n",
740  DriverGva + LIX_FIELD(Module, State), status);
741  return status;
742  }
743 
744  switch (moduleState)
745  {
746  case moduleStateLive:
747  case moduleStateComing:
748  {
749  break;
750  }
751 
752  case moduleStateGoing:
753  {
754  LOG("[MODULE] Module @ %llx is dying...\n", DriverGva);
755  gModuleIgnore++;
756 
757  return INT_STATUS_SUCCESS;
758  }
759 
760  case moduleStateUnformed:
761  {
762  LOG("[MODULE] Module @ %llx still setting up. Will ignore on static init...\n", DriverGva);
763  return INT_STATUS_SUCCESS;
764  }
765 
766  default:
767  {
768  ERROR("[ERROR] Shouldn't reach here. State type %d...\n", moduleState);
769  return INT_STATUS_SUCCESS;
770  }
771  }
772  }
773 
774  status = IntLixDrvCreateDriverObject(DriverGva, &pDriver);
775  if (!INT_SUCCESS(status))
776  {
777  ERROR("[ERROR] IntLixDrvCreateDriverObject failed with status: 0x%08x\n", status);
778  return status;
779  }
780 
781  // Hook the page-table of the init section to know when it's freed (after the module was initialized)
782  if (!StaticDetected && pDriver->Lix.InitLayout.Base != 0 && pDriver->Lix.InitLayout.Size != 0)
783  {
785  IntLixDrvInitVfreeHandler, pDriver, NULL, 0,
786  (HOOK_GVA **)&pDriver->Lix.InitSwapHook);
787  if (!INT_SUCCESS(status))
788  {
789  ERROR("[ERROR] IntHookGvaSetHook failed for VA 0x%llx with size %08x in module %s: 0x%08x",
790  pDriver->Lix.InitLayout.Base, MIN(PAGE_SIZE, pDriver->Lix.InitLayout.Size),
791  (char *)pDriver->Name, status);
792 
793  pDriver->Lix.InitSwapHook = NULL;
794  pDriver->Lix.Initialized = TRUE;
795  }
796  }
797  else
798  {
799  // We have no init section or this is a static scan... Then it's initialized!
800  pDriver->Lix.Initialized = TRUE;
801  }
802 
803  TRACE("[MODULE] Loaded 0x%016llx @ 0x%016llx: %s\n", pDriver->ObjectGva, pDriver->BaseVa, (char *)pDriver->Name);
804  TRACE("---> EP: 0x%016llx, Size: %llx, TextSize: %x, RoSize: %x\n",
805  pDriver->EntryPoint, pDriver->Size, pDriver->Lix.CoreLayout.TextSize, pDriver->Lix.CoreLayout.RoSize);
806 
807  if (!StaticDetected && pDriver->Lix.InitLayout.Base != 0 && pDriver->Lix.InitLayout.Size != 0)
808  {
809  TRACE("---> Init: 0x%016llx, Size: %x, TextSize: %x, RoSize: %x\n",
810  pDriver->Lix.InitLayout.Base, pDriver->Lix.InitLayout.Size,
811  pDriver->Lix.InitLayout.TextSize, pDriver->Lix.InitLayout.RoSize);
812  }
813 
814  InsertTailList(&gKernelDrivers, &pDriver->Link);
815 
816  if (pDriver->Lix.Initialized)
817  {
819  }
820 
821  IntLixDrvSendEvent(pDriver, TRUE, StaticDetected != 0);
822 
823  return INT_STATUS_SUCCESS;
824 }
825 
826 
827 INTSTATUS
829  _In_ KERNEL_DRIVER *Driver
830  )
841 {
842  if (NULL == Driver)
843  {
845  }
846 
848 
849  if (NULL != Driver->Lix.InitSwapHook)
850  {
851  IntHookGvaRemoveHook((HOOK_GVA **)&Driver->Lix.InitSwapHook, 0);
852  }
853 
854  if (Driver->Name)
855  {
856  HpFreeAndNullWithTag(&Driver->Name, IC_TAG_DRNU);
857  }
858 
860 
861  return INT_STATUS_SUCCESS;
862 }
863 
864 
865 INTSTATUS
867  _In_ QWORD DriverGva
868  )
878 {
879  list_for_each(gKernelDrivers, KERNEL_DRIVER, pDriver)
880  {
881  if (pDriver->ObjectGva == DriverGva)
882  {
883  INTSTATUS status;
884 
885  TRACE("[MODULE] Unloaded module %s @ 0x%016llx\n", (char *)pDriver->Name, pDriver->BaseVa);
886 
887  RemoveEntryList(&pDriver->Link);
888 
889  IntLixDrvSendEvent(pDriver, FALSE, FALSE);
890 
891  status = IntLixDrvRemoveEntry(pDriver);
892  if (!INT_SUCCESS(status))
893  {
894  ERROR("[ERROR] IntLixDrvRemoveEntry failed: 0x%08x\n", status);
895  }
896 
897  return INT_STATUS_SUCCESS;
898  }
899  }
900 
901  gModuleIgnore--;
902 
903  if (gModuleIgnore != 0)
904  {
905  ERROR("[ERROR] Driver @ 0x%016llx is not found in internal list! Count: %d. \n", DriverGva, gModuleIgnore);
906  }
907 
908  return INT_STATUS_SUCCESS;
909 }
910 
911 
912 void
914  _In_ KERNEL_DRIVER *Driver,
915  _In_ QWORD Gva,
916  _Out_writes_(8) CHAR *SectionName
917  )
927 {
928  if (Driver == NULL)
929  {
930  return;
931  }
932 
933  if (SectionName == NULL)
934  {
935  return;
936  }
937 
938  if (IN_RANGE_LEN(Gva, Driver->BaseVa, Driver->Size))
939  {
940  QWORD offset = Gva - Driver->BaseVa;
941 
942  if (offset < Driver->Lix.CoreLayout.TextSize)
943  {
944  memcpy(SectionName, "text", sizeof("text"));
945  }
946  else if (offset < Driver->Lix.CoreLayout.RoSize)
947  {
948  memcpy(SectionName, "text_ro", sizeof("text_ro"));
949  }
950  else
951  {
952  memcpy(SectionName, "text_rw", sizeof("text_rw"));
953  }
954  }
955  else if (!Driver->Lix.Initialized && IN_RANGE(Gva, Driver->Lix.InitLayout.Base, Driver->Lix.InitLayout.Size))
956  {
957  QWORD offset = Gva - Driver->Lix.InitLayout.Base;
958 
959  if (offset < Driver->Lix.InitLayout.TextSize)
960  {
961  memcpy(SectionName, "init", sizeof("init"));
962  }
963  else if (offset < Driver->Lix.InitLayout.RoSize)
964  {
965  memcpy(SectionName, "init_ro", sizeof("init_ro"));
966  }
967  else
968  {
969  memcpy(SectionName, "init_rw", sizeof("init_rw"));
970  }
971  }
972  else
973  {
974  memcpy(SectionName, "unknown", sizeof("unknown"));
975  }
976 }
977 
978 
979 INTSTATUS
981  _In_ void *Hook,
982  _In_ QWORD Address,
983  _In_ LIX_ACTIVE_PATCH *ActivePatch,
984  _Out_ INTRO_ACTION *Action
985  )
1003 {
1004  INTSTATUS status;
1005  OPERAND_VALUE newValue = { 0 };
1006  BYTE patchData[sizeof(ActivePatch->Data)];
1007  BYTE *pWriteBuffer;
1008  QWORD gva;
1009  DWORD writeSize;
1010 
1011  if (NULL == Hook)
1012  {
1014  }
1015 
1016  if (ActivePatch == NULL)
1017  {
1019  }
1020 
1021  if (NULL == Action)
1022  {
1024  }
1025 
1026  *Action = introGuestNotAllowed;
1027 
1028  gva = IntHookGetGlaFromGpaHook(Hook, Address);
1029 
1030  if (!IN_RANGE_LEN(gva, ActivePatch->Gva, ActivePatch->Length))
1031  {
1032  WARNING("[WARNING] IntLixDrvIsLegitimateTextPoke called for 0x%llx which is not in ActivePatch range!\n", gva);
1033  LOG("[INFO] Active patch is at 0x%llx and is %d bytes long\n",
1034  ActivePatch->Gva, ActivePatch->Length);
1035  return INT_STATUS_NOT_SUPPORTED;
1036  }
1037 
1038  writeSize = gVcpu->AccessSize;
1039  if (gVcpu->Instruction.IsRepeated)
1040  {
1041  writeSize *= (DWORD)gVcpu->Regs.Rcx;
1042  }
1043 
1044  if ((gVcpu->Instruction.IsRepeated && (gVcpu->Regs.Rcx > ActivePatch->Length)) ||
1045  writeSize > ActivePatch->Length ||
1046  writeSize > sizeof(patchData))
1047  {
1048  WARNING("[WARNING] Invalid patch write at GVA %llx with size %d (rcx: 0x%llx)\n",
1049  ActivePatch->Gva, writeSize, gVcpu->Regs.Rcx);
1050 
1051  return INT_STATUS_SUCCESS;
1052  }
1053 
1054  if (!gVcpu->Instruction.IsRepeated)
1055  {
1056  status = IntDecGetWrittenValueFromInstruction(&gVcpu->Instruction, &gVcpu->Regs, NULL, &newValue);
1057  if (!INT_SUCCESS(status))
1058  {
1059  WARNING("[WARNING] Can't get newValue from instruction: 0x%08x\n", status);
1060  return status;
1061  }
1062 
1063  pWriteBuffer = newValue.Value.ByteValues;
1064  }
1065  else if (ND_INS_MOVS == gVcpu->Instruction.Instruction)
1066  {
1067  status = IntKernVirtMemRead(gVcpu->Regs.Rsi, writeSize, patchData, NULL);
1068  if (!INT_SUCCESS(status))
1069  {
1070  ERROR("[ERROR] IntKernVirtMemRead failed for 0x%llx and size %d with status 0x%08x\n",
1071  gVcpu->Regs.Rsi, writeSize, status);
1072  return status;
1073  }
1074 
1075  pWriteBuffer = patchData;
1076  }
1077  else
1078  {
1079  CHAR nd[ND_MIN_BUF_SIZE];
1080 
1081  NdToText(&gVcpu->Instruction, gVcpu->Regs.Rip, sizeof(nd), nd);
1082 
1083  ERROR("[ERROR] Instruction %s at RIP %llx is not supported for detour writes...\n", nd, gVcpu->Regs.Rip);
1084 
1085  return INT_STATUS_NOT_SUPPORTED;
1086  }
1087 
1088  // Quick check to see if the access is at a page boundary.
1089  // And this is the callback for the second page.
1090  if (((gVcpu->Gla & PAGE_OFFSET) + writeSize > PAGE_SIZE) && (0 == (Address & PAGE_OFFSET)))
1091  {
1092  DWORD delta = (DWORD)PAGE_REMAINING(gVcpu->Gla);
1093 
1094  if (delta > writeSize)
1095  {
1096  ERROR("[ERROR] Found a delta greater than the written size (d:%d w:%d).\n", delta, writeSize);
1097  }
1098  else
1099  {
1100  pWriteBuffer += delta;
1101  writeSize -= delta;
1102  }
1103  }
1104 
1105  if (0 != memcmp(pWriteBuffer, ActivePatch->Data + (gva - ActivePatch->Gva), writeSize))
1106  {
1107  WARNING("[WARNING] Invalid patch write at GVA %llx with size %d\n",
1108  ActivePatch->Gva, writeSize);
1109  return INT_STATUS_SUCCESS;
1110  }
1111 
1112  *Action = introGuestAllowed;
1113 
1114  return INT_STATUS_SUCCESS;
1115 }
1116 
1117 
1118 static void
1120  _In_ KERNEL_DRIVER *Driver,
1121  _In_ EXCEPTION_KM_ORIGINATOR *Originator,
1122  _In_ EXCEPTION_VICTIM_ZONE *Victim,
1123  _In_ HOOK_GPA *Hook,
1124  _In_ QWORD Address,
1125  _In_ INTRO_ACTION Action,
1126  _In_ INTRO_ACTION_REASON Reason
1127  )
1139 {
1140  INTSTATUS status = INT_STATUS_SUCCESS;
1141  EVENT_EPT_VIOLATION *pEptViol = &gAlert.Ept;
1142  HOOK_GVA *pGvaHook = (HOOK_GVA *)(((HOOK_GPA *)Hook)->Header.ParentHook);
1143  CHAR modSym[LIX_SYMBOL_NAME_LEN] = { 0 };
1144  QWORD addr = pGvaHook->GvaPage + (Address & PAGE_OFFSET);
1145 
1146  memzero(pEptViol, sizeof(*pEptViol));
1147 
1148  pEptViol->Header.Action = Action;
1149  pEptViol->Header.Reason = Reason;
1150  pEptViol->Header.MitreID = idRootkit;
1151 
1152  pEptViol->Header.Flags = IntAlertCoreGetFlags(Driver->ProtectionFlag, Reason);
1153 
1155 
1156  IntAlertFillLixKmModule(Originator->Original.Driver, &pEptViol->Originator.Module);
1157  IntAlertFillLixKmModule(Originator->Return.Driver, &pEptViol->Originator.ReturnModule);
1158 
1159  if (addr >= gLixGuest->Layout.ExTableStart && addr < gLixGuest->Layout.ExTableEnd)
1160  {
1161  Victim->Object.Type = introObjectTypeExTable;
1162  }
1163 
1164  IntAlertFillLixKmModule(Victim->Object.Module.Module, &pEptViol->Victim.Module);
1165 
1167 
1168  pEptViol->Offset = addr & PAGE_OFFSET;
1169  pEptViol->VirtualPage = pGvaHook->GvaPage;
1170  pEptViol->HookStartVirtual = Driver->BaseVa;
1171 
1173 
1174  pEptViol->Violation = IG_EPT_HOOK_WRITE;
1175  pEptViol->ZoneTypes = Victim->ZoneFlags;
1176 
1177  pEptViol->ReturnRip = 0;
1178 
1179  status = IntKsymFindByAddress(addr, sizeof(modSym), modSym, NULL, NULL);
1180  if (INT_SUCCESS(status))
1181  {
1182  memcpy(pEptViol->FunctionName, modSym, sizeof(pEptViol->FunctionName) - 1);
1183  }
1184 
1185  IntLixDrvGetSecName(Driver, addr, pEptViol->ModifiedSectionName);
1186 
1187  IntLixDrvGetSecName(Originator->Original.Driver, Originator->Original.Rip, pEptViol->RipSectionName);
1188 
1189  IntAlertFillVersionInfo(&pEptViol->Header);
1190 
1191  IntAlertFillCodeBlocks(Originator->Original.Rip, gGuest.Mm.SystemCr3, FALSE, &pEptViol->CodeBlocks);
1192 
1194 
1195  IntAlertEptFillFromVictimZone(Victim, pEptViol);
1196 
1197  status = IntNotifyIntroEvent(introEventEptViolation, pEptViol, sizeof(*pEptViol));
1198  if (!INT_SUCCESS(status))
1199  {
1200  WARNING("[WARNING] IntNotifyIntroEvent failed: 0x%08x\n", status);
1201  }
1202 }
1203 
1204 
1205 static BOOLEAN
1207  void
1208  )
1214 {
1215  static int state = -1;
1216 
1217  if (__likely(state >= (int)(LIX_FIELD(Ungrouped, Running))))
1218  {
1219  // Last time we checked the system was Running, there is no way it's gonna get back to booting again
1220  return FALSE;
1221  }
1222 
1223  // Re-fetch the system state
1224  state = IntLixGuestGetSystemState();
1225 
1226  if (state != -1 && (state < (int)LIX_FIELD(Ungrouped, Running)))
1227  {
1228  return TRUE;
1229  }
1230 
1231  return FALSE;
1232 }
1233 
1234 
1235 INTSTATUS
1237  _In_opt_ void *Context,
1238  _In_ void *Hook,
1239  _In_ QWORD Address,
1240  _Out_ INTRO_ACTION *Action
1241  )
1258 {
1259  INTSTATUS status = INT_STATUS_SUCCESS;
1260  KERNEL_DRIVER *pDriver = NULL;
1261  HOOK_GVA *pGvaHook = NULL;
1262  EXCEPTION_VICTIM_ZONE victim = { 0 };
1263  EXCEPTION_KM_ORIGINATOR originator = { 0 };
1265  BOOLEAN informationOnly = FALSE;
1266 
1268 
1269  if (NULL == Context)
1270  {
1272  }
1273 
1274  if (NULL == Hook)
1275  {
1277  }
1278 
1279  if (NULL == Action)
1280  {
1282  }
1283 
1284  *Action = introGuestNotAllowed;
1285  pDriver = Context;
1286  pGvaHook = (HOOK_GVA *)(((HOOK_GPA *)Hook)->Header.ParentHook);
1287 
1288  if (IntLixDrvIsActivePatch(pGvaHook->GvaPage + (Address & PAGE_OFFSET)))
1289  {
1290  for (DWORD index = 0; index < ARRAYSIZE(gLixGuest->ActivePatch); index++)
1291  {
1292  status = IntLixDrvIsLegitimateTextPoke(Hook, Address, &gLixGuest->ActivePatch[index], Action);
1293  if (INT_SUCCESS(status))
1294  {
1295  break;
1296  }
1297  }
1298 
1299  if (INT_SUCCESS(status) && (*Action == introGuestAllowed))
1300  {
1301  return INT_STATUS_SUCCESS;
1302  }
1303 
1304  if (!INT_SUCCESS(status))
1305  {
1306  ERROR("[ERROR] IntLixDrvHandleTextPoke failed for GPA: 0x%llx, GVA: 0x%llx: 0x%08x\n",
1307  Address, pGvaHook->GvaPage + (Address & PAGE_OFFSET), status);
1308  }
1309  }
1310 
1311  if (IntLixDrvSystemBooting())
1312  {
1313  *Action = introGuestAllowed;
1314  return INT_STATUS_SUCCESS;
1315  }
1316 
1318 
1319  status = IntExceptKernelGetOriginator(&originator, 0);
1320  if (status == INT_STATUS_EXCEPTION_BLOCK)
1321  {
1322  reason = introReasonNoException;
1323  informationOnly = TRUE;
1324  }
1325  else if (!INT_SUCCESS(status))
1326  {
1327  ERROR("[ERROR] IntExceptKernelGetOriginator failed with status: 0x%08x\n", status);
1328  reason = introReasonInternalError;
1329  informationOnly = TRUE;
1330  }
1331 
1332  status = IntExceptGetVictimEpt(pDriver, Address, pGvaHook->GvaPage + (Address & PAGE_OFFSET),
1334  if (!INT_SUCCESS(status))
1335  {
1336  ERROR("[ERROR] IntExceptGetVictimEpt failed with status: 0x%08x\n", status);
1337  reason = introReasonInternalError;
1338  informationOnly = TRUE;
1339  }
1340 
1341  if (informationOnly)
1342  {
1343  IntExceptKernelLogInformation(&victim, &originator, *Action, reason);
1344  }
1345  else
1346  {
1347  IntExcept(&victim, &originator, exceptionTypeKm, Action, &reason, introEventEptViolation);
1348  }
1349 
1351 
1352  if (IntPolicyCoreTakeAction(pDriver->ProtectionFlag, Action, &reason))
1353  {
1354  IntLixDrvSendViolationEvent(pDriver, &originator, &victim, Hook, Address, *Action, reason);
1355  }
1356 
1358 
1359  return INT_STATUS_SUCCESS;
1360 }
1361 
1362 
1363 INTSTATUS
1366  _In_ QWORD Aux
1367  )
1381 {
1382  INTSTATUS status = INT_STATUS_SUCCESS;
1383  QWORD currentDriver = 0;
1384  QWORD moduleList = 0;
1385  DWORD count = 0;
1386 
1387  if (NULL == Callback)
1388  {
1390  }
1391 
1392  status = IntLixDrvFindList(&moduleList);
1393  if (INT_STATUS_NOT_FOUND == status)
1394  {
1395  if (NULL == gLixGuest->InitProcessObj)
1396  {
1397  WARNING("[WARNING] No modules found, and init process didn't started...\n");
1398  }
1399  else if (IntLixTaskGetExecCount() <= 2)
1400  {
1401  WARNING("[WARNING] No modules found, and there are only 2 processes started...\n");
1402  }
1403  else
1404  {
1405  return status;
1406  }
1407 
1409  }
1410  else if (!INT_SUCCESS(status))
1411  {
1412  ERROR("[ERROR] Failed finding the module list: 0x%08x\n", status);
1413  return status;
1414  }
1415 
1416  status = IntKernVirtMemFetchQword(moduleList, &currentDriver);
1417  if (!INT_SUCCESS(status))
1418  {
1419  ERROR("[ERROR] Failed getting the first module from 0x%016llx\n", currentDriver);
1420  return status;
1421  }
1422 
1423  while (currentDriver != moduleList && (count++ < LIX_MODULE_MAX_ITERATIONS))
1424  {
1425  currentDriver -= LIX_FIELD(Module, List);
1426 
1427  status = Callback(currentDriver, Aux);
1428  if (!INT_SUCCESS(status))
1429  {
1430  break;
1431  }
1432 
1433  status = IntKernVirtMemFetchQword(currentDriver + LIX_FIELD(Module, List), &currentDriver);
1434  if (!INT_SUCCESS(status))
1435  {
1436  ERROR("[ERROR] Failed getting the next module from 0x%016llx\n",
1437  currentDriver + LIX_FIELD(Module, List));
1438  break;
1439  }
1440  }
1441 
1442  if (count >= LIX_MODULE_MAX_ITERATIONS)
1443  {
1444  status = INT_STATUS_NOT_SUPPORTED;
1445  }
1446 
1447  return status;
1448 }
1449 
1450 
1451 INTSTATUS
1453  void
1454  )
1461 {
1462  KERNEL_DRIVER *pDriver = HpAllocWithTag(sizeof(*pDriver), IC_TAG_MODU);
1463  if (NULL == pDriver)
1464  {
1466  }
1467 
1468  pDriver->Name = HpAllocWithTag(sizeof("kernel"), IC_TAG_DRNU);
1469  if (NULL == pDriver->Name)
1470  {
1471  HpFreeAndNullWithTag(&pDriver, IC_TAG_MODU);
1473  }
1474 
1475  memcpy(pDriver->Name, "kernel", sizeof("kernel"));
1476  pDriver->NameLength = sizeof("kernel") - 1;
1477 
1478  pDriver->NameHash = Crc32String(pDriver->Name, INITIAL_CRC_VALUE);
1479 
1480  pDriver->BaseVa = gLixGuest->Layout.CodeStart;
1482 
1487 
1488  pDriver->Lix.Initialized = TRUE;
1489 
1490  gGuest.KernelDriver = pDriver;
1491 
1492  InsertTailList(&gKernelDrivers, &pDriver->Link);
1493 
1494  return INT_STATUS_SUCCESS;
1495 }
Measures kernel mode exceptions checks.
Definition: stats.h:51
#define _In_opt_
Definition: intro_sal.h:16
static INTSTATUS IntLixDrvSendEvent(KERNEL_DRIVER *Driver, BOOLEAN Loaded, BOOLEAN StaticDetected)
Send an event to the integrator that contains the information about the provided driver.
Definition: lixmodule.c:435
struct _EVENT_EPT_VIOLATION::@284 Victim
enum _INTRO_ACTION_REASON INTRO_ACTION_REASON
The reason for which an INTRO_ACTION was taken.
INTRO_CODEBLOCKS CodeBlocks
Code blocks extracted for the alert.
Definition: intro_types.h:1263
_Bool BOOLEAN
Definition: intro_types.h:58
#define _Out_
Definition: intro_sal.h:22
BOOLEAN Valid
Set to True if the information in the structure is valid, False otherwise.
Definition: intro_types.h:904
Describes the information about a Linux active-patch.
Definition: lixguest.h:461
INTSTATUS IntVirtMemUnmap(void **HostPtr)
Unmaps a memory range previously mapped with IntVirtMemMap.
Definition: introcore.c:2234
DWORD GplSymbolsCount
The number of GPL-exported symbols (num_gpl_syms).
Definition: lixmodule.h:34
QWORD IntAlertCoreGetFlags(QWORD ProtectionFlag, INTRO_ACTION_REASON Reason)
Returns the flags for an alert.
Definition: alerts.c:366
An internal error occurred (no memory, pages not present, etc.).
Definition: intro_types.h:195
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
static INTSTATUS IntLixDrvValidate(QWORD Driver)
Validates if the provided driver with the provided address is valid.
Definition: lixmodule.c:46
BYTE ByteValues[ND_MAX_REGISTER_SIZE]
Definition: decoder.h:54
Kernel module (ntoskrnl.exe, hal.dll, etc.).
Definition: intro_types.h:238
uint8_t BYTE
Definition: intro_types.h:47
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
BYTE Violation
The type of the access. It must be one of the IG_EPT_HOOK_TYPE values.
Definition: intro_types.h:1265
DWORD RoSize
The size of the .rodata (read-only).
Definition: lixmodule.h:22
IG_ARCH_REGS Regs
The current state of the guest registers.
Definition: guests.h:95
LIX_ACTIVE_PATCH ActivePatch[lixActivePatchCount]
An array that contains information about the active-patches.
Definition: lixguest.h:527
#define _In_
Definition: intro_sal.h:21
MITRE_ID MitreID
The Mitre ID that corresponds to this attack.
Definition: intro_types.h:1199
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
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
void IntLixDrvUpdateProtection(void)
Update Linux drivers protection according to the new core options.
Definition: lixmodule.c:487
QWORD ReturnRip
The RIP at which the code that triggered the alert returns.
Definition: intro_types.h:1282
INTSTATUS IntLixDrvRemoveEntry(KERNEL_DRIVER *Driver)
Disable protection and frees the driver structure from our internal list.
Definition: lixmodule.c:828
#define STATS_EXIT(id)
Definition: stats.h:160
#define IC_TAG_MODU
Loaded module.
Definition: memtags.h:15
INTSTATUS IntKsymFindByAddress(QWORD Gva, DWORD Length, char *SymName, QWORD *SymStart, QWORD *SymEnd)
Finds the symbol which is located at the given address.
Definition: lixksym.c:1283
QWORD BaseVa
The guest virtual address of the kernel module that owns this driver object.
Definition: drivers.h:41
QWORD HookStartPhysical
The start of the monitored guest physical memory area for which this alert was generated.
Definition: intro_types.h:1270
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
Event structure for module loading and unloading.
Definition: intro_types.h:1945
void IntLixDrvGetSecName(KERNEL_DRIVER *Driver, QWORD Gva, CHAR *SectionName)
Get the section of the driver that contains the provided guest virtual address.
Definition: lixmodule.c:913
QWORD Flags
A combination of ALERT_FLAG_* values describing the alert.
Definition: intro_types.h:1198
INTSTATUS IntVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD Cr3, QWORD *Data)
Reads 8 bytes from the guest memory.
Definition: introcore.c:866
#define PAGE_OFFSET
Definition: pgtable.h:32
#define ARRAYSIZE(A)
Definition: introdefs.h:101
void * InitProcessObj
The LIX_TASK_OBJECT of the &#39;init&#39; process.
Definition: lixguest.h:532
EVENT_MODULE_EVENT Module
Definition: alerts.h:26
QWORD IntHookGetGlaFromGpaHook(HOOK_GPA const *Hook, QWORD Address)
Gets the GLA from a GPA hook.
Definition: hook.c:279
WORD Length
The patch length.
Definition: lixguest.h:464
#define INTRO_OPT_PROT_KM_LX_MODULES
Enable Linux kernel modules protection (Linux only).
Definition: intro_types.h:421
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
#define ERROR(fmt,...)
Definition: glue.h:62
static BOOLEAN IntLixDrvIsActivePatch(QWORD Gva)
Checks if the provided guest virtual address is inside an active-patch range.
Definition: lixmodule.c:672
struct _EVENT_EPT_VIOLATION::@283 Originator
#define HpAllocWithTag(Len, Tag)
Definition: glue.h:516
int INTSTATUS
The status data type.
Definition: introstatus.h:24
QWORD Size
The size of the kernel module that owns this driver object.
Definition: drivers.h:43
QWORD GvaPage
Guest virtual page base address, aligned to 4K.
Definition: hook_gva.h:32
QWORD CodeEnd
The guest virtual address where the code ends.
Definition: lixguest.h:498
QWORD CodeStart
The guest virtual address where the code starts.
Definition: lixguest.h:497
int IntLixGuestGetSystemState(void)
Get the system state of the Linux guest.
Definition: lixguest.c:2201
LIX_MODULE_LAYOUT CoreLayout
The layout of the core section.
Definition: lixmodule.h:40
LIX_MODULE_LAYOUT InitLayout
The layout of the init section.
Definition: lixmodule.h:39
void * InitSwapHook
The hook on the init section.
Definition: lixmodule.h:31
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
Rootkit.
Definition: intro_types.h:1144
Describes a kernel-mode originator.
Definition: exceptions.h:943
static INTSTATUS IntLixDrvInitVfreeHandler(void *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize)
This function is called when the init section of the driver is freed.
Definition: lixmodule.c:372
void IntAlertFillCpuContext(BOOLEAN CopyInstruction, INTRO_CPUCTX *CpuContext)
Fills the current CPU context for an alert.
Definition: alerts.c:492
INSTRUX Instruction
The current instruction, pointed by the guest RIP.
Definition: guests.h:88
#define _Out_writes_(expr)
Definition: intro_sal.h:28
LIST_HEAD gKernelDrivers
List of all the drivers currently loaded inside the guest.
Definition: drivers.c:11
#define MIN(a, b)
Definition: introdefs.h:146
#define LIX_MODULE_MAP_START
Module mapping space, as defined by linux kernel (mm.txt)
Definition: lixmodule.c:39
QWORD HookStartVirtual
The start of the monitored guest virtual memory area for which this alert was generated.
Definition: intro_types.h:1268
EVENT_EPT_VIOLATION Ept
Definition: alerts.h:16
#define LIX_MODULE_NAME_LEN
The maximum length of the Linux module name.
Definition: lixmodule.h:11
static void IntLixDrvSendViolationEvent(KERNEL_DRIVER *Driver, EXCEPTION_KM_ORIGINATOR *Originator, EXCEPTION_VICTIM_ZONE *Victim, HOOK_GPA *Hook, QWORD Address, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Sends an introEventEptViolation event for a protected kernel module.
Definition: lixmodule.c:1119
_MODULE_STATE
The state of a kernel module.
Definition: lixmodule.c:27
QWORD GplSymbols
The GVA of the exported gpl symbols (gpl_syms).
Definition: lixmodule.h:37
#define LOG(fmt,...)
Definition: glue.h:61
DWORD gModuleIgnore
Used to count the modules that are unloading.
Definition: lixmodule.c:21
Describes a kernel driver.
Definition: drivers.h:30
INTSTATUS IntHookGvaSetHook(QWORD Cr3, QWORD Gva, DWORD Length, BYTE Type, void *Callback, void *Context, void *ParentHook, DWORD Flags, HOOK_GVA **GvaHook)
Set a read, write, execute or swap hook on a guest virtual address.
Definition: hook_gva.c:345
void IntAlertFillVersionInfo(INTRO_VIOLATION_HEADER *Header)
Fills version information for an alert.
Definition: alerts.c:327
DWORD AccessSize
The size of the memory access. Valid only for EPT exits.
Definition: guests.h:103
INTRO_VIOLATION_HEADER Header
The alert header.
Definition: intro_types.h:1217
INTSTATUS IntLixDrvRemoveFromAddress(QWORD DriverGva)
Disable protection and remove the driver structure from our internal list.
Definition: lixmodule.c:866
The module is still setting it up.
Definition: lixmodule.c:32
QWORD ZoneTypes
The types of the accessed memory area.
Definition: intro_types.h:1276
DWORD NameHash
The hash of the name.
Definition: drivers.h:59
static INTSTATUS IntLixDrvCreateDriverObject(QWORD DriverGva, KERNEL_DRIVER **Object)
Create a KERNEL_DRIVER object that contains the information found at the address of the &#39;struct modul...
Definition: lixmodule.c:558
INTRO_ACTION_REASON Reason
The reason for which Action was taken.
Definition: intro_types.h:1195
QWORD ExTableStart
The guest virtual address where the ex-table starts.
Definition: lixguest.h:506
INTSTATUS IntAlertFillCodeBlocks(QWORD Rip, QWORD Cr3, BOOLEAN Execute, INTRO_CODEBLOCKS *CodeBlocks)
Fills the code blocks pattern for an alert.
Definition: alerts.c:71
GENERIC_ALERT gAlert
Global alert buffer.
Definition: alerts.c:27
static void IntLixDrvRemoveDuplicate(QWORD DriverGva)
Removes the driver with the provided guest virtual address if exists in our list. ...
Definition: lixmodule.c:514
void IntAlertFillLixKmModule(const KERNEL_DRIVER *Driver, INTRO_MODULE *EventModule)
Saves information about a kernel module inside an alert.
Definition: alerts.c:1235
DWORD Size
The total size of the section.
Definition: lixmodule.h:20
INTSTATUS IntKernVirtMemFetchDword(QWORD GuestVirtualAddress, DWORD *Data)
Reads 4 bytes from the guest kernel memory.
Definition: introcore.c:829
QWORD DataStart
The guest virtual address where the data starts.
Definition: lixguest.h:500
#define INITIAL_CRC_VALUE
Definition: introdefs.h:221
#define INT_STATUS_EXCEPTION_BLOCK
Definition: introstatus.h:421
Describes an operand value.
Definition: decoder.h:50
void IntAlertEptFillFromVictimZone(const EXCEPTION_VICTIM_ZONE *Victim, EVENT_EPT_VIOLATION *EptViolation)
Fills the victim information inside an EPT alert.
Definition: alerts.c:868
INTSTATUS IntKernVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD *Data)
Reads 8 bytes from the guest kernel memory.
Definition: introcore.c:811
static BOOLEAN IntLixDrvSystemBooting(void)
Checks if the system is booting.
Definition: lixmodule.c:1206
SIZE_T NameLength
The length of the Name. This is the number of characters in the Name buffer.
Definition: drivers.h:56
#define STATS_ENTER(id)
Definition: stats.h:153
static INTSTATUS IntLixDrvActivateProtection(KERNEL_DRIVER *Driver)
Activates protection for the provided driver.
Definition: lixmodule.c:144
struct _LINUX_GUEST::@126 Layout
INTSTATUS IntLixDrvCreateKernel(void)
Create the KERNEL_DRIVER object for the operating system kernel and activate the protection for it...
Definition: lixmodule.c:1452
INTRO_CPUCTX CpuContext
The context of the CPU that triggered the alert.
Definition: intro_types.h:1196
INTSTATUS IntNotifyIntroEvent(INTRO_EVENT_TYPE EventClass, void *Param, size_t EventSize)
Notifies the integrator about an introspection alert.
Definition: glue.c:1042
static BOOLEAN RemoveEntryList(LIST_ENTRY *Entry)
Definition: introlists.h:87
#define IN_RANGE(x, start, end)
Definition: introdefs.h:167
#define memzero(a, s)
Definition: introcrt.h:35
INTSTATUS IntExceptGetVictimEpt(void *Context, QWORD Gpa, QWORD Gva, INTRO_OBJECT_TYPE Type, DWORD ZoneFlags, EXCEPTION_VICTIM_ZONE *Victim)
Fills an EXCEPTION_VICTIM_ZONE with relevant information from an EPT violation.
Definition: exceptions.c:742
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:236
INTSTATUS IntTranslateVirtualAddress(QWORD Gva, QWORD Cr3, QWORD *PhysicalAddress)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1999
#define IN_RANGE_LEN(x, start, len)
Definition: introdefs.h:175
#define TRUE
Definition: intro_types.h:30
INTSTATUS(* PFUNC_IterateListCallback)(QWORD Node, QWORD Aux)
Definition: introtypes.h:71
#define INT_STATUS_INVALID_PARAMETER_4
Definition: introstatus.h:71
union _OPERAND_VALUE::@22 Value
The actual operand value.
The module is full formed, running module_init.
Definition: lixmodule.c:30
static void IntLixDrvDeactivateProtection(KERNEL_DRIVER *Driver)
Disable protection for the provided driver.
Definition: lixmodule.c:214
#define LIX_FIELD(Structure, Field)
Macro used to access fields inside the LIX_OPAQUE_FIELDS structure.
Definition: lixguest.h:429
#define HpFreeAndNullWithTag(Add, Tag)
Definition: glue.h:517
#define TRACE(fmt,...)
Definition: glue.h:58
#define INT_STATUS_INVALID_PARAMETER_5
Definition: introstatus.h:74
#define INTRO_OPT_EVENT_MODULES
Enable user mode and kernel mode module load and unload events (generates introEventModuleEvent event...
Definition: intro_types.h:445
void * Name
The name of the driver.
Definition: drivers.h:54
#define PD_RW
Definition: pgtable.h:72
void IntExceptKernelLogInformation(EXCEPTION_VICTIM_ZONE *Victim, EXCEPTION_KM_ORIGINATOR *Originator, INTRO_ACTION Action, INTRO_ACTION_REASON Reason)
Print the information about a kernel-mode violation and dumps the code-blocks.
Kernel-mode exception.
Definition: exceptions.h:61
CHAR FunctionName[ALERT_MAX_FUNCTION_NAME_LEN]
The name of the modified function, if any. This is the same as Export.Name[0].
Definition: intro_types.h:1289
INTSTATUS IntLixDrvHandleWrite(void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Called if an write occurs on the protected memory zone.
Definition: lixmodule.c:1236
static void InsertTailList(LIST_ENTRY *ListHead, LIST_ENTRY *Entry)
Definition: introlists.h:135
INTRO_EXEC_CONTEXT ExecContext
Information about the instruction that triggered the alert.
Definition: intro_types.h:1309
BOOLEAN Loaded
True if the module was loaded, False if it was unloaded.
Definition: intro_types.h:1948
#define INT_STATUS_ALREADY_INITIALIZED_HINT
Definition: introstatus.h:323
LIX_KERNEL_MODULE Lix
Valid only for Linux guests.
Definition: drivers.h:71
#define WARNING(fmt,...)
Definition: glue.h:60
INTSTATUS IntLixDrvFindList(QWORD *Drivers)
Searches the Linux kernel for the &#39;modules&#39; variable.
Definition: lixmodule.c:244
INTSTATUS IntHookGvaRemoveHook(HOOK_GVA **Hook, DWORD Flags)
Remove a GVA hook.
Definition: hook_gva.c:507
Sent when an EPT violation triggers an alert. See EVENT_EPT_VIOLATION.
Definition: intro_types.h:84
#define LIX_SYMBOL_NAME_LEN
The max length of the ksym as defined by Linux kernel.
Definition: lixguest.h:585
#define PAGE_SIZE
Definition: common.h:70
Describes the modified zone.
Definition: exceptions.h:893
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
DWORD IntLixTaskGetExecCount(void)
Returns the number of processes that have performed an exec.
Definition: lixprocess.c:1077
Exception Table (Linux-only).
Definition: intro_types.h:258
uint32_t DWORD
Definition: intro_types.h:49
INTSTATUS IntLixDrvIsLegitimateTextPoke(void *Hook, QWORD Address, LIX_ACTIVE_PATCH *ActivePatch, INTRO_ACTION *Action)
This function checks if the modified zone by the current instruction is a &#39;text_poke&#39;.
Definition: lixmodule.c:980
QWORD ProtectionFlag
The introcore option that decided that this driver must be protected.
Definition: drivers.h:49
#define strlen_s(s, n)
Definition: introcrt.h:34
INTRO_PROCESS CurrentProcess
The currently active process.
Definition: intro_types.h:1957
QWORD KernelSymbols
The GVA of the exported symbols (syms).
Definition: lixmodule.h:36
QWORD DataEnd
The guest virtual address where the data ends.
Definition: lixguest.h:501
enum _INTRO_ACTION INTRO_ACTION
Event actions.
LIST_ENTRY Link
Entry inside the gKernelDrivers list.
Definition: drivers.h:33
#define INT_STATUS_INVALID_OBJECT_TYPE
Definition: introstatus.h:145
No access type. This can be used for swap hooks.
Definition: glueiface.h:297
INTRO_MODULE Module
The module for which this event was triggered.
Definition: intro_types.h:1960
QWORD RoDataEnd
The guest virtual address where the read-only data ends.
Definition: lixguest.h:504
DWORD Offset
The offset inside the page where the violation took place.
Definition: intro_types.h:1274
__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
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
enum _MODULE_STATE MODULE_STATE
The state of a kernel module.
INTRO_ACTION Action
The action that was taken as the result of this alert.
Definition: intro_types.h:1194
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
CHAR ModifiedSectionName[ALERT_MAX_SECTION_NAME_LEN]
The name of the modified section, if any.
Definition: intro_types.h:1287
#define LIX_MODULE_MAX_ITERATIONS
Definition: lixmodule.c:42
INTSTATUS IntLixDrvCreateFromAddress(QWORD DriverGva, QWORD StaticDetected)
Create the KERNEL_DRIVER object from the provided &#39;module struct&#39; address and activate the protection...
Definition: lixmodule.c:696
QWORD VirtualPage
The guest virtual page in which the access was made.
Definition: intro_types.h:1272
INTSTATUS IntHookObjectHookRegion(void *Object, QWORD Cr3, QWORD Gla, SIZE_T Length, BYTE Type, void *Callback, void *Context, DWORD Flags, HOOK_REGION_DESCRIPTOR **Region)
Hook a contiguous region of virtual memory inside the provided virtual address space.
Definition: hook_object.c:132
#define INT_STATUS_NOT_INITIALIZED_HINT
Definition: introstatus.h:320
#define __likely(x)
Definition: common.h:63
KERNEL_DRIVER * KernelDriver
Points to the driver object that describes the kernel image.
Definition: guests.h:385
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
INTRO_PROCESS CurrentProcess
The current process.
Definition: intro_types.h:1197
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
INTSTATUS IntDecGetWrittenValueFromInstruction(PINSTRUX Instrux, PIG_ARCH_REGS Registers, PBYTE MemoryValue, OPERAND_VALUE *WrittenValue)
Decode a written value from a memory write instruction.
Definition: decoder.c:1861
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
INTRO_MODULE Module
The module that did the malicious access.
Definition: intro_types.h:1221
The module is running.
Definition: lixmodule.c:29
#define PD_P
Definition: pgtable.h:71
BOOLEAN Initialized
This means that the init section is discarded.
Definition: lixmodule.h:45
QWORD Base
The base GVA of the section.
Definition: lixmodule.h:19
CHAR RipSectionName[ALERT_MAX_SECTION_NAME_LEN]
The name of the section in which the RIP resides. May be empty.
Definition: intro_types.h:1279
Event structure for EPT violations.
Definition: intro_types.h:1215
QWORD EntryPoint
The entry point of this driver.
Definition: drivers.h:45
#define IC_TAG_DRNU
Guest loaded module name buffer (Unicode)
Definition: memtags.h:11
#define UNREFERENCED_LOCAL_VARIABLE(V)
Definition: introdefs.h:30
void IntAlertFillLixCurrentProcess(INTRO_PROCESS *EventProcess)
Saves the current Linux process inside an event.
Definition: alerts.c:1310
QWORD Gva
The start of the region which follows to be patched.
Definition: lixguest.h:463
char CHAR
Definition: intro_types.h:56
#define list_for_each(_head, _struct_type, _var)
Definition: introlists.h:41
INTSTATUS IntLixDrvIterateList(PFUNC_IterateListCallback Callback, QWORD Aux)
Iterates the &#39;modules&#39; list form the guest and activate protection for each driver that is initialize...
Definition: lixmodule.c:1364
DWORD SymbolsCount
The number of symbols (num_syms).
Definition: lixmodule.h:33
#define INT_STATUS_REMOVE_HOOK_ON_RET
Can be used by hook callbacks in order to signal that the hook should be removed. ...
Definition: introstatus.h:343
Write-access hook.
Definition: glueiface.h:299
#define PAGE_MASK
Definition: pgtable.h:35
QWORD ObjectGva
The guest virtual address at which this object resides.
Definition: drivers.h:39
#define LIX_MODULE_MAP_END
Definition: lixmodule.c:40
INTSTATUS IntExceptKernelGetOriginator(EXCEPTION_KM_ORIGINATOR *Originator, DWORD Options)
This function is used to get the information about the kernel-mode originator.
INTSTATUS IntAlertFillExecContext(QWORD Cr3, INTRO_EXEC_CONTEXT *ExecContext)
Fills the current execution context.
Definition: alerts.c:31
#define ZONE_WRITE
Used for write violation.
Definition: exceptions.h:734
#define INT_STATUS_INVALID_PARAMETER_2
Definition: introstatus.h:65
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271
QWORD Gla
The accessed guest virtual address. Valid only for EPT exits.
Definition: guests.h:102
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
BOOLEAN Protected
True if the module is protected.
Definition: intro_types.h:1950
INTRO_MODULE ReturnModule
The module to which the current code returns to.
Definition: intro_types.h:1222
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281
The module is going away.
Definition: lixmodule.c:31
DWORD TextSize
The size of the .text (code usually).
Definition: lixmodule.h:21
#define INT_STATUS_INVALID_PARAMETER_3
Definition: introstatus.h:68