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