Bitdefender Hypervisor Memory Introspection
lixguest.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Bitdefender
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #include "lixguest.h"
6 #include "cr_protection.h"
7 #include "decoder.h"
8 #include "drivers.h"
9 #include "hook.h"
10 #include "introcpu.h"
11 #include "lixapi.h"
12 #include "lixidt.h"
13 #include "lixkernel.h"
14 #include "lixmm.h"
15 #include "lixvdso.h"
16 #include "msr_protection.h"
17 #include "dtr_protection.h"
18 #include "lixfiles.h"
19 #include "lixksym.h"
20 #include "memcloak.h"
21 
23 #include "detours_hypercall.h"
24 
31 
33 
38 
40 #define LIX_KERNEL_MAX_PAGES 16384
41 
42 #define LIX_BANNER_START "Linux version "
43 
44 #define LIX_MODULE_MAPPING_SPACE_START 0xffffffffa0000000
45 #define LIX_MODULE_MAPPING_SPACE_END 0xfffffffffeffffff
46 
47 #define LIX_KAISER_ENABLED_PCP_OFFSET_CAP 0xE000UL
49 
50 
51 static void
53  void
54  )
58 {
59  gGuest.OSVersion = ((gLixGuest->Version.Version & 0xFF) << 24);
60  gGuest.OSVersion |= ((gLixGuest->Version.Patch & 0xFF) << 16);
61  gGuest.OSVersion |= (gLixGuest->Version.Sublevel & 0xFFFF);
62 }
63 
64 
65 static INTSTATUS
67  _In_reads_z_(BufferLength) const char *Buffer,
68  _In_ DWORD BufferLength
69  )
81 {
82  DWORD start, end;
83  WORD v[3];
84  char c[5];
85  BOOLEAN hasBackport;
86 
87  start = end = 0;
88  hasBackport = FALSE;
89  for (DWORD i = 0; i < 3; i++)
90  {
91  BOOLEAN found = FALSE;
92  while (end < BufferLength && Buffer[end])
93  {
94  // See the 'linux_proc_banner' for more info
95  if ('.' == Buffer[end] || ' ' == Buffer[end] || '-' == Buffer[end] || '+' == Buffer[end])
96  {
97  found = TRUE;
98  hasBackport = '-' == Buffer[end];
99  break;
100  }
101  else if (Buffer[end] < '0' || Buffer[end] > '9')
102  {
103  return INT_STATUS_NOT_FOUND;
104  }
105 
106  end++;
107  }
108 
109  if (!found)
110  {
111  return INT_STATUS_NOT_FOUND;
112  }
113 
114  if (end - start >= sizeof(c))
115  {
116  WARNING("[WARNING] Version number too big (%d/%zu)\n", end - start, sizeof(c));
117  return INT_STATUS_NOT_FOUND;
118  }
119 
120  memcpy(c, &Buffer[start], end - start);
121  c[end - start] = 0;
122  v[i] = (WORD)strtol(c, NULL, 0);
123 
124  ++end;
125  start = end;
126  }
127 
128  gLixGuest->Version.Version = (BYTE)v[0];
129  gLixGuest->Version.Patch = (BYTE)v[1];
130  gLixGuest->Version.Sublevel = v[2];
131 
132  if (!hasBackport)
133  {
134  TRACE("[LIXGUEST] No backport info!");
135 
136  gLixGuest->Version.Backport = 0;
137  goto _log_and_exit;
138  }
139 
140  start = end;
141  while (end < BufferLength && Buffer[end] >= '0' && Buffer[end] <= '9')
142  {
143  ++end;
144  }
145 
146  if (end - start >= sizeof(c))
147  {
148  WARNING("[WARNING] Backport number too big (%d/%zu)\n", end - start, sizeof(c));
149 
150  gLixGuest->Version.Backport = 0;
151  goto _log_and_exit;
152  }
153 
154  memset(c, 0, sizeof(c));
155  memcpy(c, &Buffer[start], end - start);
156  gLixGuest->Version.Backport = (WORD)strtol(c, NULL, 0);
157 
158 _log_and_exit:
159 
160 
161  TRACE("[LIXGUEST] We run kernel version %d.%d.%d-%d (%08x)\n",
162  gLixGuest->Version.Version,
163  gLixGuest->Version.Patch,
164  gLixGuest->Version.Sublevel,
165  gLixGuest->Version.Backport,
166  gLixGuest->Version.Value);
167 
169 
170  return INT_STATUS_SUCCESS;
171 }
172 
173 
174 INTSTATUS
176  _In_ QWORD StartGva
177  )
194 {
195  INTSTATUS status;
196  QWORD gva = StartGva & PAGE_MASK;
197  DWORD pageCount = 0;
198  BOOLEAN versionFound = FALSE;
199 
200  // Now find the version and the size. Skip pages which are not present until we find the linux banner.
201  // We already counted how many pages are to the beginning, now count how many are to the end.
202  gva = StartGva & PAGE_MASK;
203  while (pageCount < LIX_KERNEL_MAX_PAGES)
204  {
205  char *pPage;
206  DWORD parsed, size;
207  VA_TRANSLATION tr;
208 
209  // If translation fails we stop. We only continue if the page is not present and we haven't found
210  // the Linux banner yet.
211  status = IntTranslateVirtualAddressEx(gva, gGuest.Mm.SystemCr3, 0, &tr);
212  if (!INT_SUCCESS(status))
213  {
214  break;
215  }
216 
217  if (0 == (tr.Flags & PT_P))
218  {
219  if (versionFound)
220  {
221  // We have the version, this should be the end of kernel.
222  break;
223  }
224 
225  // Continue searching
226  gva += PAGE_SIZE;
227  pageCount++;
228  continue;
229  }
230 
231  if (versionFound)
232  {
233  // No need to skip only one page if we already have the version.
234  gva += tr.PageSize;
235  pageCount += (DWORD)(tr.PageSize / PAGE_SIZE);
236  continue;
237  }
238 
239  status = IntPhysMemMap(tr.PhysicalAddress, PAGE_SIZE, 0, &pPage);
240  if (!INT_SUCCESS(status))
241  {
242  ERROR("[ERROR] IntPhysMemMap failed for %016llx (%016llx): 0x%08x\n", tr.PhysicalAddress, gva, status);
243  break;
244  }
245 
246  for (parsed = 0; parsed + sizeof(LIX_BANNER_START) < PAGE_SIZE; parsed++)
247  {
248  DWORD verMax = parsed;
249  BOOLEAN verMaxFound = FALSE;
250 
251  if (0 != strncmp(&pPage[parsed], LIX_BANNER_START, sizeof(LIX_BANNER_START) - 1))
252  {
253  continue;
254  }
255 
256  TRACE("[LIXGUEST] Found a 'Linux version ' at %llx. The start of rodata is at %llx\n", gva + parsed, gva);
257 
258  status = IntLixGuestParseVersion(pPage + parsed + CSTRLEN(LIX_BANNER_START),
259  PAGE_SIZE - parsed - CSTRLEN(LIX_BANNER_START));
260  if (!INT_SUCCESS(status))
261  {
262  continue;
263  }
264 
265  // Find out if the linux_proc_banner ends in the same page (it should!), and print it.
266  // If it doesn't, just skip it for now...
267  while (verMax < PAGE_SIZE)
268  {
269  if (pPage[verMax] == '\0')
270  {
271  LOG("[LIXGUEST] Linux version complete: %s\n", &pPage[parsed]);
272 
273  verMaxFound = TRUE;
274  verMax++; // Include the NULL-terminator
275 
276  break;
277  }
278 
279  verMax++;
280  }
281 
282  if (!verMaxFound)
283  {
284  continue;
285  }
286 
287  size = verMax - parsed + sizeof(LIX_BANNER_START) - 1;
288 
289  if (size > sizeof(gLixGuest->VersionString))
290  {
291  memcpy(gLixGuest->VersionString,
292  &pPage[parsed + sizeof(LIX_BANNER_START) - 1],
293  sizeof(gLixGuest->VersionString) - 1);
294  }
295  else
296  {
297  // Already includes the NULL terminator
298  memcpy(gLixGuest->VersionString, &pPage[parsed + sizeof(LIX_BANNER_START) - 1], size);
299  }
300 
301  gLixGuest->Layout.RoDataStart = gva;
302 
303  // Temporary set, until we find kallsyms
304  gLixGuest->Layout.CodeEnd = gva;
305 
306  versionFound = TRUE;
307  }
308 
309  IntPhysMemUnmap(&pPage);
310 
311  gva += PAGE_SIZE;
312  pageCount++;
313  }
314 
315  if (!versionFound)
316  {
317  WARNING("[WARNING] Could not find kernel version! Retry ...");
318  return INT_STATUS_NOT_FOUND;
319  }
320 
321  gLixGuest->Layout.RoDataEnd = gva;
322 
323  return INT_STATUS_SUCCESS;
324 }
325 
326 
327 static INTSTATUS
329  _In_ QWORD StartGva
330  )
346 {
347  INTSTATUS status;
348  QWORD kernelBase = StartGva & PAGE_MASK;
349  DWORD pageCount = 0;
350  void *p = NULL;
351 
352  status = IntVirtMemMap(kernelBase, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &p);
353  if (!INT_SUCCESS(status))
354  {
355  ERROR("[ERROR] IntVirtMemMap failed for %016llx: %08x\n", kernelBase, status);
356  return status;
357  }
358 
359  while (SIG_NOT_FOUND == IntPatternMatch(p, gLinuxDistSigsCount, gLinuxDistSigs) &&
360  pageCount++ < LIX_KERNEL_MAX_PAGES)
361  {
362  kernelBase -= PAGE_SIZE;
363 
364  IntVirtMemUnmap(&p);
365 
366  status = IntVirtMemMap(kernelBase, PAGE_SIZE, gGuest.Mm.SystemCr3, 0, &p);
367  if (!INT_SUCCESS(status))
368  {
369  ERROR("[ERROR] IntVirtMemMap failed for %016llx: %08x\n", kernelBase, status);
370  return status;
371  }
372  }
373 
374  IntVirtMemUnmap(&p);
375 
376  if (pageCount >= LIX_KERNEL_MAX_PAGES)
377  {
378  ERROR("[ERROR] Failed finding the base of the kernel, bailing out...\n");
379  return INT_STATUS_NOT_FOUND;
380  }
381 
382  gGuest.KernelVa = kernelBase;
383 
384  gLixGuest->Layout.CodeStart = kernelBase;
385 
386  return INT_STATUS_SUCCESS;
387 }
388 
389 
390 static INTSTATUS
392  _In_ QWORD SyscallHandler
393  )
407 {
408  INTSTATUS status;
409 
410  if (!IS_KERNEL_POINTER_LIX(SyscallHandler))
411  {
413  }
414 
416  if (!INT_SUCCESS(status))
417  {
418  ERROR("[ERROR] Could not load dist sigs from update buffer.");
420  }
421 
422  status = IntLixGuestFindKernelVersionAndRo(SyscallHandler + PAGE_SIZE);
423  if (!INT_SUCCESS(status))
424  {
425  WARNING("[WARNING] IntLixGuestFindKernelVersionAndRo failed for syscall %llx: %08x\n", SyscallHandler, status);
426  return status;
427  }
428 
429  status = IntLixGuestFindKernelBase(SyscallHandler);
430  if (INT_SUCCESS(status))
431  {
433  }
434 
435  HpFreeAndNullWithTag(&gLinuxDistSigs, IC_TAG_CAMI);
436 
437  return status;
438 }
439 
440 
441 static INTSTATUS
443  void
444  )
456 {
457  QWORD funcStart, funcEnd;
458  INTSTATUS status;
459  INSTRUX instrux;
460  QWORD addrs[2] = { 0 };
461 
462  funcStart = IntKsymFindByName("search_exception_tables", &funcEnd);
463  if (!funcStart)
464  {
465  ERROR("[ERROR] IntKsymFindByName could not find search_exception_tables\n");
466  return INT_STATUS_NOT_FOUND;
467  }
468 
469  while (funcStart < funcEnd)
470  {
471  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, funcStart, &instrux);
472  if (!INT_SUCCESS(status))
473  {
474  ERROR("[ERROR] IntDecDecodeInstruction failed: %08x\n", status);
475  return status;
476  }
477 
478  funcStart += instrux.Length;
479 
480  if (ND_INS_MOV != instrux.Instruction || ND_OP_IMM != instrux.Operands[1].Type)
481  {
482  continue;
483  }
484 
485  if (0 == addrs[0])
486  {
487  addrs[0] = SIGN_EX_32(instrux.Operands[1].Info.Immediate.Imm);
488  }
489  else
490  {
491  addrs[1] = SIGN_EX_32(instrux.Operands[1].Info.Immediate.Imm);
492  break;
493  }
494  }
495 
496  if (!addrs[1])
497  {
498  return INT_STATUS_NOT_FOUND;
499  }
500 
501  gLixGuest->Layout.ExTableStart = ((addrs[0] < addrs[1]) ? addrs[0] : addrs[1]);
502  gLixGuest->Layout.ExTableEnd = ((addrs[0] > addrs[1]) ? addrs[0] : addrs[1]);
503 
504  // ExTableEnd points to the last entry of the exception table.
505  // Since this function will be called only on Debian 8, we can assume that sizeof(struct exception_table_entry) == 8.
506  // On newer kernels they added another int field so the sizeof became 12.
507 
508  gLixGuest->Layout.ExTableEnd += 8;
509 
510  return INT_STATUS_SUCCESS;
511 }
512 
513 
514 static void
516  void
517  )
521 {
522  const char *memoryFuncs[] =
523  {
524  "memcpy",
525  "__memcpy",
526  "memset",
527  "__memset",
528  "memmove"
529  };
530 
531  STATIC_ASSERT(ARRAYSIZE(memoryFuncs) == ARRAYSIZE(gLixGuest->MemoryFunctions), "These two should be equal...");
532 
533  for (DWORD i = 0; i < ARRAYSIZE(memoryFuncs); i++)
534  {
535  gLixGuest->MemoryFunctions[i].Start = IntKsymFindByName(memoryFuncs[i], &gLixGuest->MemoryFunctions[i].End);
536  }
537 }
538 
539 
540 static INTSTATUS
542  void
543  )
553 {
554  QWORD gva, ksymEnd;
555 
556  gva = IntKsymFindByName("do_exit", &ksymEnd);
557  if (!gva)
558  {
559  ERROR("[ERROR] IntKsymFindByName could not find do_exit\n");
561  }
562 
563  while (gva < ksymEnd)
564  {
565  INSTRUX instrux;
566 
567  INTSTATUS status = IntDecDecodeInstruction(IG_CS_TYPE_64B, gva, &instrux);
568  if (!INT_SUCCESS(status))
569  {
570  ERROR("[ERROR] Failed decoding instruction at 0x%016llx: %08x\n", gva, status);
571  return status;
572  }
573 
574  gva += instrux.Length;
575 
576  if (instrux.Instruction == ND_INS_MOV &&
577  instrux.OperandsCount == 2 &&
578  instrux.Operands[1].Type == ND_OP_MEM &&
579  instrux.Seg == ND_PREFIX_G2_SEG_GS)
580  {
581  gLixGuest->OsSpecificFields.CurrentTaskOffset = instrux.Displacement;
582  LOG("[OFFSETS] 'current' gs offset: 0x%x\n", gLixGuest->OsSpecificFields.CurrentTaskOffset);
583 
584  return INT_STATUS_SUCCESS;
585  }
586  }
587 
588  return INT_STATUS_NOT_FOUND;
589 }
590 
591 
592 static INTSTATUS
594  void
595  )
608 {
609  QWORD cpuNumberAddress;
610 
611  cpuNumberAddress = IntKsymFindByName("cpu_number", NULL);
612  if (!cpuNumberAddress)
613  {
614  QWORD gva, functionEnd;
615  INSTRUX instrux;
616 
617  LOG("[WARNING] Failed finding 'cpu_number' will try with xen_halt");
618 
619  gva = IntKsymFindByName("xen_halt", &functionEnd);
620  if (!gva)
621  {
622  WARNING("[WARNING] IntKsymFindByName could not find xen_halt\n");
623  return INT_STATUS_NOT_FOUND;
624  }
625 
626  while (gva < functionEnd)
627  {
628  INTSTATUS status = IntDecDecodeInstruction(IG_CS_TYPE_64B, gva, &instrux);
629  if (!INT_SUCCESS(status))
630  {
631  ERROR("[ERROR] IntDecDecodeInstruction failed at %llx: %08x\n", gva, status);
632  return status;
633  }
634 
635  gva += instrux.Length;
636 
637  if (instrux.Instruction == ND_INS_MOV &&
638  instrux.OperandsCount == 2 &&
639  instrux.Operands[1].Type == ND_OP_MEM &&
640  instrux.Seg == ND_PREFIX_G2_SEG_GS)
641  {
642  DWORD cpuNumberOffset = instrux.Displacement;
643 
644  if (instrux.IsRipRelative)
645  {
646  cpuNumberOffset += (DWORD)gva;
647  }
648 
649  gLixGuest->OsSpecificFields.CurrentCpuOffset = cpuNumberOffset;
650 
651  LOG("[OFFSETS] 'current cpu' gs offset: 0x%x\n", cpuNumberOffset);
652 
653  return INT_STATUS_SUCCESS;
654  }
655  }
656  }
657  else
658  {
659  gLixGuest->OsSpecificFields.CurrentCpuOffset = (DWORD)cpuNumberAddress;
660  }
661 
662  return INT_STATUS_SUCCESS;
663 }
664 
665 
666 static INTSTATUS
668  void
669  )
680 {
681  INTSTATUS status;
682  QWORD gva, ksymEnd;
683 
684  gva = IntKsymFindByName("set_tls_desc", &ksymEnd);
685  if (!gva)
686  {
687  ERROR("[ERROR] IntKsymFindByName could not find set_tls_desc\n");
688  return INT_STATUS_NOT_FOUND;
689  }
690 
691  while (gva < ksymEnd)
692  {
693  INSTRUX instrux;
694 
695  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, gva, &instrux);
696  if (!INT_SUCCESS(status))
697  {
698  ERROR("[ERROR] IntDecDecodeInstruction failed at %llx: %08x\n", gva, status);
699  return status;
700  }
701 
702  if (instrux.Instruction == ND_INS_ADD &&
703  instrux.OperandsCount == 3 &&
704  instrux.Operands[1].Type == ND_OP_IMM &&
705  instrux.Operands[0].Type == ND_OP_REG &&
706  instrux.Operands[0].Info.Register.Reg == NDR_RDI)
707  {
708  if (instrux.Operands[1].Info.Immediate.Imm < PAGE_SIZE * 3)
709  {
710  gLixGuest->OsSpecificFields.ThreadStructOffset = (DWORD)(instrux.Operands[1].Info.Immediate.Imm);
711  LOG("[OFFSETS] 'thread_struct' offset (task_struct): 0x%x\n",
713 
714  return INT_STATUS_SUCCESS;
715  }
716  else
717  {
718  WARNING("[WARNING] Candidate 'thread_struct' offset (0x%lx) is bigger than 0x%x ...\n",
719  instrux.Operands[1].Info.Immediate.Imm, PAGE_SIZE * 3);
720  }
721  }
722 
723  gva += instrux.Length;
724  }
725 
726  return INT_STATUS_NOT_FOUND;
727 }
728 
729 
730 static INTSTATUS
732  void
733  )
745 {
746  INTSTATUS status;
747  QWORD gva, ksymEnd;
748  DWORD paramReg = NDR_RDI;
749 
750  gva = IntKsymFindByName("get_mm_exe_file", &ksymEnd);
751  if (!gva)
752  {
753  WARNING("[WARNING] IntKsymFindByName could not find get_mm_exe_file\n");
754  return INT_STATUS_NOT_FOUND;
755  }
756 
757  while (gva < ksymEnd)
758  {
759  INSTRUX instrux;
760 
761  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, gva, &instrux);
762  if (!INT_SUCCESS(status))
763  {
764  ERROR("[ERROR] IntDecDecodeInstruction failed at %llx: %08x\n", gva, status);
765  return status;
766  }
767 
768  // Maybe it saves RDI into another one
769  if (instrux.Instruction == ND_INS_MOV &&
770  instrux.OperandsCount == 2 &&
771  instrux.Operands[0].Type == ND_OP_REG &&
772  instrux.Operands[1].Type == ND_OP_REG &&
773  (instrux.Operands[1].Info.Register.Reg == NDR_RDI ||
774  instrux.Operands[1].Info.Register.Reg == paramReg))
775  {
776  paramReg = instrux.Operands[0].Info.Register.Reg;
777  }
778 
779  if (instrux.Instruction == ND_INS_MOV &&
780  instrux.OperandsCount == 2 &&
781  instrux.Operands[1].Type == ND_OP_MEM &&
782  instrux.Operands[1].Info.Memory.HasBase &&
783  instrux.Operands[1].Info.Memory.HasDisp &&
784  (instrux.Operands[1].Info.Memory.Base == NDR_RDI ||
785  instrux.Operands[1].Info.Memory.Base == paramReg) &&
786  instrux.Operands[1].Info.Memory.Disp < PAGE_SIZE &&
787  instrux.Operands[0].Type == ND_OP_REG &&
788  instrux.Operands[0].Info.Register.Type == ND_REG_GPR &&
789  instrux.Operands[0].Info.Register.Size == gGuest.WordSize)
790  {
791  LIX_FIELD(MmStruct, ExeFile) = (DWORD)instrux.Operands[1].Info.Memory.Disp;
792  LOG("[OFFSETS] mm_struct->exe_file offset: 0x%x\n", LIX_FIELD(MmStruct, ExeFile));
793 
794  return INT_STATUS_SUCCESS;
795  }
796 
797  gva += instrux.Length;
798  }
799 
800  return INT_STATUS_NOT_FOUND;
801 }
802 
803 
804 INTSTATUS
806  _In_ QWORD SyscallAddress,
807  _Out_ QWORD *ProperSyscallAddress
808  )
827 {
828  QWORD currentSyscallAddress;
829  QWORD foundSyscallAddress = 0;
830 
831  *ProperSyscallAddress = 0;
832 
833  if ((SyscallAddress >> 31) & 1)
834  {
835  return INT_STATUS_NOT_FOUND;
836  }
837 
838  currentSyscallAddress = SyscallAddress;
839 
840  while (currentSyscallAddress - SyscallAddress < PAGE_SIZE)
841  {
842  INSTRUX instrux;
843  NDSTATUS status;
844 
845  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, currentSyscallAddress, &instrux);
846  if (!ND_SUCCESS(status))
847  {
848  ERROR("[ERROR] IntDecDecodeInstruction failed with status 0x%08X", status);
849  return INT_STATUS_NOT_FOUND;
850  }
851 
852  if (instrux.Instruction == ND_INS_MOV &&
853  instrux.OperandsCount == 2 &&
854  instrux.Operands[0].Info.Register.Reg == NDR_RDI &&
855  instrux.Operands[1].Type == ND_OP_IMM &&
856  IS_KERNEL_POINTER_LIX(instrux.Operands[1].Info.Immediate.Imm))
857  {
858  foundSyscallAddress = instrux.Operands[1].Info.Immediate.Imm;
859  }
860  else if (0 != foundSyscallAddress &&
861  ((instrux.Instruction == ND_INS_CALLNR &&
862  instrux.Operands[0].Type == ND_OP_OFFS &&
863  instrux.Operands[0].Info.RelativeOffset.Rel < 0x20) ||
864  (instrux.Instruction == ND_INS_JMPNI &&
865  instrux.Operands[0].Type == ND_OP_REG &&
866  instrux.Operands[0].Info.Register.Reg == NDR_RDI)))
867  {
868  *ProperSyscallAddress = foundSyscallAddress;
869  return INT_STATUS_SUCCESS;
870  }
871 
872  currentSyscallAddress += instrux.Length;
873  }
874 
875  return INT_STATUS_NOT_FOUND;
876 }
877 
878 
879 static INTSTATUS
881  void
882  )
889 {
890  INTSTATUS status;
891 
893  if (!INT_SUCCESS(status))
894  {
895  ERROR("[ERROR] IntLixResolveCurrentProcessOffset failed: 0x%08x\n", status);
896  return status;
897  }
898 
900  if (!INT_SUCCESS(status))
901  {
902  WARNING("[WARNING] IntLixResolveCurrentCpuOffset failed: 0x%08x\n", status);
903  // not a critical error
904  }
905 
906  status = IntLixResolveExeFileOffset();
907  if (!INT_SUCCESS(status))
908  {
909  WARNING("[WARNING] IntLixResolveExeFileOffset failed: 0x%08x\n", status);
910  // for now not a critical error
911  }
912 
914  if (!INT_SUCCESS(status))
915  {
916  ERROR("[ERROR] IntLixResolveThreadStructOffset failed: 0x%08x\n", status);
917  return status;
918  }
919 
920  // The following offsets can be found dynamically:
921  // taks->flags -> at begining
922  // task->tgid, task->pid -> '__audit_ptrace'
923  // task->real_parent, task_parent -> '__ptrace_unlink'
924  // task->mm, task->active_mm -> 'sys_brk'
925  // task->comm -> 'get_task_comm'
926  // task->signal -> 'do_signal_stop'
927  // task->exit_code ->
928  // module->list -> at begining
929  // module->name -> fixed
930  // moulde->core_layout -> 'module_disable_ro'
931  // moulde->init_layout -> 'module_disable_ro'
932  // module->init -> 'do_init_module'
933  //
934  // NOTE: Add more as needed
935 
936  return INT_STATUS_SUCCESS;
937 }
938 
939 
940 static INTSTATUS
942  void
943  )
951 {
952  INTSTATUS status;
953  INTSTATUS returnStatus = INT_STATUS_SUCCESS;
954 
956  {
957  status = IntLixKernelWriteProtect();
958  if (!INT_SUCCESS(status))
959  {
960  ERROR("[ERROR] IntLixKernelWriteProtect failed: 0x%08x\n", status);
961  returnStatus = status;
962  }
963  }
964 
966  {
967  status = IntLixKernelReadProtect();
968  if (!INT_SUCCESS(status))
969  {
970  ERROR("[ERROR] IntLixKernelReadProtect failed: 0x%08x\n", status);
971  returnStatus = status;
972  }
973  }
974 
976  {
977  status = IntLixVdsoProtect();
978  if (!INT_SUCCESS(status))
979  {
980  ERROR("[ERROR] IntLixVdsoProtect failed: 0x%08x\n", status);
981  returnStatus = status;
982  }
983  }
984 
986  {
987  IntGdtrProtect();
988  }
989 
991  {
992  IntIdtrProtect();
993  }
994 
996  {
997  status = IntLixIdtProtectAll();
998  if (!INT_SUCCESS(status))
999  {
1000  ERROR("[ERROR] IntLixIdtProtectAll failed: 0x%08x\n", status);
1001  // Not critical, go forward
1002  }
1003  }
1004 
1006  {
1007  status = IntMsrSyscallProtect();
1008  if (!INT_SUCCESS(status))
1009  {
1010  ERROR("[ERROR] IntMsrSyscallProtect failed: 0x%08x\n", status);
1011  returnStatus = status;
1012  }
1013  }
1014 
1016  {
1017  status = IntCr4Protect();
1018  if (!INT_SUCCESS(status))
1019  {
1020  ERROR("[ERROR] IntCr4Protect failed: 0x%08x\n", status);
1021  returnStatus = status;
1022  }
1023  }
1024 
1026 
1027  return returnStatus;
1028 }
1029 
1030 
1031 INTSTATUS
1033  _In_ QWORD SyscallGva
1034  )
1055 {
1056  QWORD gsBase;
1057  DWORD gsOffset = 0;
1058  DWORD gsValue = 0;
1059  INTSTATUS status;
1060  BOOLEAN foundMovCr3 = FALSE;
1061  BYTE pSyscall[256];
1062 
1065 
1066  status = IntKernVirtMemRead(SyscallGva, sizeof(pSyscall), pSyscall, NULL);
1067  if (!INT_SUCCESS(status))
1068  {
1069  ERROR("[ERROR] IntKernVirtMemRead failed for %llx: %08x\n", SyscallGva, status);
1070  return status;
1071  }
1072 
1073  for (DWORD i = 0; i < sizeof(pSyscall);)
1074  {
1075  INSTRUX instrux;
1076 
1077  status = IntDecDecodeInstructionFromBuffer(&pSyscall[i], sizeof(pSyscall) - i, IG_CS_TYPE_64B, &instrux);
1078  if (!INT_SUCCESS(status))
1079  {
1080  if (sizeof(pSyscall) - i < ND_MAX_INSTRUCTION_LENGTH)
1081  {
1082  break;
1083  }
1084 
1085  ERROR("[ERROR] Invalid instruction in syscall @ %llx: %08x\n", SyscallGva, status);
1086  return status;
1087  }
1088 
1089  if (i <= 10 &&
1090  (instrux.Instruction == ND_INS_JMPNR &&
1091  instrux.Operands[0].Type == ND_OP_OFFS))
1092  {
1093  LOG("[INFO] Found a JMP right after SWAPGS, skip until that (+%02x)\n", instrux.RelativeOffset);
1094  i += instrux.RelativeOffset;
1095  }
1096 
1097  i += instrux.Length;
1098 
1099  if (instrux.Instruction == ND_INS_TEST &&
1100  instrux.Operands[0].Type == ND_OP_MEM &&
1101  instrux.Seg == ND_PREFIX_G2_SEG_GS)
1102  {
1103  gsOffset = instrux.Displacement;
1104  }
1105 
1106  if (instrux.Instruction == ND_INS_MOV_CR &&
1107  instrux.Operands[0].Type == ND_OP_REG &&
1108  instrux.Operands[0].Info.Register.Type == ND_REG_CR &&
1109  instrux.Operands[0].Info.Register.Reg == NDR_CR3 &&
1110  instrux.Operands[1].Type == ND_OP_REG &&
1111  instrux.Operands[1].Info.Register.Type == ND_REG_GPR)
1112  {
1113  foundMovCr3 = TRUE;
1114  break;
1115  }
1116  }
1117 
1118  if (!foundMovCr3)
1119  {
1120  goto _do_leave;
1121  }
1122 
1123  if (0 == gsOffset)
1124  {
1126  goto _do_leave;
1127  }
1128 
1129  if (gsOffset > LIX_KAISER_ENABLED_PCP_OFFSET_CAP)
1130  {
1131  ERROR("[ERROR] The value of misplacement operand (0x%08x) from instruction 'TEST [GS:displacement], immediate' "
1132  "exceed our cap (0x%lx)\n", gsOffset, LIX_KAISER_ENABLED_PCP_OFFSET_CAP);
1134  goto _do_leave;
1135  }
1136 
1137  status = IntGsRead(IG_CURRENT_VCPU, &gsBase);
1138  if (!INT_SUCCESS(status))
1139  {
1140  ERROR("[ERROR] IntGsRead failed: %08x\n", status);
1141  return status;
1142  }
1143 
1144  if (!IS_KERNEL_POINTER_LIX(gsBase))
1145  {
1147  goto _do_leave;
1148  }
1149 
1150  status = IntKernVirtMemFetchDword(gsBase + gsOffset, &gsValue);
1151  if (!INT_SUCCESS(status))
1152  {
1153  WARNING("[WARNING] IntKernVirtMemFetchDword failed for %llx: %08x\n", gsBase + gsOffset, status);
1155  goto _do_leave;
1156  }
1157 
1158  gGuest.KptiActive = (gsValue & 1) != 0;
1159 
1160 _do_leave:
1161  if (gGuest.KptiInstalled)
1162  {
1163  LOG("[LIXGUEST] KPTI active: %d\n", gGuest.KptiActive);
1164  }
1165  else
1166  {
1167  LOG("[LIXGUEST] KPTI cannot be reliable detected... Defer it!\n");
1168  }
1169 
1170  return INT_STATUS_SUCCESS;
1171 }
1172 
1173 
1174 static INTSTATUS
1176  void
1177  )
1193 {
1194  INTSTATUS status;
1195  QWORD gvaStart, gvaEnd;
1196  INSTRUX instrux;
1197  QWORD allGvas[10];
1198  DWORD nrOfGvas = 0, iGva;
1199  QWORD gva;
1200 
1201  gvaStart = IntKsymFindByName("mark_rodata_ro", &gvaEnd);
1202  if (!gvaStart)
1203  {
1204  ERROR("[ERROR] IntKsymFindByName failed for mark_rodata_ro\n");
1205  return INT_STATUS_NOT_FOUND;
1206  }
1207 
1208  while (gvaStart < gvaEnd)
1209  {
1210  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, gvaStart, &instrux);
1211  if (!INT_SUCCESS(status))
1212  {
1213  WARNING("[WARNING] IntDecDecodeInstruction failed: %08x\n", status);
1214  break;
1215  }
1216 
1217  gvaStart += instrux.Length;
1218  gva = instrux.Operands[1].Info.Immediate.Imm;
1219 
1220  if (!(instrux.Operands[0].Type == ND_OP_REG && instrux.Operands[1].Type == ND_OP_IMM) ||
1221  ((0xFFFFFFFF80000000 & gva) != 0xFFFFFFFF80000000))
1222  {
1223  continue;
1224  }
1225 
1226  for (iGva = 0; iGva < nrOfGvas; iGva++)
1227  {
1228  if (allGvas[iGva] == gva)
1229  {
1230  break;
1231  }
1232  }
1233 
1234  if (nrOfGvas != iGva)
1235  {
1236  continue; // already exists
1237  }
1238 
1239  for (iGva = nrOfGvas; (iGva > 0) && (allGvas[iGva - 1] > gva); iGva--)
1240  {
1241  allGvas[iGva] = allGvas[iGva - 1];
1242  }
1243 
1244  allGvas[iGva] = gva;
1245  nrOfGvas++;
1246 
1247  if (nrOfGvas >= 10)
1248  {
1249  break;
1250  }
1251  }
1252 
1253  if (nrOfGvas <= 4)
1254  {
1255  return INT_STATUS_NOT_FOUND;
1256  }
1257 
1258  if ((allGvas[2] & PAGE_MASK) != gLixGuest->Layout.RoDataStart)
1259  {
1260  return INT_STATUS_NOT_FOUND;
1261  }
1262 
1263  for (iGva = 4; iGva < nrOfGvas; iGva++)
1264  {
1265  if (allGvas[iGva] >= gLixGuest->Layout.DataEnd)
1266  {
1267  return INT_STATUS_NOT_FOUND;
1268  }
1269 
1270  if (!(allGvas[iGva] & PAGE_OFFSET_MASK_2M) &&
1271  (((allGvas[iGva] - 1) & PAGE_BASE_MASK_2M) == ((allGvas[iGva - 1] & PAGE_BASE_MASK_2M))))
1272  {
1273  gLixGuest->Layout.RoDataEnd = allGvas[iGva - 1] & PAGE_MASK;
1274  gLixGuest->Layout.DataStart = allGvas[iGva];
1275 
1276  return INT_STATUS_SUCCESS;
1277  }
1278  }
1279 
1280  return INT_STATUS_NOT_FOUND;
1281 }
1282 
1283 
1284 static INTSTATUS
1286  _Out_ QWORD *Pgd
1287  )
1311 {
1312  INTSTATUS status;
1313  QWORD ksymEnd, ksymEndAux;
1314 
1315  QWORD pgdAddr = IntKsymFindByName("init_top_pgt", NULL);
1316  if (pgdAddr)
1317  {
1318  *Pgd = pgdAddr;
1319  return INT_STATUS_SUCCESS;
1320  }
1321 
1322  pgdAddr = IntKsymFindByName("init_level4_pgt", NULL);
1323  if (pgdAddr)
1324  {
1325  *Pgd = pgdAddr;
1326  return INT_STATUS_SUCCESS;
1327  }
1328 
1329  QWORD ksymStart = IntKsymFindByName("arch_crash_save_vmcoreinfo", &ksymEnd);
1330  if (!ksymStart)
1331  {
1332  ERROR("[ERROR] IntKsymFindByName could not find arch_crash_save_vmcoreinfo\n");
1333  return INT_STATUS_NOT_FOUND;
1334  }
1335 
1336  QWORD ksymStartAux = IntKsymFindByName("vmcoreinfo_append_str", &ksymEndAux);
1337  if (!ksymStartAux)
1338  {
1339  ERROR("[ERROR] IntKsymFindByName could not find vmcoreinfo_append_str\n");
1340  return INT_STATUS_NOT_FOUND;
1341  }
1342 
1343  WORD funcCallCount = 0;
1344  while (ksymStart < ksymEnd)
1345  {
1346  INSTRUX instrux;
1347 
1348  status = IntDecDecodeInstruction(IG_CS_TYPE_64B, ksymStart, &instrux);
1349  if (!INT_SUCCESS(status))
1350  {
1351  ERROR("[ERROR] IntDecDecodeInstruction failed at GVA %llx: 0x%08x.\n", ksymStart, status);
1352  ksymStart++;
1353  continue;
1354  }
1355 
1356  if (instrux.Instruction == ND_INS_MOV &&
1357  instrux.Operands[0].Type == ND_OP_REG &&
1358  instrux.Operands[0].Info.Register.Reg == NDR_RDX &&
1359  instrux.Operands[1].Type == ND_OP_IMM)
1360  {
1361  pgdAddr = instrux.Operands[1].Info.Immediate.Imm;
1362  }
1363 
1364  if (instrux.Instruction == ND_INS_CALLNR)
1365  {
1366  QWORD ksymRelAux = ksymStartAux - (ksymStart + 5);
1367 
1368  if (instrux.Operands[0].Info.RelativeOffset.Rel == ksymRelAux)
1369  {
1370  funcCallCount++;
1371  }
1372 
1373  if (funcCallCount == 2)
1374  {
1375  *Pgd = pgdAddr;
1376  return INT_STATUS_SUCCESS;
1377  }
1378  }
1379 
1380  ksymStart += instrux.Length;
1381  }
1382 
1383  return INT_STATUS_NOT_FOUND;
1384 }
1385 
1386 
1387 static INTSTATUS
1389  _In_ void *Detour,
1390  _In_ LIX_ACTIVE_PATCH *ActivePatch
1391  )
1404 {
1405  INTSTATUS status = INT_STATUS_SUCCESS;
1406  LIST_ENTRY *list = NULL;
1407  QWORD patchGva, address;
1408  WORD length;
1409  BOOLEAN bFound;
1410 
1411  UNREFERENCED_PARAMETER(Detour);
1412 
1413  patchGva = gVcpu->Regs.R8;
1414  length = (WORD)gVcpu->Regs.R10;
1415  address = gVcpu->Regs.R9;
1416 
1417  list = gKernelDrivers.Head;
1418  bFound = FALSE;
1419  while (list != &gKernelDrivers)
1420  {
1421  KERNEL_DRIVER *pDriver = CONTAINING_RECORD(list, KERNEL_DRIVER, Link);
1422  list = list->Flink;
1423 
1424  if (pDriver->Lix.CoreLayout.Base <= patchGva &&
1425  patchGva < pDriver->Lix.CoreLayout.Base + pDriver->Lix.CoreLayout.RoSize)
1426  {
1427  bFound = TRUE;
1428  break;
1429  }
1430  }
1431 
1432  if (!bFound)
1433  {
1434  // If we can not find the address in any driver then most likely that area is not protected.
1435  // But there is a chance the GVA points to a GPA which is hooked by introcore e.g. __va(__pa(x))
1436  TRACE("[WARNING] Incoming patch at address 0x%llx with no corresponding driver. Will ignore!\n", patchGva);
1437  return INT_STATUS_SUCCESS;
1438  }
1439 
1440  if (length > sizeof(ActivePatch->Data))
1441  {
1442  WARNING("[WARNING] Patch with size %d... We ignore it!\n", length);
1443  return INT_STATUS_SUCCESS;
1444  }
1445 
1446  status = IntKernVirtMemRead(address, length, ActivePatch->Data, NULL);
1447  if (!INT_SUCCESS(status))
1448  {
1449  ERROR("[ERROR] IntKernVirtMemRead failed: 0x%08x. Patch at GVA 0x%llx will be ignored.\n", status, patchGva);
1450  return status;
1451  }
1452 
1453  ActivePatch->Gva = patchGva;
1454  ActivePatch->Length = length;
1455 
1456  ActivePatch->IsDetour = IntDetIsPtrInRelocatedCode(patchGva, &ActivePatch->DetourTag);
1457 
1458  return INT_STATUS_SUCCESS;
1459 }
1460 
1461 
1462 INTSTATUS
1464  _In_ void *Detour
1465  )
1473 {
1474  LIX_ACTIVE_PATCH *pActivePatch = &gLixGuest->ActivePatch[lixActivePatchTextPoke];
1475 
1476  return IntLixPatchHandler(Detour, pActivePatch);
1477 }
1478 
1479 
1480 INTSTATUS
1482  _In_ void *Detour
1483  )
1489 {
1490  LIX_ACTIVE_PATCH *pActivePatch = &gLixGuest->ActivePatch[lixActivePatchFtrace];
1491 
1492  return IntLixPatchHandler(Detour, pActivePatch);
1493 }
1494 
1495 
1496 INTSTATUS
1498  _In_ void *Detour
1499  )
1509 {
1510  INTSTATUS status = INT_STATUS_SUCCESS;
1511  LIX_ACTIVE_PATCH *pActivePatch = &gLixGuest->ActivePatch[lixActivePatchJmpLabel];
1512  QWORD jumpEntry = gVcpu->Regs.R8;
1513  QWORD gva = 0;
1514 
1515  UNREFERENCED_PARAMETER(Detour);
1516 
1517  status = IntKernVirtMemFetchQword(jumpEntry, &gva);
1518  if (!INT_SUCCESS(status))
1519  {
1520  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: %08x\n", jumpEntry, status);
1521  return status;
1522  }
1523 
1524  pActivePatch->Gva = gva;
1525  pActivePatch->Length = 5;
1526 
1527  return INT_STATUS_SUCCESS;
1528 }
1529 
1530 
1531 static INTSTATUS
1533  void
1534  )
1544 {
1545  INTSTATUS status;
1546 
1548  {
1550  }
1551 
1552  status = IntKsymInit();
1553  if (!INT_SUCCESS(status))
1554  {
1555  ERROR("[ERROR] IntKsymInit failed: 0x%08x", status);
1556  return status;
1557  }
1558 
1559  gLixGuest->Layout.CodeEnd = IntKsymFindByName("_etext", NULL);
1560  if (!gLixGuest->Layout.CodeEnd)
1561  {
1562  ERROR("[ERROR] Failed finding '_etext' symbol\n");
1564  }
1565 
1566  // If this is missing, then the kernel exports in kallsyms only the functions.
1567  // We can still approximate the layout (based on _sinittext and _etext)
1568  gLixGuest->Layout.DataStart = IntKsymFindByName("_sdata", NULL);
1569  if (!gLixGuest->Layout.DataStart)
1570  {
1571  gLixGuest->Layout.DataStart = ROUND_UP(gLixGuest->Layout.CodeEnd, PAGE_SIZE);
1572 
1573  gLixGuest->Layout.DataEnd = IntKsymFindByName("_sinittext", NULL);
1574  if (!gLixGuest->Layout.DataEnd)
1575  {
1576  ERROR("[ERROR] IntKsymFindByName could not find _sinittext\n");
1578  }
1579 
1580  status = IntLixFindDataStart();
1581  if (!INT_SUCCESS(status))
1582  {
1583  ERROR("[ERROR] IntLixFindDataStart failed: %08x\n", status);
1584  return status;
1585  }
1586 
1588  if (!INT_SUCCESS(status))
1589  {
1590  WARNING("[WARNING] Could not find ex_table limits: 0x%08x\n", status);
1591  }
1592  }
1593  else
1594  {
1595  gLixGuest->Layout.DataEnd = IntKsymFindByName("_edata", NULL);
1596  if (!gLixGuest->Layout.DataEnd)
1597  {
1598  ERROR("[ERROR] IntKsymFindByName could not find _edata\n");
1600  }
1601 
1602  gLixGuest->Layout.ExTableStart = IntKsymFindByName("__start___ex_table", &gLixGuest->Layout.ExTableEnd);
1603  if (!gLixGuest->Layout.ExTableStart)
1604  {
1605  ERROR("[ERROR] IntKsymFindByName could not find __start___ex_table\n");
1607  }
1608 
1609  gLixGuest->Layout.RoDataStart = IntKsymFindByName("__start_rodata", NULL);
1610  if (!gLixGuest->Layout.RoDataStart)
1611  {
1612  ERROR("[ERROR] IntKsymFindByName could not find __start_rodata\n");
1614  }
1615 
1616  gLixGuest->Layout.RoDataEnd = IntKsymFindByName("__end_rodata", NULL);
1617  if (!gLixGuest->Layout.RoDataEnd)
1618  {
1619  ERROR("[ERROR] IntKsymFindByName could not find __end_rodata\n");
1621  }
1622  }
1623 
1624  if (LIX_FIELD(Info, HasAlternateSyscall))
1625  {
1626  gLixGuest->PropperSyscallGva = IntKsymFindByName("do_syscall_64", NULL);
1627  if (!gLixGuest->PropperSyscallGva)
1628  {
1629  WARNING("[WARNING] Could not find proper syscall gva. Agent injection may fail!\n");
1630  }
1631  else
1632  {
1633  TRACE("[INFO] Proper syscall address: %llx\n", gLixGuest->PropperSyscallGva);
1634  }
1635  }
1636 
1637  TRACE("[LIXGUEST] .kernel : 0x%016llx - 0x%016llx (%4lld kB)\n",
1638  gGuest.KernelVa,
1641  TRACE("[LIXGUEST] .text : 0x%016llx - 0x%016llx (%4lld kB)\n",
1642  gLixGuest->Layout.CodeStart, gLixGuest->Layout.CodeEnd,
1643  (gLixGuest->Layout.CodeEnd - gLixGuest->Layout.CodeStart) / ONE_KILOBYTE);
1644  TRACE("[LIXGUEST] .data : 0x%016llx - 0x%016llx (%4lld kB)\n",
1645  gLixGuest->Layout.DataStart, gLixGuest->Layout.DataEnd,
1646  (gLixGuest->Layout.DataEnd - gLixGuest->Layout.DataStart) / ONE_KILOBYTE);
1647  TRACE("[LIXGUEST] .rodata : 0x%016llx - 0x%016llx (%4lld kB)\n",
1648  gLixGuest->Layout.RoDataStart, gLixGuest->Layout.RoDataEnd,
1649  (gLixGuest->Layout.RoDataEnd - gLixGuest->Layout.RoDataStart) / ONE_KILOBYTE);
1650 
1651  status = IntLixDrvCreateKernel();
1652  if (!INT_SUCCESS(status))
1653  {
1654  ERROR("[ERROR] Failed initializing the linux kernel driver: 0x%08x\n", status);
1655  return status;
1656  }
1657 
1659 
1660  status = IntLixGuestResolveOffsets();
1661  if (!INT_SUCCESS(status))
1662  {
1663  ERROR("[ERROR] IntLixGuestResolveOffsets failed: 0x%08x\n", status);
1664  return status;
1665  }
1666 
1668 
1669  return INT_STATUS_SUCCESS;
1670 }
1671 
1672 
1673 void
1675  void
1676  )
1682 {
1683  INTSTATUS status;
1684 
1685  if (!gGuest.GuestInitialized || NULL == gLixGuest)
1686  {
1687  return;
1688  }
1689 
1691 
1692  status = IntLixIdtUnprotectAll();
1693  if (!INT_SUCCESS(status))
1694  {
1695  ERROR("[ERROR] IntLixIdtUnprotectAll failed: 0x%08x\n", status);
1696  }
1697 
1698  status = IntCr4Unprotect();
1699  if (!INT_SUCCESS(status))
1700  {
1701  ERROR("[ERROR] IntCr4Unprotect failed: 0x%08x\n", status);
1702  }
1703 
1704  IntDriverUninit();
1705 
1706  IntLixTaskUninit();
1707 
1708  if (NULL != gLixGuest->OsSpecificFields.Functions)
1709  {
1711  }
1712 
1713  IntKsymUninit();
1714 
1716 
1718 
1719  TRACE("[INTRO-UNINIT] Uninit allocated guest memory ...\n");
1721 
1723 }
1724 
1725 
1726 static BOOLEAN
1728  void
1729  )
1738 {
1740  if (!INT_SUCCESS(status))
1741  {
1742  return FALSE;
1743  }
1744 
1746 
1747  return TRUE;
1748 }
1749 
1750 
1751 static INTSTATUS
1753  _In_opt_ void **Context,
1754  _In_ void *Hook,
1755  _In_ QWORD Address,
1756  _Out_ INTRO_ACTION *Action
1757  )
1768 {
1769  INTSTATUS status;
1770  QWORD address = IntHookGetGlaFromGpaHook(Hook, Address);
1771  CHAR ksymbol[126] = { 0 };
1772  HOOK_GPA *pHook = Hook;
1773 
1774  UNREFERENCED_PARAMETER(Context);
1775 
1776  status = IntKsymFindByAddress(gVcpu->Regs.Rip, sizeof(ksymbol), ksymbol, NULL, NULL);
1777  TRACE("[LIXGUEST] %s attempt on detour code from @0x%016llx (%s).\n",
1778  pHook->Header.EptHookType == IG_EPT_HOOK_WRITE ? "Write" : "Execute",
1779  address, INT_SUCCESS(status) ? ksymbol : "none");
1780 
1781  TRACE("[LIXGUEST] Instruction:");
1782  IntDisasmGva(gVcpu->Regs.Rip, ND_MAX_INSTRUCTION_LENGTH);
1784 
1785  *Action = introGuestNotAllowed;
1786 
1787  return INT_STATUS_SUCCESS;
1788 }
1789 
1790 
1791 static INTSTATUS
1793  _In_opt_ void **Context,
1794  _In_ void *Hook,
1795  _In_ QWORD Address,
1796  _Out_ INTRO_ACTION *Action
1797  )
1808 {
1809  UNREFERENCED_PARAMETER(Context);
1810  INTSTATUS status;
1811  QWORD address = IntHookGetGlaFromGpaHook(Hook, Address);
1812  CHAR ksymbol[126] = { 0 };
1813 
1814  status = IntKsymFindByAddress(gVcpu->Regs.Rip, sizeof(ksymbol), ksymbol, NULL, NULL);
1815  TRACE("[LIXGUEST] Write/Read attempt on agent content from @0x%016llx (%s).\n",
1816  address, INT_SUCCESS(status) ? ksymbol : "none");
1817 
1818  TRACE("[LIXGUEST] Instruction:");
1819  IntDisasmGva(gVcpu->Regs.Rip, ND_MAX_INSTRUCTION_LENGTH);
1821 
1822  *Action = introGuestNotAllowed;
1823 
1824  return INT_STATUS_SUCCESS;
1825 }
1826 
1827 
1828 INTSTATUS
1830  void
1831  )
1862 {
1863  INTSTATUS status;
1864  LIX_HYPERCALL_PAGE *pHypercallPage = (LIX_HYPERCALL_PAGE *)(gLixDetours);
1865 
1866  if (sizeof(*pHypercallPage) > gLixGuest->MmAlloc.Detour.Data.Length)
1867  {
1868  ERROR("[ERROR] Linux hypercall page size exceed %d bytes", gLixGuest->MmAlloc.Detour.Data.Length);
1869  return INT_STATUS_NOT_SUPPORTED;
1870  }
1871 
1872  if (sizeof(gLixDetours) > ((size_t)gLixGuest->MmAlloc.Detour.Code.Length + gLixGuest->MmAlloc.Detour.Data.Length))
1873  {
1874  ERROR("[ERROR] Linux detours content size exceed %d bytes", gLixGuest->MmAlloc.Detour.Data.Length);
1875  return INT_STATUS_NOT_SUPPORTED;
1876  }
1877 
1878  if (sizeof(gLixAgents) > gLixGuest->MmAlloc.Agent.Length)
1879  {
1880  ERROR("[ERROR] Linux agents content size exceed %d bytes", gLixGuest->MmAlloc.Detour.Data.Length);
1881  return INT_STATUS_NOT_SUPPORTED;
1882  }
1883 
1884  pHypercallPage->OsSpecificFields.Info.CredAltered = LIX_FIELD(Info, CredAltered);
1885 
1886  pHypercallPage->OsSpecificFields.Mm.FlagsOffset = LIX_FIELD(MmStruct, Flags);
1887  pHypercallPage->OsSpecificFields.Mm.ProtectionBit = 63;
1888  pHypercallPage->OsSpecificFields.Mm.Rb = LIX_FIELD(MmStruct, RbNode);
1889 
1890  pHypercallPage->OsSpecificFields.Vma.MmOffset = LIX_FIELD(Vma, Mm);
1891  pHypercallPage->OsSpecificFields.Vma.FlagsOffset = LIX_FIELD(Vma, Flags);
1892  pHypercallPage->OsSpecificFields.Vma.FileOffset = LIX_FIELD(Vma, File);
1893  pHypercallPage->OsSpecificFields.Vma.VmNextOffset = LIX_FIELD(Vma, VmNext);
1894  pHypercallPage->OsSpecificFields.Vma.VmPrevOffset = LIX_FIELD(Vma, VmPrev);
1895  pHypercallPage->OsSpecificFields.Vma.Rb = LIX_FIELD(Vma, RbNode);
1896  pHypercallPage->OsSpecificFields.Vma.ProtectionBit = 63;
1897 
1898  pHypercallPage->OsSpecificFields.Task.InExecve = LIX_FIELD(TaskStruct, InExecve);
1899  pHypercallPage->OsSpecificFields.Task.InExecveBit = LIX_FIELD(TaskStruct, InExecveBit);
1900 
1901  pHypercallPage->OsSpecificFields.Binprm.FileOffset = LIX_FIELD(Binprm, File);
1902 
1903  pHypercallPage->OsSpecificFields.File.DentryOffset = LIX_FIELD(Ungrouped, FileDentry);
1904  pHypercallPage->OsSpecificFields.File.PathOffset = LIX_FIELD(Ungrouped, FilePath);
1905 
1906  pHypercallPage->OsSpecificFields.Dentry.InodeOffset = LIX_FIELD(Dentry, Inode);
1907 
1908  pHypercallPage->OsSpecificFields.Inode.Mode = LIX_FIELD(Inode, Imode);
1909  pHypercallPage->OsSpecificFields.Inode.Uid = LIX_FIELD(Inode, Uid);
1910  pHypercallPage->OsSpecificFields.Inode.Gid = LIX_FIELD(Inode, Gid);
1911 
1914 
1915  pHypercallPage->OsSpecificFields.PercpuMemPtr = (void *)(gLixGuest->MmAlloc.PerCpuData.PerCpuAddress);
1916 
1917  pHypercallPage->OsSpecificFields.DPathFnPtr = (void *)(IntKsymFindByName("d_path", NULL));
1918  if (!pHypercallPage->OsSpecificFields.DPathFnPtr)
1919  {
1920  ERROR("[ERROR] IntKsymFindByName could not find 'd_path'\n");
1922  }
1923 
1924  status = IntKernVirtMemWrite(gLixGuest->MmAlloc.Agent.Address, sizeof(gLixAgents), gLixAgents);
1925  if (!INT_SUCCESS(status))
1926  {
1927  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
1928  return status;
1929  }
1930 
1931  TRACE("[LIXGUEST] Deployed agents @0x%016llx.", gLixGuest->MmAlloc.Agent.Address);
1932 
1933  status = IntKernVirtMemWrite(gLixGuest->MmAlloc.Detour.Data.Address, gLixGuest->MmAlloc.Detour.Data.Length, gLixDetours);
1934  if (!INT_SUCCESS(status))
1935  {
1936  ERROR("[ERROR] IntKernVirtMemWrite failed: 0x%08x\n", status);
1937  return status;
1938  }
1939 
1940  TRACE("[LIXGUEST] Deployed detours data @0x%016llx.", gLixGuest->MmAlloc.Detour.Data.Address);
1941 
1942  status = IntMemClkCloakRegion(gLixGuest->MmAlloc.Detour.Code.Address,
1943  0,
1944  gLixGuest->MmAlloc.Detour.Code.Length,
1946  NULL,
1947  gLixDetours + gLixGuest->MmAlloc.Detour.Data.Length,
1948  NULL,
1949  &gLixGuest->MmAlloc.Detour.Code.HookObject);
1950  if (!INT_SUCCESS(status))
1951  {
1952  ERROR("[ERROR] IntMemClkCloakRegion failed for 0x%016llx (%d bytes) with status: 0x%08x\n",
1953  gLixGuest->MmAlloc.Detour.Code.Address, gLixGuest->MmAlloc.Detour.Code.Length, status);
1954  return status;
1955  }
1956 
1957  TRACE("[LIXGUEST] Deployed detours code @0x%016llx.", gLixGuest->MmAlloc.Detour.Code.Address);
1958 
1959  return INT_STATUS_SUCCESS;
1960 }
1961 
1962 
1963 static INTSTATUS
1965  _In_ QWORD Gva,
1966  _In_ DWORD Length
1967  )
1979 {
1980  INTSTATUS status;
1981  void *p;
1982  DWORD left = Length;
1983  QWORD gva = Gva;
1984 
1985  do
1986  {
1987  DWORD size = MIN(left, PAGE_REMAINING(Gva));
1988 
1989  status = IntVirtMemMap(gva, size, gGuest.Mm.SystemCr3, 0, &p);
1990  if (!INT_SUCCESS(status))
1991  {
1992  ERROR("[ERROR] IntVirtMemMap failed for %llx: %08x\n", gva, status);
1993  return status;
1994  }
1995 
1996  memzero(p, size);
1997 
1998  IntVirtMemUnmap(&p);
1999 
2000  gva += size;
2001  left -= size;
2002  } while (gva < Gva + Length);
2003 
2004  return INT_STATUS_SUCCESS;
2005 }
2006 
2007 
2008 static INTSTATUS
2010  void
2011  )
2018 {
2019  INTSTATUS status;
2020  IG_ARCH_REGS *pRegs = &gVcpu->Regs;
2021  VA_TRANSLATION translation = { 0 };
2022 
2023  if (pRegs->R8 == 0)
2024  {
2025  ERROR("[ERROR] Failed to allocate guest virtual space for detours. Abort...\n");
2027  }
2028 
2029  // The provided guest virtual address must be inside the module mapping range [ffffffffa0000000 - fffffffffeffffff].
2031  {
2032  ERROR("[ERROR] The guest virtual address (0x%016llx) return by 'module_alloc' is not inside the module mapping "
2033  "region. Abort...\n", pRegs->R8);
2035  }
2036 
2037  gLixGuest->MmAlloc.Detour.Data.Address = pRegs->R8;
2038  gLixGuest->MmAlloc.Detour.Data.Length = PAGE_SIZE;
2039  gLixGuest->MmAlloc.PerCpuData.PerCpuAddress = pRegs->R9;
2040 
2041  TRACE("[LIXGUEST] Allocated guest virtual memory for detours data @ 0x%016llx (0x%x bytes)\n",
2042  gLixGuest->MmAlloc.Detour.Data.Address, gLixGuest->MmAlloc.Detour.Data.Length);
2043 
2044  gLixGuest->MmAlloc.Detour.Code.Address = pRegs->R8 + PAGE_SIZE;
2045  gLixGuest->MmAlloc.Detour.Code.Length = PAGE_SIZE;
2046 
2047  TRACE("[LIXGUEST] Allocated guest virtual memory for detours code @ 0x%016llx (0x%x bytes)\n",
2048  gLixGuest->MmAlloc.Detour.Code.Address, gLixGuest->MmAlloc.Detour.Code.Length);
2049 
2050  gLixGuest->MmAlloc.Detour.Initialized = TRUE;
2051 
2052  gLixGuest->MmAlloc.Agent.Address = pRegs->R8 + PAGE_SIZE * 2;
2053  gLixGuest->MmAlloc.Agent.Length = PAGE_SIZE;
2054  TRACE("[LIXGUEST] Allocated guest virtual memory for agent code @ 0x%016llx (0x%x bytes)\n",
2055  gLixGuest->MmAlloc.Agent.Address, gLixGuest->MmAlloc.Agent.Length);
2056 
2057  gLixGuest->MmAlloc.Agent.Initialized = TRUE;
2058 
2059  status = IntTranslateVirtualAddressEx(pRegs->R8, gGuest.Mm.SystemCr3, TRFLG_NONE, &translation);
2060  if (!INT_SUCCESS(status))
2061  {
2062  ERROR("[ERROR] IntTranslateVirtualAddressEx failed with status: 0x%08x.\n", status);
2063  return status;
2064  }
2065 
2066  gLixGuest->MmAlloc.OriginalPagesAttr = translation.MappingsEntries[translation.MappingsCount - 1];
2067 
2068  return INT_STATUS_SUCCESS;
2069 }
2070 
2071 
2072 void
2074  void
2075  )
2079 {
2080  INTSTATUS status;
2081 
2082  if (gLixGuest->MmAlloc.Detour.Data.HookObject)
2083  {
2084  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLixGuest->MmAlloc.Detour.Data.HookObject, 0);
2085  if (!INT_SUCCESS(status))
2086  {
2087  ERROR("[ERROR] IntHookObjectDestroy failed with status: 0x%08x\n", status);
2088  }
2089 
2090  gLixGuest->MmAlloc.Detour.Data.HookObject = NULL;
2091  }
2092 
2093  if (gLixGuest->MmAlloc.Detour.Code.HookObject)
2094  {
2095  status = IntMemClkUncloakRegion(gLixGuest->MmAlloc.Detour.Code.HookObject, MEMCLOAK_OPT_APPLY_PATCH);
2096  if (!INT_SUCCESS(status))
2097  {
2098  ERROR("[ERROR] IntMemClkUncloakRegion failed with status: 0x%08x\n", status);
2099  }
2100 
2101  gLixGuest->MmAlloc.Detour.Code.HookObject = NULL;
2102  }
2103 
2104  if (gLixGuest->MmAlloc.Agent.HookObject)
2105  {
2106  status = IntHookObjectDestroy((HOOK_OBJECT_DESCRIPTOR **)&gLixGuest->MmAlloc.Agent.HookObject, 0);
2107  if (!INT_SUCCESS(status))
2108  {
2109  ERROR("[ERROR] IntHookObjectDestroy failed with status: 0x%08x\n", status);
2110  }
2111 
2112  gLixGuest->MmAlloc.Agent.HookObject = NULL;
2113  }
2114 }
2115 
2116 
2117 INTSTATUS
2119  void
2120  )
2129 {
2130  INTSTATUS status = INT_STATUS_SUCCESS;
2131 
2132  status = IntHookObjectCreate(introObjectTypeRaw, 0, &gLixGuest->MmAlloc.Detour.Data.HookObject);
2133  if (!INT_SUCCESS(status))
2134  {
2135  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
2136  goto _exit;
2137  }
2138 
2139  status = IntHookObjectHookRegion(gLixGuest->MmAlloc.Detour.Data.HookObject,
2141  gLixGuest->MmAlloc.Detour.Data.Address,
2142  gLixGuest->MmAlloc.Detour.Data.Length,
2145  NULL,
2146  0,
2147  NULL);
2148  if (!INT_SUCCESS(status))
2149  {
2150  ERROR("[ERROR] IntHookObjectHookRegion failed with status: 0x%x", status);
2151  goto _exit;
2152  }
2153 
2154  status = IntHookObjectHookRegion(gLixGuest->MmAlloc.Detour.Data.HookObject,
2156  gLixGuest->MmAlloc.Detour.Data.Address,
2157  gLixGuest->MmAlloc.Detour.Data.Length,
2160  NULL,
2161  0,
2162  NULL);
2163  if (!INT_SUCCESS(status))
2164  {
2165  ERROR("[ERROR] IntHookObjectHookRegion failed with status: 0x%x", status);
2166  goto _exit;
2167  }
2168 
2169  status = IntHookObjectCreate(introObjectTypeRaw, 0, &gLixGuest->MmAlloc.Agent.HookObject);
2170  if (!INT_SUCCESS(status))
2171  {
2172  ERROR("[ERROR] IntHookObjectCreate failed: 0x%08x\n", status);
2173  goto _exit;
2174  }
2175 
2176  status = IntHookObjectHookRegion(gLixGuest->MmAlloc.Agent.HookObject,
2178  gLixGuest->MmAlloc.Agent.Address,
2179  gLixGuest->MmAlloc.Agent.Length,
2182  NULL,
2183  0,
2184  NULL);
2185  if (!INT_SUCCESS(status))
2186  {
2187  ERROR("[ERROR] IntHookObjectHookRegion failed with status: 0x%x", status);
2188  goto _exit;
2189  }
2190 
2191  return INT_STATUS_SUCCESS;
2192 
2193 _exit:
2195 
2196  return status;
2197 }
2198 
2199 
2200 int
2202  void
2203  )
2211 {
2212  INTSTATUS status;
2213  static QWORD sysStateStart = 0;
2214 
2215  if (__unlikely(0 == sysStateStart))
2216  {
2217  sysStateStart = IntKsymFindByName("system_state", NULL);
2218  if (!sysStateStart)
2219  {
2220  return -1;
2221  }
2222  }
2223 
2224  DWORD systemState;
2225 
2226  // The value of the 'system_state' must be validated by the caller.
2227  status = IntKernVirtMemFetchDword(sysStateStart, &systemState);
2228  if (INT_SUCCESS(status))
2229  {
2230  return (int)systemState;
2231  }
2232 
2233  return -1;
2234 }
2235 
2236 
2237 BOOLEAN
2239  void
2240  )
2249 {
2250  INTSTATUS status;
2251  LIX_AGENT_HANDLER *pHandler;
2252  LIX_AGENT_UNINIT_ARGS *pArgs;
2253 
2254  if (!gGuest.GuestInitialized)
2255  {
2256  return FALSE;
2257  }
2258 
2259  if (!gLixGuest->MmAlloc.Agent.Initialized && !gLixGuest->MmAlloc.Detour.Initialized)
2260  {
2261  return FALSE;
2262  }
2263 
2264  // If the guest is terminating there is no reason to perform any cleanup.
2266  {
2267  return FALSE;
2268  }
2269 
2270  if (!gLixGuest->MmAlloc.Agent.Cleared || !gLixGuest->MmAlloc.Detour.Cleared)
2271  {
2272  WARNING("[WARNING] Trying to deploy init agent without clearing the memory: %d %d\n",
2273  gLixGuest->MmAlloc.Agent.Cleared, gLixGuest->MmAlloc.Detour.Cleared);
2274  }
2275 
2277  if (pHandler == NULL)
2278  {
2279  ERROR("[ERROR] Requested to deploy the uninit agent, but none was found!\n");
2280  return FALSE;
2281  }
2282 
2283  pArgs = pHandler->Args.Content;
2284  pArgs->Free.ModuleAddress = gLixGuest->MmAlloc.Detour.Data.Address;
2285  pArgs->Free.PerCpuAddress = gLixGuest->MmAlloc.PerCpuData.PerCpuAddress;
2286 
2287  // Get the page attrs from original guest mapping entry to create a 'set mask' and a 'clear mask' to restore the
2288  // original attrs
2289  pArgs->Attr.MaskSet = gLixGuest->MmAlloc.OriginalPagesAttr & (PT_RW | PT_XD);
2290  pArgs->Attr.MaskClear = (gLixGuest->MmAlloc.OriginalPagesAttr & (PT_RW | PT_XD)) ^ (PT_RW | PT_XD);
2291 
2292  TRACE("[LIXGUEST] Change page (0x%llx) attributes: Clear -> 0x%llx Set -> 0x%llx\n",
2293  gLixGuest->MmAlloc.OriginalPagesAttr, pArgs->Attr.MaskClear, pArgs->Attr.MaskSet);
2294 
2295  LOG("[LIXGUEST] Deploy the uninit agent...\n");
2296 
2297  status = IntLixAgentInject(lixAgTagUninit, NULL, NULL);
2298  if (!INT_SUCCESS(status))
2299  {
2300  ERROR("[ERROR] IntLixAgentInject failed with status: 0x%08x.", status);
2301  return FALSE;
2302  }
2303 
2304  gLixGuest->MmAlloc.Agent.Initialized = FALSE;
2305  gLixGuest->MmAlloc.Detour.Initialized = FALSE;
2306 
2307  return TRUE;
2308 }
2309 
2310 
2311 static INTSTATUS
2313  void
2314  )
2320 {
2321  INTSTATUS status;
2322 
2323  status = IntLixGuestAllocateFill();
2324  if (!INT_SUCCESS(status))
2325  {
2326  ERROR("[ERROR] IntLixGuestAllocateFill failed with status: %08x\n", status);
2327  return status;
2328  }
2329 
2330  status = IntLixGuestAllocateHook();
2331  if (!INT_SUCCESS(status))
2332  {
2333  ERROR("[ERROR] IntLixGuestAllocateHook failed with status: %08x\n", status);
2334  return status;
2335  }
2336 
2337  status = IntLixGuestAllocateDeploy();
2338  if (!INT_SUCCESS(status))
2339  {
2340  ERROR("[ERROR] IntLixGuestAllocateDeploy failed with status: %08x", status);
2341  return status;
2342  }
2343 
2344  return INT_STATUS_SUCCESS;
2345 }
2346 
2347 
2348 static INTSTATUS
2350  _In_ void *Context
2351  )
2360 {
2361  INTSTATUS status;
2362 
2363  UNREFERENCED_PARAMETER(Context);
2364 
2365  if (gGuest.UninitPrepared)
2366  {
2368  }
2369 
2370  IntPauseVcpus();
2371 
2372  status = IntLixGuestAllocateInit();
2373  if (!INT_SUCCESS(status))
2374  {
2375  ERROR("[ERROR] IntLixGuestAllocateInit failed with status: 0x%08x.", status);
2376  goto _exit;
2377  }
2378 
2379  IntResumeVcpus();
2380 
2381  return INT_STATUS_SUCCESS;
2382 
2383 _exit:
2384  IntResumeVcpus();
2386 
2387  return INT_STATUS_SUCCESS;
2388 }
2389 
2390 
2391 static INTSTATUS
2393  void *Context
2394  )
2411 {
2412  INTSTATUS status;
2413 
2414  UNREFERENCED_PARAMETER(Context);
2415 
2416  if (gGuest.UninitPrepared)
2417  {
2419  }
2420 
2421  IntPauseVcpus();
2422 
2423  // Clear the stack to remove any addresses that may have been save by the kernel
2424  QWORD addr = gVcpu->Regs.Rsp - 8; // Do not clear the return address
2425  QWORD stackBase = addr & (~((QWORD)LIX_FIELD(Info, ThreadSize) - 1));
2426  DWORD length = MIN(PAGE_SIZE, (DWORD)(addr - stackBase));
2427 
2428  status = IntVirtMemSet(addr - length, length, gGuest.Mm.SystemCr3, 0);
2429  if (!INT_SUCCESS(status))
2430  {
2431  ERROR("[ERROR] IntVirtMemSet failed for gva 0x%016llx with status: 0x%08x\n", addr, status);
2432  }
2433 
2435  if (!INT_SUCCESS(status))
2436  {
2437  ERROR("[ERROR] IntLixTaskIterateGuestTasks failed, status = 0x%08x\n", status);
2438  goto _exit;
2439  }
2440 
2442  if (!INT_SUCCESS(status))
2443  {
2444  if (status != INT_STATUS_NOT_INITIALIZED)
2445  {
2446  ERROR("[ERROR] IntLixDrvIterateList failed, status = 0x%08x\n", status);
2447  }
2448 
2450  goto _exit;
2451  }
2452 
2453  status = IntLixApiHookAll();
2454  if (!INT_SUCCESS(status))
2455  {
2456  ERROR("[ERROR] IntLixHookAll failed with status: 0x%08x", status);
2457  goto _exit;
2458  }
2459 
2460  status = IntLixGuestActivateProtection();
2461  if (!INT_SUCCESS(status))
2462  {
2463  ERROR("[ERROR] IntLixGuestActivateProtection failed: 0x%08x\n", status);
2464  goto _exit;
2465  }
2466 
2467  status = IntNotifyIntroActive();
2468  if (!INT_SUCCESS(status))
2469  {
2470  ERROR("[ERROR] IntNotifyIntroActive failed: 0x%08x\n", status);
2471  }
2472 
2474 
2475  IntResumeVcpus();
2476 
2477  return INT_STATUS_SUCCESS;
2478 
2479 _exit:
2480  IntResumeVcpus();
2482 
2483  return INT_STATUS_SUCCESS;
2484 }
2485 
2486 
2487 static INTSTATUS
2489  void
2490  )
2497 {
2498  INTSTATUS status;
2499 
2501  if (pHandler == NULL)
2502  {
2503  return INT_STATUS_NOT_FOUND;
2504  }
2505 
2506  LIX_AGENT_INIT_ARGS *pArgs = pHandler->Args.Content;
2507 
2509 
2511  if (!INT_SUCCESS(status))
2512  {
2513  ERROR("[ERROR] IntLixAgentInject failed with status: 0x%08x.", status);
2514  return status;
2515  }
2516 
2517  TRACE("[LIXGUEST] Allocation agent injected...");
2518 
2519  return INT_STATUS_SUCCESS;
2520 }
2521 
2522 
2523 void
2525  void
2526  )
2530 {
2531  INTSTATUS status;
2532 
2533  if (!gLixGuest->MmAlloc.Agent.Initialized || gLixGuest->MmAlloc.Agent.Cleared ||
2534  !gLixGuest->MmAlloc.Detour.Initialized || gLixGuest->MmAlloc.Detour.Cleared)
2535  {
2536  return;
2537  }
2538 
2539  TRACE("[LIXGUEST] Clear the allocated guest memory...\n");
2540 
2541  status = IntLixGuestClearGuestMemory(gLixGuest->MmAlloc.Detour.Data.Address, gLixGuest->MmAlloc.Detour.Data.Length);
2542  if (!INT_SUCCESS(status))
2543  {
2544  ERROR("[ERROR] IntLixGuestClearGuestMemory failed with status: 0x%08x. (detour data)", status);
2545  }
2546 
2547  status = IntLixGuestClearGuestMemory(gLixGuest->MmAlloc.Agent.Address, gLixGuest->MmAlloc.Agent.Length);
2548  if (!INT_SUCCESS(status))
2549  {
2550  ERROR("[ERROR] IntLixGuestClearGuestMemory failed with status: 0x%08x. (agent content)", status);
2551  }
2552 
2554 
2555  gLixGuest->MmAlloc.Agent.Cleared = TRUE;
2556  gLixGuest->MmAlloc.Detour.Cleared = TRUE;
2557 }
2558 
2559 
2560 INTSTATUS
2562  void
2563  )
2573 {
2574  INTSTATUS status;
2575  QWORD originalSyscall, syscallGva, properSyscallGva, initPgd;
2576 
2577  if (!gGuest.Guest64)
2578  {
2579  // Just a sanity check... Theoretically syscall patterns shouldn't match, so there should be no way to
2580  // get here.
2581  return INT_STATUS_NOT_SUPPORTED;
2582  }
2583 
2584  // Uninitialize some things which may have been left here from the previous retry
2585  if (gLixGuest)
2586  {
2587  IntKsymUninit();
2588 
2589  memzero(gLixGuest, sizeof(*gLixGuest));
2590  }
2591 
2592  gLixGuest = &gGuest._LinuxGuest;
2593 
2594  status = IntSyscallRead(IG_CURRENT_VCPU, NULL, &syscallGva);
2595  if (!INT_SUCCESS(status))
2596  {
2597  ERROR("[ERROR] IntSyscallRead failed: 0x%08x\n", status);
2598  return status;
2599  }
2600 
2601  originalSyscall = syscallGva;
2602 
2603  TRACE("[INTRO-INIT] Found SYSCALL handler @ %llx\n", syscallGva);
2604 
2605  status = IntLixGuestFindProperSyscall(syscallGva, &properSyscallGva);
2606  if (INT_SUCCESS(status))
2607  {
2608  syscallGva = properSyscallGva;
2609 
2610  TRACE("[INTRO-INIT] Found SYSCALL handler @ %llx (the proper one)", syscallGva);
2611  }
2612 
2613  status = IntLixGuestIsKptiActive(originalSyscall);
2614  if (!INT_SUCCESS(status))
2615  {
2616  ERROR("[ERROR] IntLixGuestIsKptiActive failed: 0x%08x\n", status);
2617  return status;
2618  }
2619 
2620  status = IntLixGuestFindKernel(syscallGva);
2621  if (!INT_SUCCESS(status))
2622  {
2623  WARNING("[WARNING] Failed locating the kernel image in memory starting from syscall %llx: %08X\n",
2624  syscallGva, status);
2625 
2627 
2628  return status;
2629  }
2630 
2631  // Fill the guest info
2635 
2636  if (!IntLixGuestIsSupported())
2637  {
2638  ERROR("[ERROR] Unsupported guest OS loaded, will NOT activate protection!\n");
2639 
2641 
2642  return INT_STATUS_NOT_SUPPORTED;
2643  }
2644 
2645  gLixGuest->SyscallAddress = originalSyscall;
2646 
2647  status = IntLixGuestInit();
2648  if (!INT_SUCCESS(status))
2649  {
2650  ERROR("[ERROR] IntLixGuestInit failed, status = 0x%08x\n", status);
2651  return status;
2652  }
2653 
2654  status = IntLixGuestFindPgd(&initPgd);
2655  if (!INT_SUCCESS(status))
2656  {
2657  QWORD initMmGva;
2658 
2659  ERROR("[ERROR] IntLixGuestFindPgd failed with status: 0x%08x\n", status);
2660 
2661  status = IntLixMmGetInitMm(&initMmGva);
2662  if (!INT_SUCCESS(status))
2663  {
2664  ERROR("[ERROR] Failed getting the init_mm: 0x%08x\n", status);
2665  goto _exit;
2666  }
2667 
2668  status = IntKernVirtMemFetchQword(initMmGva + LIX_FIELD(MmStruct, Pgd), &initPgd);
2669  if (!INT_SUCCESS(status))
2670  {
2671  ERROR("[ERROR] IntKernVirtMemFetchQword failed for %llx: 0x%08x\n",
2672  initMmGva + LIX_FIELD(MmStruct, Pgd), status);
2673  goto _exit;
2674  }
2675  }
2676 
2677  status = IntTranslateVirtualAddress(initPgd, 0, &gGuest.Mm.SystemCr3);
2678  if (!INT_SUCCESS(status))
2679  {
2680  ERROR("[ERROR] Translating init PGD failed, status = 0x%08x\n", status);
2681  goto _exit;
2682  }
2683 
2684  TRACE("[INTRO-INIT] Found SystemCr3 @ %llx\n", gGuest.Mm.SystemCr3);
2685 
2686  for (DWORD i = 0; i < gGuest.CpuCount; i++)
2687  {
2688  QWORD idtBase;
2689  WORD idtLimit;
2690 
2691  status = IntIdtFindBase(i, &idtBase, &idtLimit);
2692  if (!INT_SUCCESS(status))
2693  {
2694  ERROR("[ERROR] CPU %d doesn't appear to be used, skipping IDT...\n", i);
2695  continue;
2696  }
2697 
2698  gGuest.VcpuArray[i].IdtBase = idtBase;
2699  gGuest.VcpuArray[i].IdtLimit = idtLimit;
2700  }
2701 
2702  IntLixAgentInit();
2703 
2705 
2706  status = IntLixGuestAllocate();
2707  if (!INT_SUCCESS(status))
2708  {
2709  ERROR("[ERROR] IntLixGuestAllocate failed with status: 0x%08x.", status);
2710  goto _exit;
2711  }
2712 
2714 
2715  status = INT_STATUS_SUCCESS;
2716 
2717 _exit:
2718  if (!INT_SUCCESS(status))
2719  {
2721 
2723  }
2724 
2725  return status;
2726 }
2727 
2728 
2729 INTSTATUS
2731  _In_ DWORD FullStringSize,
2732  _In_ DWORD VersionStringSize,
2733  _Out_ CHAR *FullString,
2734  _Out_ CHAR *VersionString
2735  )
2749 {
2750  DWORD startOfDistroName = 0;
2751  int count = 0;
2752  DWORD endOfDistroName = 0;
2753  DWORD sizeOfString;
2754 
2755  // OK to type cast to DWORD here, the internal VersionString won't exceed 2G...
2756  sizeOfString = (DWORD)strlen(gLixGuest->VersionString);
2757 
2758  if (sizeOfString >= FullStringSize)
2759  {
2761  }
2762 
2763  while (startOfDistroName < sizeOfString && count != 3)
2764  {
2765  if (gLixGuest->VersionString[startOfDistroName] == '(')
2766  {
2767  ++count;
2768  }
2769 
2770  ++startOfDistroName;
2771  }
2772 
2773  if (count < 3)
2774  {
2776  }
2777 
2778  if (startOfDistroName >= sizeOfString)
2779  {
2781  }
2782 
2783  endOfDistroName = startOfDistroName;
2784  while (endOfDistroName < sizeOfString &&
2785  (IN_RANGE_INCLUSIVE(gLixGuest->VersionString[endOfDistroName], 'a', 'z') ||
2786  IN_RANGE_INCLUSIVE(gLixGuest->VersionString[endOfDistroName], 'A', 'Z') ||
2787  gLixGuest->VersionString[endOfDistroName] == ' '))
2788  {
2789  endOfDistroName += 1;
2790  }
2791 
2792  if (endOfDistroName >= sizeOfString)
2793  {
2795  }
2796 
2797  strcpy(FullString, gLixGuest->VersionString);
2798 
2799  count = snprintf(VersionString, VersionStringSize, "Kernel: %d.%d.%d-%d distro: ",
2800  gLixGuest->Version.Version,
2801  gLixGuest->Version.Patch,
2802  gLixGuest->Version.Sublevel,
2803  gLixGuest->Version.Backport);
2804  if (count < 0)
2805  {
2807  }
2808 
2809  if ((DWORD)count >= VersionStringSize)
2810  {
2812  }
2813 
2814  if (gLixGuest->VersionString[endOfDistroName] == ')')
2815  {
2816  ++endOfDistroName;
2817  }
2818 
2819  if (endOfDistroName - startOfDistroName >= VersionStringSize - count)
2820  {
2822  }
2823 
2824  // Oracle linux has a kernel identically with Red Hat one, so the only special identification is the
2825  // "el7uek" string in the version
2826  if (strstr(FullString, "el7uek"))
2827  {
2828  snprintf(VersionString + count, sizeof("Oracle"), "%s", "Oracle");
2829  }
2830  else
2831  {
2832  snprintf(VersionString + count, endOfDistroName - startOfDistroName, "%s", FullString + startOfDistroName);
2833  }
2834 
2835  return INT_STATUS_SUCCESS;
2836 }
2837 
struct _LIX_GUEST_OS_SPECIFIC::@265 File
#define INT_STATUS_GUEST_OS_NOT_SUPPORTED
Indicates that the guest operating system is not supported.
Definition: introstatus.h:446
TIMER_FRIENDLY void IntDumpArchRegs(IG_ARCH_REGS const *Registers)
This function dumps the register values in a user friendly format.
Definition: dumper.c:20
void IntLixAgentDisablePendingAgents(void)
Disables all pending agents.
Definition: lixagent.c:1844
BOOLEAN IntDetIsPtrInRelocatedCode(QWORD Ptr, DETOUR_TAG *Tag)
Checks if a guest pointer is inside the modified prologue of a function.
Definition: detours.c:1942
#define _In_opt_
Definition: intro_sal.h:16
#define PT_XD
Definition: pgtable.h:92
QWORD PhysicalAddress
The physical address to which VirtualAddress translates to.
Definition: introcore.h:107
DWORD CurrentCpuOffset
The offset of the CPU from GS.
Definition: lixguest.h:414
LIX_OPAQUE_FIELDS OsSpecificFields
OS-dependent and specific information.
Definition: lixguest.h:579
#define __unlikely(x)
Definition: common.h:64
LIST_HEAD gKernelDrivers
List of all the drivers currently loaded inside the guest.
Definition: drivers.c:11
#define _Out_
Definition: intro_sal.h:22
_Bool BOOLEAN
Definition: intro_types.h:58
INTSTATUS IntIdtFindBase(DWORD CpuNumber, QWORD *Base, WORD *Limit)
Returns the IDT base and limit for a guest CPU.
Definition: introcpu.c:102
#define CONTAINING_RECORD(List, Type, Member)
Definition: introlists.h:36
#define ROUND_UP(what, to)
Definition: introdefs.h:158
INTSTATUS IntLixMmGetInitMm(QWORD *InitMm)
Find the address of the "init_mm" variable inside the kernel.
Definition: lixmm.c:76
int IntLixGuestGetSystemState(void)
Get the system state of the Linux guest.
Definition: lixguest.c:2201
void IntGuestSetIntroErrorState(INTRO_ERROR_STATE State, INTRO_ERROR_CONTEXT *Context)
Updates the value of the gErrorState and the value of the gErrorStateContext.
Definition: guests.c:90
HOOK_HEADER Header
Hook header.
Definition: hook_gpa.h:43
#define IC_TAG_CAMI
Live update allocations.
Definition: memtags.h:124
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
Success.
Definition: intro_types.h:2435
Describes a handlers that contains the data required by the agent.
Definition: lixagent.h:217
struct _LIX_GUEST_OS_SPECIFIC::@263 Task
struct _LIX_AGENT_UNINIT_ARGS::@107 Attr
struct _LIX_GUEST_OS_SPECIFIC::@262 Mm
struct _LINUX_GUEST::@128 MmAlloc
static INTSTATUS IntLixGuestFindPgd(QWORD *Pgd)
Searches for the system CR3.
Definition: lixguest.c:1285
QWORD OriginalPagesAttr
The original page protection-attributes for the allocated region.
Definition: lixguest.h:576
INTSTATUS IntLixDrvCreateKernel(void)
Create the KERNEL_DRIVER object for the operating system kernel and activate the protection for it...
Definition: lixmodule.c:1452
struct _LINUX_GUEST::@128::@132 Agent
QWORD End
The end guest virtual address of ksym (exclusive).
Definition: lixguest.h:439
static INTSTATUS IntLixResolveThreadStructOffset(void)
Decodes each instruction of the &#39;set_tls_desc&#39; function and searches for &#39;MOV RDI, immediate&#39; pattern in order to find the &#39;task_struct->thread_struct&#39; offset.
Definition: lixguest.c:667
uint8_t BYTE
Definition: intro_types.h:47
struct _LINUX_GUEST::@128::@133 PerCpuData
QWORD ModuleAddress
The address of the allocated memory (module).
Definition: lixagent.h:272
BOOLEAN Terminating
Definition: guests.h:314
INTSTATUS IntKernVirtMemWrite(QWORD KernelGva, DWORD Length, void *Buffer)
Writes data to a guest kernel virtual memory range.
Definition: introcore.c:699
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
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
INTSTATUS IntIdtrProtect(void)
Enable IDTR protection.
INTSTATUS IntLixKernelReadProtect(void)
Activates kernel protection.
Definition: lixkernel.c:781
static INTSTATUS IntLixResolveCurrentCpuOffset(void)
Searches for the &#39;cpu_number&#39; offset.
Definition: lixguest.c:593
LIX_AGENT_HANDLER * IntLixAgentGetHandlerByTag(LIX_AGENT_TAG AgentTag)
Iterates through all agent handlers and search the entry that has the provided tag.
Definition: lixaghnd.c:408
#define _In_
Definition: intro_sal.h:21
INTSTATUS IntLixTaskAdd(QWORD TaskGva, QWORD StaticDetected)
Creates and adds a Linux process in the internal list.
Definition: lixprocess.c:3996
QWORD SystemCr3
The Cr3 used to map the kernel.
Definition: guests.h:211
#define INT_STATUS_SUCCESS
Definition: introstatus.h:54
#define PAGE_REMAINING(addr)
Definition: pgtable.h:163
uint16_t WORD
Definition: intro_types.h:48
#define CSTRLEN(String)
Definition: introdefs.h:105
INT32 __cdecl strtol(const INT8 *nptr, INT8 **endptr, INT32 ibase)
Definition: conv.c:106
static INTSTATUS IntLixResolveExeFileOffset(void)
Decodes each instruction of the &#39;get_mm_exe_file&#39; function and searches for &#39;MOV REG, [RDI + Displacement]&#39; pattern in order to find the &#39;mm_struct->exe_file&#39; offset.
Definition: lixguest.c:731
DWORD KernelSize
The size of the kernel.
Definition: guests.h:284
#define INTRO_OPT_PROT_KM_LX
Enable kernel image protection (Linux only).
Definition: intro_types.h:409
WORD IdtLimit
The current IDT limit.
Definition: guests.h:111
struct _LIST_ENTRY * Flink
Definition: introlists.h:20
QWORD Start
The start guest virtual address of ksym.
Definition: lixguest.h:438
QWORD RoDataStart
The guest virtual address where the read-only data starts.
Definition: lixguest.h:503
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
static INTSTATUS IntLixGuestParseVersion(const char *Buffer, DWORD BufferLength)
Parses the &#39;linux_proc_banner&#39; and searches for &#39;version.patch.sublevel-backport&#39; pattern...
Definition: lixguest.c:66
#define IN_RANGE_INCLUSIVE(x, start, end)
Definition: introdefs.h:171
#define INT_SUCCESS(Status)
Definition: introstatus.h:42
A critical structure was not found inside the guest kernel.
Definition: intro_types.h:2441
struct _LIST_ENTRY * Head
Definition: introlists.h:21
INTSTATUS IntResumeVcpus(void)
Resumes the VCPUs previously paused with IntPauseVcpus.
Definition: introcore.c:2355
QWORD PerCpuAddress
The address of the allocated memory (per-CPU).
Definition: lixagent.h:273
BOOLEAN ProtectionActivated
Definition: guests.h:297
QWORD PerCpuLength
The per-CPU memory allocation size.
Definition: lixagent.h:260
#define ARRAYSIZE(A)
Definition: introdefs.h:101
#define INTRO_OPT_PROT_KM_LX_TEXT_READS
Enable kernel &#39;_text&#39; section read protection (Linux only).
Definition: intro_types.h:499
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
BOOLEAN SafeToApplyOptions
True if the current options can be changed dynamically.
Definition: guests.h:294
BYTE EptHookType
The type of the hook in EPT (see IG_EPT_HOOK_TYPE)
Definition: hook.h:69
INTSTATUS IntLixGuestFindKernelVersionAndRo(QWORD StartGva)
Scans pages from guest memory, starting from the provided StartGva and tries to find the ...
Definition: lixguest.c:175
unsigned int VmPrevOffset
Definition: handlers.h:55
#define INT_STATUS_NOT_NEEDED_HINT
Definition: introstatus.h:317
Section will contain linux related information.
Definition: update_guests.h:48
#define ERROR(fmt,...)
Definition: glue.h:62
static INTSTATUS IntLixGuestResolveExTableLimits(void)
Decodes each instruction of the &#39;search_exception_tables&#39; function and searches for &#39;MOV REG/RSI...
Definition: lixguest.c:442
INTSTATUS IntLixAgentUninit(void)
Uninit the agents state.
Definition: lixagent.c:1997
struct _LIX_GUEST_OS_SPECIFIC::@260 Info
INTSTATUS IntLixGuestNew(void)
Starts the initialization and enable protection for a new Linux guest.
Definition: lixguest.c:2561
DWORD IntPatternMatch(const BYTE *Buffer, DWORD SigCount, const PATTERN_SIGNATURE *Sigs)
Matches one of the given signatures on the given buffer.
Definition: patsig.c:9
int INTSTATUS
The status data type.
Definition: introstatus.h:24
static INTSTATUS IntLixResolveCurrentProcessOffset(void)
Decodes each instruction of the &#39;do_exit&#39; function and searches for &#39;MOV REG/MEM, [gs:displacement]&#39; ...
Definition: lixguest.c:541
The operating system version is not supported.
Definition: intro_types.h:2437
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
INTSTATUS IntMsrSyscallProtect(void)
Enable protection for all SYSCALL and SYSENTER MSRs.
#define PAGE_OFFSET_MASK_2M
Definition: pgtable.h:16
DWORD OSVersion
Os version.
Definition: guests.h:281
QWORD gEventId
The ID of the current event.
Definition: glue.c:55
LIX_MODULE_LAYOUT CoreLayout
The layout of the core section.
Definition: lixmodule.h:40
#define LIX_MODULE_MAPPING_SPACE_END
The end of module mapping region.
Definition: lixguest.c:45
#define INT_STATUS_NOT_FOUND
Definition: introstatus.h:284
unsigned char gLixAgents[]
Definition: agents_content.h:1
static INTSTATUS IntLixGuestAgentContentHandler(void **Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Dumps information about the read/write attempt.
Definition: lixguest.c:1792
static INTSTATUS IntLixGuestAllocateInit(void)
Initialize the required information about the allocated memory zone for detours/agents.
Definition: lixguest.c:2312
#define ONE_KILOBYTE
Definition: introdefs.h:89
DWORD ThreadStructOffset
The offset of the thread_struct from task_struct.
Definition: lixguest.h:415
QWORD IntroActiveEventId
The event ID on which introcore became active.
Definition: guests.h:381
static void IntLixGuestSetOsVersion(void)
Computes the OS version number using the version, patch and sublevel.
Definition: lixguest.c:52
#define TRFLG_NONE
No special options.
Definition: introcore.h:82
INTSTATUS IntLixGuestFindProperSyscall(QWORD SyscallAddress, QWORD *ProperSyscallAddress)
Decodes each instruction from the provided syscall handler address and searches for a pattern if the ...
Definition: lixguest.c:805
struct _LIX_GUEST_OS_SPECIFIC::@266 Dentry
PVCPU_STATE VcpuArray
Array of the VCPUs assigned to this guest. The index in this array matches the VCPU number...
Definition: guests.h:372
unsigned int DentryOffset
Definition: handlers.h:78
LIX_GUEST_OS_SPECIFIC OsSpecificFields
Definition: handlers.h:117
INTSTATUS IntCr4Unprotect(void)
Disables the CR4 protection.
INTSTATUS IntPauseVcpus(void)
Pauses all the guest VCPUs.
Definition: introcore.c:2320
void IntLixVdsoUnprotect(void)
Remove protection for the vDSO image and VSYSCALL.
Definition: lixvdso.c:928
struct _LIX_AGENT_INIT_ARGS::@105 Allocate
#define INTRO_OPT_PROT_KM_IDT
Definition: intro_types.h:412
INTRO_GUEST_TYPE OSType
The type of the guest.
Definition: guests.h:278
unsigned int FileOffset
Definition: handlers.h:53
INTSTATUS IntLixTextPokeHandler(void *Detour)
Handles the incoming &#39;text_poke&#39; patches from the guest.
Definition: lixguest.c:1463
#define MIN(a, b)
Definition: introdefs.h:146
Will write the contents of the patched data inside the guest.
Definition: memcloak.h:54
unsigned int Gid
Definition: handlers.h:89
INTSTATUS IntNotifyIntroActive(void)
Definition: glue.c:927
#define INTRO_OPT_PROT_KM_VDSO
Enable vDSO image protection (Linux only).
Definition: intro_types.h:500
Section will contain information about a supported OS.
Definition: update_guests.h:42
#define LOG(fmt,...)
Definition: glue.h:61
Describes a kernel driver.
Definition: drivers.h:30
INTSTATUS IntGdtrProtect(void)
Enable GDTR protection.
#define PAGE_BASE_MASK_2M
Definition: pgtable.h:18
BOOLEAN KptiActive
True if KPTI is enabled on this guest, False if it is not.
Definition: guests.h:291
#define INTRO_OPT_PROT_KM_MSR_SYSCALL
Definition: intro_types.h:427
static INTSTATUS IntLixGuestFindKernel(QWORD SyscallHandler)
Finds the most things required by Introcore to be able to initialize completely.
Definition: lixguest.c:391
QWORD ExTableStart
The guest virtual address where the ex-table starts.
Definition: lixguest.h:506
static INTSTATUS IntLixGuestDetourDataHandler(void **Context, void *Hook, QWORD Address, INTRO_ACTION *Action)
Dumps information about the read/write attempt.
Definition: lixguest.c:1752
DWORD MappingsCount
The number of entries inside the MappingsTrace and MappingsEntries arrays.
Definition: introcore.h:123
Used for &#39;arch_jump_label_transform&#39;.
Definition: lixguest.h:451
struct _LINUX_GUEST::@128::@131 Detour
void IntLixGuestUninit(void)
Uninitialize the Linux guest.
Definition: lixguest.c:1674
struct _LIX_AGENT_UNINIT_ARGS::@106 Free
#define PT_RW
Definition: pgtable.h:84
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
unsigned int Rb
Definition: handlers.h:56
INTSTATUS IntVirtMemSet(QWORD VirtualAddress, DWORD Length, QWORD Cr3, BYTE Value)
Definition: introcore.c:414
INTSTATUS IntLixGuestAllocateHook(void)
Add EPT hooks for the detours and agents.
Definition: lixguest.c:2118
INTSTATUS IntLixIdtUnprotectAll(void)
Disable protection for IDT on all CPUs.
Definition: lixidt.c:261
QWORD Flags
The entry that maps VirtualAddress to PhysicalAddress, together with all the control bits...
Definition: introcore.h:119
#define _In_reads_z_(expr)
Definition: intro_sal.h:36
INTSTATUS IntKernVirtMemFetchDword(QWORD GuestVirtualAddress, DWORD *Data)
Reads 4 bytes from the guest kernel memory.
Definition: introcore.c:829
INTSTATUS IntLixGuestIsKptiActive(QWORD SyscallGva)
Checks if the Linux guest has the KPTI active.
Definition: lixguest.c:1032
QWORD DataStart
The guest virtual address where the data starts.
Definition: lixguest.h:500
unsigned int CurrentCpuOffset
Definition: handlers.h:93
static INTSTATUS IntLixGuestClearGuestMemory(QWORD Gva, DWORD Length)
Clear the provided memory zone.
Definition: lixguest.c:1964
#define IS_KERNEL_POINTER_LIX(p)
Definition: lixguest.h:11
#define INT_STATUS_NOT_INITIALIZED
Definition: introstatus.h:266
#define SIG_NOT_FOUND
Signals that a signature was not matched.
Definition: patsig.h:13
d_path_fn * DPathFnPtr
Definition: handlers.h:96
INTSTATUS IntKernVirtMemFetchQword(QWORD GuestVirtualAddress, QWORD *Data)
Reads 8 bytes from the guest kernel memory.
Definition: introcore.c:811
unsigned int InExecveBit
Definition: handlers.h:70
The kernel image was not found.
Definition: intro_types.h:2438
#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
struct _LINUX_GUEST::@126 Layout
Used for &#39;text_poke&#39;.
Definition: lixguest.h:449
QWORD MappingsEntries[MAX_TRANSLATION_DEPTH]
Contains the entry in which paging table.
Definition: introcore.h:115
INTSTATUS IntGetVersionStringLinux(DWORD FullStringSize, DWORD VersionStringSize, CHAR *FullString, CHAR *VersionString)
Gets the version string for a Linux guest.
Definition: lixguest.c:2730
#define IN_RANGE(x, start, end)
Definition: introdefs.h:167
#define memzero(a, s)
Definition: introcrt.h:35
#define PT_P
Definition: pgtable.h:83
BOOLEAN Guest64
True if this is a 64-bit guest, False if it is a 32-bit guest.
Definition: guests.h:290
Describes a Linux guest.
Definition: lixguest.h:476
unsigned long long QWORD
Definition: intro_types.h:53
QWORD Current
The currently used options.
Definition: guests.h:236
PATTERN_SIGNATURE * gLinuxDistSigs
An array that contains the distro signatures.
Definition: lixguest.c:35
#define INTRO_OPT_PROT_KM_IDTR
Enable interrupt descriptor-table registers protection.
Definition: intro_types.h:429
QWORD IdtBase
Original IDT base.
Definition: guests.h:110
CHAR VersionString[MAX_VERSION_LENGTH]
The version string.
Definition: lixguest.h:492
INTSTATUS IntLixVdsoProtect(void)
Activates protection for the vDSO image and VSYSCALL.
Definition: lixvdso.c:883
unsigned int InodeOffset
Definition: handlers.h:83
INTSTATUS IntTranslateVirtualAddress(QWORD Gva, QWORD Cr3, QWORD *PhysicalAddress)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1999
#define TRUE
Definition: intro_types.h:30
unsigned int Uid
Definition: handlers.h:88
INTSTATUS IntDecDecodeInstructionFromBuffer(PBYTE Buffer, size_t BufferSize, IG_CS_TYPE CsType, void *Instrux)
Decode an instruction from the provided buffer.
Definition: decoder.c:308
BOOLEAN GuestInitialized
True if the OS-specific portion has been initialized.
Definition: guests.h:293
static BOOLEAN IntLixGuestIsSupported(void)
Load OS information from CAMI if the guest is supported.
Definition: lixguest.c:1727
#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
INTSTATUS IntGsRead(DWORD CpuNumber, QWORD *GsValue)
Reads the IA32_GS_BASE guest MSR.
Definition: introcpu.c:289
unsigned int CurrentTaskOffset
Definition: handlers.h:92
#define INT_STATUS_INVALID_INTERNAL_STATE
Definition: introstatus.h:272
INTSTATUS IntMemClkCloakRegion(QWORD VirtualAddress, QWORD Cr3, DWORD Size, DWORD Options, PBYTE OriginalData, PBYTE PatchedData, PFUNC_IntMemCloakWriteHandle WriteHandler, void **CloakHandle)
Hides a memory zone from the guest.
Definition: memcloak.c:548
QWORD KernelVa
The guest virtual address at which the kernel image.
Definition: guests.h:283
#define LIX_KERNEL_MAX_PAGES
The maximum number of pages of kernel that will be scanned.
Definition: lixguest.c:40
QWORD MaskSet
The page attributes that must be set.
Definition: lixagent.h:279
LINUX_GUEST * gLixGuest
Global variable holding the state of a Linux guest.
Definition: lixguest.c:30
LIX_SYMBOL MemoryFunctions[5]
The guest virtual address of memcpy, __memcpy, memset, __memset, memmove.
Definition: lixguest.h:513
BYTE WordSize
Guest word size. Will be 4 for 32-bit guests and 8 for 64-bit guests.
Definition: guests.h:367
#define LIX_BANNER_START
The start of the &#39;linux_proc_banner&#39; string.
Definition: lixguest.c:42
void IntLixGuestUnhookGuestCode(void)
Remove the EPT hooks and memcloack from detours and agents.
Definition: lixguest.c:2073
unsigned int InExecve
Definition: handlers.h:69
Arguments of the uninit agent.
Definition: lixagent.h:268
INTSTATUS IntKsymInit(void)
Initialize the kallsyms subsystem based on the os info provided by LIX_FIELD(Info, HasKsym*).
Definition: lixksym.c:1046
INTSTATUS IntTranslateVirtualAddressEx(QWORD Gva, QWORD Cr3, DWORD Flags, VA_TRANSLATION *Translation)
Translates a guest virtual address to a guest physical address.
Definition: introcore.c:1863
struct _LIX_AGENT_HANDLER::@101 Args
static INTSTATUS IntLixGuestAllocate(void)
Injects the &#39;init&#39; agent in order to allocate a memory zone inside the guest.
Definition: lixguest.c:2488
BOOLEAN IntLixGuestDeployUninitAgent(void)
Inject the &#39;uninit&#39; agent to free the previously allocated memory for detours/agents.
Definition: lixguest.c:2238
BOOLEAN UninitPrepared
Definition: guests.h:320
LIX_KERNEL_MODULE Lix
Valid only for Linux guests.
Definition: drivers.h:71
#define WARNING(fmt,...)
Definition: glue.h:60
struct _LIX_GUEST_OS_SPECIFIC::@264 Binprm
static INTSTATUS IntLixGuestResolveOffsets(void)
Finds the offsets required by Introcore.
Definition: lixguest.c:880
LIX_FUNCTION * Functions
An array of LIX_FUNCTION to be hooked.
Definition: lixguest.h:391
DWORD CpuCount
The number of logical CPUs.
Definition: guests.h:279
#define LIX_KAISER_ENABLED_PCP_OFFSET_CAP
The max value of &#39;kaiser_enabled_pcp&#39; offset (the maximum observed was 0xD040 on CentOS - kernel 3...
Definition: lixguest.c:48
#define PAGE_SIZE
Definition: common.h:70
#define UNREFERENCED_PARAMETER(P)
Definition: introdefs.h:29
unsigned int ProtectionBit
Definition: handlers.h:58
INTSTATUS IntLixApiHookAll(void)
Iterates through all APIs that can be hooked and sets requested hooks.
Definition: lixapi.c:257
void IntDriverUninit(void)
Uninitializes the drivers submodule.
Definition: drivers.c:354
#define INT_STATUS_DATA_BUFFER_TOO_SMALL
Definition: introstatus.h:194
uint32_t DWORD
Definition: intro_types.h:49
static INTSTATUS IntLixPatchHandler(void *Detour, LIX_ACTIVE_PATCH *ActivePatch)
Handles the incoming patches (ftrace/text_poke) from the guest.
Definition: lixguest.c:1388
#define INT_STATUS_INVALID_DATA_VALUE
Definition: introstatus.h:136
void IntLixAgentInit(void)
Initialize the agents state.
Definition: lixagent.c:1978
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
INTSTATUS IntSyscallRead(DWORD CpuNumber, QWORD *SysStar, QWORD *SysLstar)
Queries the IA32_STAR, and IA32_LSTAR guest MSRs.
Definition: introcpu.c:635
INTSTATUS IntLixGuestAllocateDeploy(void)
Deploys the content of Linux detours and the content of the Linux agents.
Definition: lixguest.c:1829
unsigned char gLixDetours[]
INTSTATUS IntLixFtraceHandler(void *Detour)
Handles the incoming &#39;text_poke&#39; patches from the guest.
Definition: lixguest.c:1481
QWORD DataEnd
The guest virtual address where the data ends.
Definition: lixguest.h:501
enum _INTRO_ACTION INTRO_ACTION
Event actions.
INTSTATUS IntLixAgentInject(LIX_AGENT_TAG Tag, PFUNC_AgentCallbackHypercall HypercallCallback, PFUNC_AgentCallbackCompletion CompletionCallback)
Schedule an agent injection inside the guest.
Definition: lixagent.c:896
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
unsigned int MmOffset
Definition: handlers.h:51
static INTSTATUS IntLixGuestAllocateFill(void)
Fill the required information about the allocated memory zone from the guest.
Definition: lixguest.c:2009
void IntLixFilesCacheUninit(void)
Removes and frees the entries of the dentry-cache.
Definition: lixfiles.c:86
unsigned int PathOffset
Definition: handlers.h:79
#define SIGN_EX_32(x)
Definition: introdefs.h:202
QWORD RoDataEnd
The guest virtual address where the read-only data ends.
Definition: lixguest.h:504
__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
#define LIX_MODULE_MAPPING_SPACE_START
The start of module mapping region.
Definition: lixguest.c:44
void IntLixTaskUninit(void)
Uninitializes the Linux process subsystem.
Definition: lixprocess.c:4570
MM Mm
Guest memory information, such as paging mode, system Cr3 value, etc.
Definition: guests.h:374
static INTSTATUS IntLixGuestInitAgentHypercall(void *Context)
This callback is called when the &#39;init&#39; agent has been allocated the memory zone from guest...
Definition: lixguest.c:2349
BOOLEAN IntLixTaskGuestTerminating(void)
Check whether the guest OS is terminating or not.
Definition: lixprocess.c:4923
QWORD MaskClear
The page attributes that must be cleared.
Definition: lixagent.h:278
void IntLixGuestUninitGuestCode(void)
Removes the EPT hooks from detours/agents memory zone and clears these memory zones.
Definition: lixguest.c:2524
GUEST_STATE gGuest
The current guest state.
Definition: guests.c:50
void * Content
The content of the arguments.
Definition: lixagent.h:225
unsigned int VmNextOffset
Definition: handlers.h:54
QWORD PageSize
The page size used for this translation.
Definition: introcore.h:121
#define STATIC_ASSERT(Cond, Msg)
Definition: introdefs.h:50
INTSTATUS IntKernVirtMemRead(QWORD KernelGva, DWORD Length, void *Buffer, DWORD *RetLength)
Reads data from a guest kernel virtual memory range.
Definition: introcore.c:674
DWORD gLinuxDistSigsCount
The number of distro signatures from gLinuxDistSigs.
Definition: lixguest.c:37
LINUX_GUEST _LinuxGuest
Linux specific information. Valid when OSType is introGuestLinux.
Definition: guests.h:419
BOOLEAN KptiInstalled
True if KPTI was detected as installed (not necessarily active).
Definition: guests.h:292
unsigned int Mode
Definition: handlers.h:87
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
__must_check INTSTATUS IntPhysMemMap(QWORD PhysAddress, DWORD Length, DWORD Flags, void **HostPtr)
Maps a guest physical address inside Introcore VA space.
Definition: glue.c:338
INTSTATUS IntLixIdtProtectAll(void)
Activates protection for IDT on all CPUs.
Definition: lixidt.c:234
Encapsulates information about a virtual to physical memory translation.
Definition: introcore.h:102
QWORD SyscallAddress
The guest virtual address of the syscall.
Definition: lixguest.h:529
void IntKsymUninit(void)
Definition: lixksym.c:1256
static INTSTATUS IntLixGuestFindKernelBase(QWORD StartGva)
Scans pages from guest memory, starting from the provided StartGva, until we find a signature that ma...
Definition: lixguest.c:328
#define INT_STATUS_INVALID_PARAMETER_1
Definition: introstatus.h:62
#define INT_STATUS_NOT_SUPPORTED
Definition: introstatus.h:287
INTSTATUS IntMemClkUncloakRegion(void *CloakHandle, DWORD Options)
Removes a cloak region, making the original memory contents available again to the guest...
Definition: memcloak.c:970
VCPU_STATE * gVcpu
The state of the current VCPU.
Definition: guests.c:59
The init agent.
Definition: lixagent.h:62
static INTSTATUS IntLixGuestActivateProtection(void)
Activates the protection for a Linux guest.
Definition: lixguest.c:941
static INTSTATUS IntLixGuestInit(void)
Initializes a new Linux guest.
Definition: lixguest.c:1532
64-bit selector.
Definition: glueiface.h:188
static INTSTATUS IntLixFindDataStart(void)
Decodes each instruction of the &#39;mark_rodata_ro&#39; function and searches for end of ...
Definition: lixguest.c:1175
unsigned int FlagsOffset
Definition: handlers.h:52
QWORD Base
The base GVA of the section.
Definition: lixmodule.h:19
Describes a signature that can be used for searching or matching guest contents.
Definition: patsig.h:23
DWORD ActiveCpuCount
The number of CPUs actually used by the guest.
Definition: guests.h:280
Holds register state.
Definition: glueiface.h:30
#define INTRO_OPT_PROT_KM_CR4
Enable CR4.SMEP and CR4.SMAP protection.
Definition: intro_types.h:426
void IntDisasmGva(QWORD Gva, DWORD Length)
This function disassembles a code buffer (given its GVA) and then dumps the instructions (textual dis...
Definition: dumper.c:432
Used for &#39;ftrace&#39;.
Definition: lixguest.h:450
Section will contain distribution signatures.
Definition: update_guests.h:44
Execute-access hook.
Definition: glueiface.h:300
INTSTATUS IntLixJumpLabelHandler(void *Detour)
Handles the incoming read (arch_jmp_label_transform) from the guest.
Definition: lixguest.c:1497
BOOLEAN DisableOnReturn
Set to True if after returning from this event handler, introcore must be unloaded.
Definition: guests.h:328
QWORD Gva
The start of the region which follows to be patched.
Definition: lixguest.h:463
BYTE Version
The version field of the version string.
Definition: lixguest.h:487
char CHAR
Definition: intro_types.h:56
static void IntLixGuestResolveSymbols(void)
Searches for the &#39;memcpy&#39;, &#39;__memcpy&#39;, &#39;memset&#39;, &#39;__memset&#39; and &#39;memmove&#39; ksyms.
Definition: lixguest.c:515
QWORD IntKsymFindByName(const char *Name, QWORD *SymEnd)
Searches the given Name in kallsyms and returns the Start & End offset.
Definition: lixksym.c:1399
INTSTATUS IntLixKernelWriteProtect(void)
Activates kernel protection.
Definition: lixkernel.c:754
INTSTATUS IntCr4Protect(void)
Activates the Cr4 protection.
DWORD CurrentTaskOffset
The offset of the current task from GS.
Definition: lixguest.h:413
static INTSTATUS IntLixGuestInitAgentCompletion(void *Context)
This callback is called when the &#39;init&#39; agent completed the execution and the protection can be activ...
Definition: lixguest.c:2392
Write-access hook.
Definition: glueiface.h:299
INTSTATUS IntPhysMemUnmap(void **HostPtr)
Unmaps an address previously mapped with IntPhysMemMap.
Definition: glue.c:396
#define PAGE_MASK
Definition: pgtable.h:35
unsigned int CredAltered
Definition: handlers.h:47
INTSTATUS IntNotifyIntroDetectedOs(INTRO_GUEST_TYPE OsType, DWORD OsVersion, BOOLEAN Is64)
Wrapper over GLUE_IFACE.NotifyIntrospectionDetectedOs.
Definition: glue.c:955
struct _LIX_GUEST_OS_SPECIFIC::@267 Inode
INTRO_PROT_OPTIONS CoreOptions
The activation and protection options for this guest.
Definition: guests.h:271
INTSTATUS IntDecDecodeInstruction(IG_CS_TYPE CsType, QWORD Gva, void *Instrux)
Decode an instruction from the provided guest linear address.
Definition: decoder.c:180
QWORD ExTableEnd
The guest virtual address where the ex-table ends.
Definition: lixguest.h:507
INTSTATUS IntHookObjectCreate(DWORD ObjectType, QWORD Cr3, void **Object)
Create a new hook object.
Definition: hook_object.c:81
Arguments of the init agent.
Definition: lixagent.h:255
QWORD PropperSyscallGva
The guest virtual address of the &#39;real&#39; syscall.
Definition: lixguest.h:530
#define INTRO_OPT_PROT_KM_GDTR
Enable global descriptor-table registers protection.
Definition: intro_types.h:440
void IntLixAgentEnableInjection(void)
Enables agent injections.
Definition: lixagent.c:1964
INTSTATUS IntCamiLoadSection(DWORD CamiSectionHint)
Load CAMI objects from section with given hint.
struct _LIX_GUEST_OS_SPECIFIC::@261 Vma
The uninit agent.
Definition: lixagent.h:63
#define FALSE
Definition: intro_types.h:34
#define INT_STATUS_INSUFFICIENT_RESOURCES
Definition: introstatus.h:281