OS Support Mechanism

CAMI is an Introcore sub-module serving mainly as an information database specific to operating-systems. However, it may include other features to control Introspection behavior, such-as hooked kernel APIs or enforced options (forcing features to be on or off). Introcore will not protect a guest VM without the OS support binary file.

General architecture

On a base level, the OS specific information is stored in YAML files for easier maintenance. To serve them to Introcore in a safe and easy manner, these files are serialized into a binary file. The file binary is loaded in memory by the integrator and then handed-over to the introspection engine as parameters to the NewGuestNotification API. The currently loaded update file can be updated with the UpdateSupport API. However, the OS-specific part will not be updated, and Introcore must be restarted if an update is needed.

Dependencies

To build CAMI you need python 3 and the PyYAML library:

python3 -m pip install pyyaml

Automatically Adding Support for an OS

Adding support for an OS requires creating an YAML formatted file containing all information needed by Introspection regarding that OS type.

Windows OS

Generating a support file for a Windows OS requires radare2, and the r2pipe python library, as well as the pydis python wrapper over the Bitdefender Disassembler.

To generate the support file, supply the ntoskrnl.exe and the ntdll.dll available on that system to the r2cami.py script. This script will automatically download the debugging symbols and generate the support file. The script is found in the cami/tools/r2cami directory. The following is an example of how to create a support file:

python3 r2cami.py -k ntoskrnl.exe -n ntdll.dll -o windows_support.yaml

Linux OS

On the Linux side, the debugging symbols must be downloaded manually as the mirror locations differ for each distribution. Once the debugging symbols are available, the offsets.py script from the cami directory will automatically generate a suitable yaml file for that kernel. This script requires both gdb and pygdb.

python3 offsets.py --kernel=vmlinux-4.9.0-11-amd64 --out linux_support.yaml

Manually Adding Support for an OS

Windows Guest

Adding Guest Field Offsets

To add support for a new guest OS, one must create a new yaml file inside the cami/windows/opaque_fields/km and/or the cami/windows/opaque_fields/um for kernel and/or user mode fields.

Kernel mode fields

The file will conventionally be named windows_<nt_build_number>_x<arch>_kpti_<ON/OFF>.yml. For example, windows_7600_x64_kpti_OFF.yml for Windows 7 x64 with KPTI disabled and no service pack installed.

Note

The following examples do not necessarily contain valid offsets.

All yaml files must start with a ---. The following line must contain a yaml_tag describing the python class that will be responsible for parsing the current yaml structure. In this case, it is !intro_update_win_supported_os. The following 4 lines must contain the build_numberkpti_installedversion_string, and is_64 fields (on separate lines), populated with required information, as follows:

---
!intro_update_win_supported_os
build_number: 7600
version_string: !intro_update_win_version_string
    version_string: "Windows 7 x64"
    server_version_string: "Windows Server 2008 R2 x64"
kpti_installed: False
is_64: True

The :version_string contents are used by APIs like GetVersionString and serve only an information purpose. server_version_string serves the same purpose, but it is used for Windows Server editions. The contents are mandatory for generating a CAMI binary file.

Next, we populate the actual guest kernel mode fields. To do so, add the following lines:

km_fields: !opaque_structures
    type: win_km_fields
    os_structs:

The os_structs field will be populated with more collections of fields. For example, we need more than one field from the EPROCESS structure. Those fields are grouped under Process as follows:

Process: !opaque_fields
    Cr3: 0x28
    UserCr3: 0x28
    KexecOptions: 0x1bf
    ...

The keen eye will notice the !opaque_fields after each Process and the !opaque_structures after km_fields. Those are the same as the yaml_tag described at the beginning, and serve the same purpose - to tell the python class to which it belongs. Be sure to not forget about it.

The next tables describe all of the kernel mode fields, from which structure to extract them, and how to populate the yaml groups. All field offsets are relative to the start of the structure containing them.

Process

Used to describe a EPROCESS Windows kernel structure.

CAMI field name Description
Cr3 The offset of the Pcb.DirectoryTableBase field. It contains the CR3 used by a process. If KPTI is active this will be the CR3 used in ring 0.
UserCr3 The offset of the Pcb.UserDirectoryTableBase field. If KPTI is active this will be the CR3 used in ring 3. For operating systems without KPTI this will be the same as **Cr3
KexecOptions The offset of the **Pcb.Flags field.
ListEntry The offset of the ActiveProcessLinks field, containing the LIST_ENTRY structure used for the global kernel process list.
Name The offset of the **ImageFileName field.
SectionBase The offset of the **SectionBaseAddress field.
Id The offset of the UniqueProcessId field.
ParentPid The offset of the InheritedFromUniqueProcessId field.
VadRoot The offset of the VadRoot field.
CreateTime The offset of the CreateTime field.
ExitStatus The offset of the ExitStatus field.
Token The offset of the Token field.
ObjectTable The offset of the ObjectTable field.
Peb The offset of the Peb field.
ThreadListHead The offset of the Pcb.ThreadListHead field.
WoW64 The offset of the WoW64Process field.
Flags The offset of the Flags field.
Flags3 The offset of the Flags3 field.
MitigationFlags The offset of the MitigationFlags field.
MitigationFlags2 The offset of the MitigationFlags2 field.
DebugPort The offset of the DebugPort field.
Spare The offset of the Pcb.Spare1 field.
Thread

Used to describe a ETHREAD Windows kernel structure.

CAMI field name Description
Process The offset of the Tcb.Process field.
ThreadListEntry The offset of the Tcb.ThreadListEntry** field.
KernelStack The offset of the Tcb.KernelStack** field.
StackBase The offset of the Tcb.StackBase** field.
StackLimit The offset of the Tcb.StackLimit** field.
State The offset of the Tcb.State** field.
WaitReason The offset of the Tcb.WaitReason** field.
AttachedProcess The offset of the Tcb.ApcState.AttachedProcess** field.
Teb The offset of the Tcb.Teb** field.
Id The offset of the Tcb.Cid.UniqueThread** field.
ClientSecurity The offset of the ClientSecurity** field.
TrapFrame The offset of the Tcb.TrapFrame** field.
Win32StartAddress The offset of the Win32StartAddress** field.
PreviousMode The offset of the Tcb.PreviousMode** field.
DrvObj

Used to describe a DRIVER_OBJECT Windows kernel structure.

CAMI field name Description
FiodispSize The size of the FAST_IO_DISPATCH structure.
Fiodisp The offset of the FastIoDispatch field.
AllocationGap The gap between the pool header and the driver object.
Start The offset of the DriverObject field.
Size The size of the DRIVER_OBJECT structure.
Pcr

Used to describe a KPCR Windows kernel structure.

CAMI field name Description
CurrentThread The offset of the Pcrb.CurrentThread field.
UserTime The offset of the Pcrb.UserTime field.
PoolDescriptor

Used to describe a POOL_DESCRIPTOR Windows kernel structure.

CAMI field name Description
TotalBytes The offset of the BytesAllocated field.
NppSize The size of the non paged pool (usually hard-coded to 0x80000000)
Mmpfn

Used to describe a MMPFN Windows kernel structure. Note that most fields have two versions; one for PAE, and another for non-PAE systems. For 64-bit Windows versions the PAE version is ignored. For 32-bit Windows versions Introcore selects the correct field based on how the OS is configured. The non-PAE version is invalid for 32-bit Windows versions newer than Windows 7 because, starting with Windows 8, systems without PAE are no longer supported by the Windows kernel.

CAMI field name Description
Size The size of the structure. Valid for 64-bit Windows versions and for 32-bit versions with PAE disabled.
Pte The offset of the PteAddress field. Valid for 64-bit Windows versions and for 32-bit versions with PAE disabled.
RefCount The offset of the u3.ReferenceCount field. Valid for 64-bit Windows versions and for 32-bit versions with PAE disabled.
Flags The offset of the u3.Flags field. Valid for 64-bit Windows versions and for 32-bit versions with PAE disabled.
PaeSize The size of the structure. Valid for 32-bit Windows versions with PAE enabled.
PaePte The offset of the PteAddress field. Valid for 32-bit Windows versions with PAE enabled.
PaeRefCount The offset of the u3.ReferenceCount field. Valid for 32-bit Windows versions with PAE enabled.
PaeFlags The offset of the u3.Flags field. Valid for 32-bit Windows versions with PAE enabled.
Token

Used to describe a TOKEN Windows kernel structure.

CAMI field name Description
Privs The offset of the Privileges field.
UserCount The offset of the UserAndGroupCount field.
RestricredCount The offset of the RestrictedSidCount field.
Users The offset of the UserAndGroups field.
RestrictedSids The offset of the RestrictedSids field.
Ungrouped

Used to describe certain fields that are not organized in a dedicated CAMI structure.

CAMI field name Description
CtlAreaFile The offset of the FilePointer field inside the CONTROL_AREA Windows kernel structure.
HandleTableTableCode The offset of the TableCode field inside the HANDLE_TABLE Windows kernel structure.
reserved No longer used. The file format still has this field to be backwards compatible with older Introcore versions.
WmiGetClockOffset The offset of the GetCpuClock field inside the WMI_LOGGER_CONTEXT structure.
EtwDbgDataSiloOffset Offset of EtwDbgDataSilo in EtwpDbgData.
EtwSignatureOffset The offset relative to the EtwDebuggerData structure at which the ETW signature is found
SubsectionCtlArea The offset of the ControlArea field inside the SUBSECTION Windows kernel structure.
HalPerfCntFunctionOffset The offset of the protected functions inside HalPerformanceCounter structure from Hal Heap.
RspOffsetOnZwCall The offset of RSP inside the fake trapframe constructed on a Zw* function call on x64 systems.
HalIntCtrlTypeMaxOffset The maximum offset of Type inside HalInterruptController.
HalIntCtrlTypeMinOffset The minimum offset of Type inside HalInterruptController.
SharedUserDataSize The size of the _KUSER_SHARED_DATA structure.
EprocessFlags

Used to describe bits inside the Flags field of the EPROCESS Windows kernel structure.

CAMI field name Description
NoDebugInherit The index of the NoDebugInherit flag.
Exiting The index of the ProcessExiting flag.
Delete The index of the ProcessDelete flag.
3Crashed The index of the Crashed flag.
VmDeleted The index of the VmDeleted flag.
HasAddrSpace The index of the HasAddressSpace flag.
OutSwapped The index of the OutSwapped flag.
VadShort

The following values describe a MMVAD_SHORT Windows kernel structure.

CAMI field name Description
Parent The offset of VadNode.ParentValue field. VadNode is a RTL_BALANCED_NODE or a MM_AVL_NODE structure included in MMVAD_SHORT.
Left The offset of VadNode.Left field. VadNode is a RTL_BALANCED_NODE or MM_AVL_NODE structure included in MMVAD_SHORT.
Right The offset of VadNode.Right field. VadNode is a RTL_BALANCED_NODE or MM_AVL_NODE structure included in MMVAD_SHORT.
StartingVpn The offset of the StartingVpn field.
StartingVpnHigh The offset of the StartingVpnHigh field. Not all Windows versions have this field. It is 0 if it is not present.
EndingVpn The offset of the EndingVpn field.
EndingVpnHigh The offset of the EndingVpnHigh field. Not all Windows versions have this field. It is 0 if it is not present.
Flags The offset of the VadFlags field. Note that this is included in the same union as LongFlags.
FlagsSize The minimum size that needs to be read in order to properly parse the Flags field. See VadFlags.
VpnSize The size of the StartingVpn and EndingVpn fields. StartingVpnHigh and EndingVpnHigh always have the size of one byte.
Size The minimum size that needs to be read in order to properly parse a MMVAD_SHORT structure.
VadLong

The following values describe a MMVAD Windows kernel structure.

CAMI field name Description
Subsection The offset of the Subsection field.
VadFlags

The following values are used to parse the VadFlags field of a MMVAD_SHORT Windows kernel structure. Since VadFlags is actually a bitfield, these are used to isolate parts of the field. Some of these fields work in pairs.

CAMI field name Description
TypeShift / TypeMask pair Used to obtain the VadType value. The raw VadFlags value needs to be right-shifted with the TypeShift value first, then the bits of the VadType can be isolated by applying the TypeMask mask.
ProtectionShift / ProtectionMask pair Used to obtain the Protection value. The raw VadFlags value needs to be right-shifted with the ProtectionShift value first, then the bits of the Protection can be isolated by applying the ProtectionMask mask.
NoChangeBit The index of the NoChange flag. This is always one bit.
PrivateFixup Mask used to isolate the PrivateFixup flag. This is always one bit, but on certain Windows versions it is missing. It is 0 if it is not available.
DeleteInProgress Mask used to isolate the DeleteInProgress flag. This is always one bit, but on certain Windows version it is missing. It is 0 if it is not available.
SyscallNumbers

The following values are syscall numbers used by introcore to link the syscall kernel linkage functions into the boot driver. The kernel linkages are Zw* functions corresponding to the Nt* ntdll exports. Some aren’t exported. so we search for them using the syscall number and a “constant” parttern signature.

CAMI field name Description
NtWriteVirtualMemory The syscall number of NtWriteVirtualMemory.
NtProtectVirtualMemory The syscall number of NtProtectVirtualMemory.
NtCreateThreadEx The syscall nubmer of NtCreateThreadEx.
FileObject

This is used to describe a FILE_OBJECT Windows kernel structure.

CAMI field name Description
NameBuffer The offsrt of the FileName.Buffer field. FileName is a UNICODE_STRING structure.
NameLength The offset of the FileName.Length field. FileName is a UNICODE_STRING structure.
Windows kernel mode fields example

After you’re done, windows_7600_x64_kpti_OFF.yml should look like this:

---
!intro_update_win_supported_os
build_number: 7600
version_string: !intro_update_win_version_string
    version_string: "Windows 7 x64"
    server_version_string: "Windows Server 2008 R2 x64"
kpti_installed: False
is_64: True

km_fields: !opaque_structures
    type: win_km_fields
    os_structs:

        Process: !opaque_fields
            Cr3: 0x28
            UserCr3: 0x28
            KexecOptions: 0xd2
            ListEntry: 0x188
            Name: 0x2e0
            SectionBase: 0x270
            Id: 0x180
            ParentPid: 0x290
            VadRoot: 0x448
            CreateTime: 0x168
            ExitStatus: 0x444
            Token: 0x208
            ObjectTable: 0x200
            Peb: 0x338
            ThreadListHead: 0x30
            WoW64: 0x320
            Flags: 0x440
            Flags3: 0x0
            MitigationFlags: 0x0
            MitigationFlags2: 0x0
            DebugPort: 0x1f0
            Spare: 0x2a0

        Thread: !opaque_fields
            Process: 0x210
            ThreadListEntry: 0x2f8
            KernelStack: 0x38
            StackBase: 0x278
            StackLimit: 0x30
            State: 0x164
            WaitReason: 0x26b
            AttachedProcess: 0x70
            Teb: 0xb8
            Id: 0x3c0
            ClientSecurity: 0x3e8
            TrapFrame: 0x1d8

        DrvObj: !opaque_fields
            Size: 0x150
            FiodispSize: 0xe0
            AllocationGap: 0x50
            Fiodisp: 0x50
            Start: 0x18

        Pcr: !opaque_fields
            CurrentThread: 0x188
            UserTime: 0x4888

        PoolDescriptor: !opaque_fields
            TotalBytes: 0x50
            NppSize: 0x80000000

        Mmpfn: !opaque_fields
            Size: 0x30
            Pte: 0x10
            RefCount: 0x18
            Flags: 0x1a
            PaeSize: 0x0
            PaePte: 0x0
            PaeRefCount: 0x0
            PaeFlags: 0x0

        Token: !opaque_fields
            Privs: 0x40
            UserCount: 0x78
            RestrictedCount: 0x7c
            Users: 0x90
            RestrictedSids: 0x98

        Ungrouped: !opaque_fields
            CtlAreaFile: 0x40
            HandleTableTableCode: 0x0
            HalIntCtrlType: 0x0
            WmiGetClockOffset: 0x18
            EtwDbgDataSiloOffset: 0x10
            # We want to treat it as "-2" so we send the unsigned int value which will be correctly treated by introcore
            EtwSignatureOffset: 0xFFFFFFFE
            SubsectionCtlArea: 0

        EprocessFlags: !opaque_fields
            NoDebugInherit: 0x2
            Exiting: 0x4
            Delete: 0x8
            3Crashed: 0x10
            VmDeleted: 0x20
            HasAddrSpace: 0x40000

        VadShort: !opaque_fields
            Parent: 0x0
            Left: 0x8
            Right: 0x10
            StartingVpn: 0x18
            StartingVpnHigh: 0x0
            EndingVpn: 0x20
            EndingVpnHigh: 0x0
            Flags: 0x28
            FlagsSize: 0x8
            VpnSize: 0x8
            Size: 0x30

        VadLong: !opaque_fields
            Subsection: 0x48

        VadFlags: !opaque_fields
            TypeShift: 0x34
            TypeMask: 0x7
            ProtectionShift: 0x38
            ProtectionMask: 0x1F
            NoChangeBit: 51
            PrivateFixup: 0x0
            DeleteInProgress: 0x0

        SyscallNumbers: !opaque_fields
            NtWriteVirtualMemory: 0x37
            NtProtectVirtualMemory: 0x4d
            NtCreateThreadEx: 0xa5

        FileObject: !opaque_fields
            NameBuffer: 0x60
            NameLength: 0x58
User mode fields

The file will conventionally be named windows_um_<version>_x<arch>.yml. For example, windows_um_7_x64.yml for Windows 7 x64 7600, 7601, and 7602.

Note

The following examples do not necessarily contain valid offsets.

All yaml files must start with a ---. The following line must contain a yaml_tag describing the python class that will be responsible for parsing the current yaml structure. In this case, it is !intro_update_win_um_fields. The following 3 lines must contain the is64min_ver, and max_ver fields (on separate lines). is_64 is True if the file contains information for a 64-bit system. min_ver and max_ver represent the minimum and the maximum operating system versions for which the information is valid. This interval is inclusive.

---
!intro_update_win_um_fields
is64: True
min_ver: 7600
max_ver: 7602

Next, we populate the actual guest user mode fields. To do so, add the following lines:

fields: !opaque_structures
    type: win_um_fields
    os_structs:

The os_structs field will be populated with more collections of fields. For example, we need more than one field from the LDR_DATA_TABLE_ENTRY structure. Those fields are grouped under Dll as follows:

Dll: !opaque_fields
    BaseOffsetInModule64: 0x30
    BaseOffsetInModule32: 0x18
    SizeOffsetInModule64: 0x40
    ...

The next tables describe all of the user mode fields, from which structure to extract them, and how to populate the yaml groups. All field offsets are relative to the start of the structure containing them.

Dll

This is used to describe a LDR_DATA_TABLE_ENTRY Windows structure.

CAMI field name Description
BaseOffsetInModule64 The offset of the DllBase field for 64-bit processes.
BaseOffsetInModule32 The offset of the DllBase field for 32-bit processes.
SizeOffsetInModule64 The offset of the SizeOfImage field field for 64-bit processes.
SizeOffsetInModule32 The offset of the SizeOfImage field field for 64-bit processes.
NameOffsetInModule64 The offset of the FullDllName field field for 64-bit processes.
NameOffsetInModule32 The offset of the FullDllName field field for 64-bit processes.
Peb

This is used to describe a PEB Windows structure.

CAMI field name Description
64Size The size of the structure for 64-bit processes. This is not the actual structure size, but the size that is relevant for Introcore.
32Size The size of the structure for 32-bit processes. This is not the actual structure size, but the size that is relevant for Introcore.
Teb

This is used to describe a TEB Windows structure.

CAMI field name Description
64Size The size of the structure for 64-bit processes. This is not the actual structure size, but the size that is relevant for Introcore.
32Size The size of the structure for 32-bit processes. This is not the actual structure size, but the size that is relevant for Introcore.
Wow64SaveArea The offset of the area in which a thread of a WoW64 application saves its general purpose registers when jumping to 64-bit code in order to issue a syscall.
Wow64StackInSaveArea The offset of ESP in the Wow64SaveArea.
Windows user mode fields example
---
!intro_update_win_um_fields
is64: True
min_ver: 7600
max_ver: 7602
fields: !opaque_structures
    type: win_um_fields
    os_structs:

        Dll: !opaque_fields
            BaseOffsetInModule64: 0x30
            BaseOffsetInModule32: 0x18
            SizeOffsetInModule64: 0x40
            SizeOffsetInModule32: 0x20
            NameOffsetInModule64: 0x48
            NameOffsetInModule32: 0x24

        Peb: !opaque_fields
            64Size: 0x388
            32Size: 0x250

        Teb: !opaque_fields
            64Size: 0x20
            32Size: 0x20
            Wow64SaveArea: 0x1488
            Wow64StackInSaveArea: 0xc8

Adding new fields

New fields must be added in the tags.yaml file, and then the OS-specific configuration files must be changed to contain the fields. For technical documentation about this see the Doxygen documentation for the guest support module.

The format of the tags.yaml file is the same as the previous os_structs: the same groups, the same field names.

Function patterns

Function patterns are found in the windows/functions directory. Each function has a specific yaml file for 32-bit Windows versions and 64-bit Windows versions. One file can contain multiple patterns. These patterns are used by Introcore to find kernel APIs that will be hooked.

Note

All of the following examples are based on the KiDispatchException32.yml file.

All yaml files must start with a ---. The following line must contain a yaml_tag describing the python class that will be responsible for parsing the current yaml structure. In this case, it is !intro_update_win_function. The following 2 lines contain the name and guest64 fields. name is used to identify the function, while guest64 is used to identify the guest architecture.

In our case, the first couple of lines look like this:

---
!intro_update_win_function
name: KiDispatchException
guest64: False

The arguments field is optional and describes the arguments passed to introcore by the hook handler and not the actual argument list of the function. While these can be the same as the parameters the kernel API receives, the handler can pass different parameters. CAMI describes exactly what the handler will pass to Introcore. Arguments can be different for different Windows versions, so the first two fields that must be added are the min_ver and max_ver fields. These contain the minimum and maximum version for which the argument description is valid (the range is inclusive). WIN_PATTERN_MIN_VERSION_ANY and WIN_PATTERN_MAX_VERSION_ANY can be used to specify any version.

The next field, args, is a list that describes the arguments used by Introcore in the order in which Introcore expects them. The list uses some predefined constants for describing the location of the arguments. For example, DET_ARG_RAX is used for parameters passed using the RAX register, while DET_ARG_STACK3 means that the argument is the third guest word on the stack.

arguments:
    -
        !intro_update_win_args
        min_ver: WIN_PATTERN_MIN_VERSION_ANY
        max_ver: WIN_PATTERN_MAX_VERSION_ANY

        args:
            - DET_ARG_STACK1       # Exception record GVA
            - DET_ARG_STACK2       # Exception frame GVA, or i think at least, not used in introcore
            - DET_ARG_STACK3       # Trap frame GVA
            - DET_ARG_STACK4       # Previous mode

In this example, there are 4 arguments valid for all windows versions. All of them are from the stack.

Next, there is a list of patterns, with each element in the list having the !intro_update_win_pattern tag. Exactly as with the case of the arguments, these have a min_ver and max_ver pair of fields that are used to select the Windows version for which a pattern is available. The section_hint field is used as a hint by Introcore to first search the function in the given section.

Followed by those fields there’s the actual pattern field with the yaml tag !code_pattern. This field has a code field that contains a list of python-like lists describing instructions.

patterns:
    -
        !intro_update_win_pattern
        section_hint: .text
        min_ver: WIN_PATTERN_MIN_VERSION_ANY
        max_ver: 8000
        pattern: !code_pattern
            code:
                - [0x68, 0x100, 0x100, 0x100, 0x100]                 #  push    0F8h
                - [0x68, 0x100, 0x100, 0x100, 0x100]                 #  push    offset nt+0x5c50 (826984b0)
                - [0xe8, 0x100, 0x100, 0x100, 0x100]                 #  call    nt!_SEH_prolog4_GS (826be8b0)
                - [0x64, 0xa1, 0x20, 0x00, 0x00, 0x00]               #  mov     eax,dword ptr fs:[00000020h]
                - [0xff, 0x80, 0x100, 0x100, 0x100, 0x100]           #  inc     dword ptr [eax+58Ch]
                - [0xc7, 0x45, 0x100, 0x17, 0x00, 0x01, 0x00]        #  mov     dword ptr [ebp-20h],10017h
                - [0x80, 0x7d, 0x100, 0x100]                         #  cmp     byte ptr [ebp+14h],1
                - [0x74, 0x100]                                      #  je      nt!KiDispatchException+0x31 (826f34a2)
                - [0x80, 0x3d, 0x100, 0x100, 0x100, 0x100, 0x00]     #  cmp     byte ptr [nt!KdDebuggerEnabled (827a4a4c)],0
                - [0x74, 0x100]                                      #  je      nt!KiDispatchException+0x6b (826f34dc)
                - [0xc7, 0x45, 0x100, 0x1f, 0x00, 0x01, 0x00]        #  mov     dword ptr [ebp-20h],1001Fh
                - [0x80, 0x3d, 0x100, 0x100, 0x100, 0x100, 0x00]     #  cmp     byte ptr [nt!KeI386XMMIPresent (827a1158)],0
                - [0x74, 0x100]                                      #  je      nt!KiDispatchException+0x48 (826f34b9)
                - [0xc7, 0x45, 0x100, 0x3f, 0x00, 0x01, 0x00]        #  mov     dword ptr [ebp-20h],1003Fh
                - [0xf7, 0x05, 0x100, 0x100, 0x100, 0x100, 0x00, 0x00, 0x40, 0x00] #  test    dword ptr [nt!KeFeatureBits (827a9a94)],400000h
                - [0x74, 0x100]                                      #  je      nt!KiDispatchException+0x6b (826f34dc)
                - [0xa1, 0x100, 0x100, 0x100, 0x100]                 #  mov     eax,dword ptr [nt!KeEnabledXStateFeatures (827a9b80)]
                - [0x83, 0xe0, 0xfc]                                 #  and     eax,0FFFFFFFCh
                - [0x0b, 0x05, 0x100, 0x100, 0x100, 0x100]           #  or      eax,dword ptr [nt!KeEnabledXStateFeatures+0x4 (827a9b84)]

In this example, there’s a pattern valid for any Windows version up to 8000 that resides in the .text section. Even if the pattern is used to search for a sequence of bytes, the 0x100 value can be used as a wild-card for matching anything. This is useful for masking addresses that will change after every boot, or immediates that might slightly change between Windows versions.

Linux Guest

The file format for a Linux guest configuration file is similar to the Windows one. The file starts with the !intro_update_lix_supported_os tag, followed by version, which is a glob pattern, as in the following example. The glob format is slightly different from the standard: the [] pattern is treated as a closed numeric interval (e.g. [12-14] will match 12, 13, and 14). If the left value is missing (e.g. [-15]) it is assumed to be 0, and a missing right value (e.g. [15-]) is assumed to be MAX_QWORD.

!intro_update_lix_supported_os
version: 4.9.0-[0-5]-amd64 *Debian*

The following lines contain the functions that will be hooked by Introcore. Each element of the hooks list must have the !intro_update_lix_hook tag. The handler attribute tells Introcore which hook handler should be used for this function. The skip_on_boot attribute is used on older Introcore versions to discern if a function can be hooked after the OS finishes the boot process.

hooks: !intro_update_lix_hooks
    - !intro_update_lix_hook
        run_init_process:
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        module_param_sysfs_setup:
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        module_param_sysfs_remove:
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        flush_old_exec:
        handler: 0
        skip_on_boot: 0

# -- more functions --

The next part of the file contains kernel structure descriptions. The YAML member is called fields and has the !opaque_structures tag. Opaque fields are grouped in structures. (see tags.yaml for a complete list of structures along with their fields). If the value of a field is not specified then it will be automatically considered 0.

fields: !opaque_structures
    type: lix_fields
    os_structs:

        Info: !opaque_fields
            ThreadSize : 0x4000
            HasModuleLayout : 0x0001
            HasVdsoImageStruct : 0x0001

        Module: !opaque_fields
            ListOffset : 0x0008
            NameOffset : 0x0018
            SymbolsOffset : 0x00d0
            NumberOfSymbolsOffset : 0x00e0

# -- more info --

In the end, your configuration file should look like this:

!intro_update_lix_supported_os
version: 4.15.0-[24-]*Ubuntu*

hooks:
    - !intro_update_lix_hook
        name: run_init_process
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: sched_init
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: module_param_sysfs_setup
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: module_param_sysfs_remove
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: flush_old_exec
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: do_exit
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: cgroup_post_fork
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: wake_up_new_task
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: arch_ptrace
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: compat_arch_ptrace
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: process_vm_rw_core*
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: __vma_link_rb
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: change_protection
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: __vma_adjust
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: __vma_rb_erase
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: expand_downwards
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: complete_signal
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: text_poke
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: commit_creds
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: ftrace_write
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: crash_kexec
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: panic
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: arch_jump_label_transform
        handler: 0
        skip_on_boot: 0

    - !intro_update_lix_hook
        name: __access_remote_vm
        handler: 0
        skip_on_boot: 0

fields: !opaque_structures
    type: lix_fields
    os_structs:

        Info: !opaque_fields
            ThreadSize : 0x4000
            HasModuleLayout : 0x0001
            HasVdsoImageStruct : 0x0001
            HasSmallSlack : 0x0000
            HasKsymRelative : 0x0001
            HasKsymAbsolutePercpu : 0x0001
            HasKsymSize : 0x0000
            HasAlternateSyscall : 0x0001
            HasVdsoFixed : 0x0001
            HasVmaAdjustExpand : 0x0001

        Module: !opaque_fields
            ListOffset : 0x0008
            NameOffset : 0x0018
            SymbolsOffset : 0x00d0
            NumberOfSymbolsOffset : 0x00e0
            GplSymbolsOffset : 0x0118
            NumberOfGplSymbolsOffset : 0x0114
            InitOffset : 0x0178
            ModuleInitOffset : 0x0000
            ModuleCoreOffset : 0x0000
            InitSizeOffset : 0x0000
            CoreSizeOffset : 0x0000
            InitTextSizeOffset : 0x0000
            CoreTextSizeOffset : 0x0000
            InitRoSizeOffset : 0x0000
            CoreRoSizeOffset : 0x0000
            CoreLayoutOffset : 0x0180
            InitLayoutOffset : 0x01d0
            StateOffset : 0x0000
            Sizeof : 0x0340

        Binprm: !opaque_fields
            MmOffset : 0x0090
            FileOffset : 0x00a8
            CredOffset : 0x00b0
            FilenameOffset : 0x00c8
            InterpOffset : 0x00d0
            Vma : 0x0080
            Argc : 0x00c0
            Sizeof : 0x00f0

        Vma: !opaque_fields
            VmaStartOffset : 0x0000
            VmaEndOffset : 0x0008
            VmNextOffset : 0x0010
            VmPrevOffset : 0x0018
            RbNodeOffset : 0x0020
            MmOffset : 0x0040
            FlagsOffset : 0x0050
            FileOffset : 0x00a0

        Dentry: !opaque_fields
            ParentOffset : 0x0018
            NameOffset : 0x0020
            DinameOffset : 0x0038
            InodeOffset : 0x0030

        MmStruct: !opaque_fields
            PgdOffset : 0x0050
            MmUsersOffset : 0x0058
            MmListOffset : 0x0098
            StartCodeOffset : 0x00f0
            EndCodeOffset : 0x00f8
            StartDataOffset : 0x0100
            EndDataOffset : 0x0108
            FlagsOffset : 0x0370
            ExeFileOffset : 0x03a0
            VmaOffset : 0x0000
            StartStack: 0x0120
            RbNodeOffset : 0x0008

        TaskStruct: !opaque_fields
            StackOffset : 0x0018
            UsageOffset : 0x0020
            FlagsOffset : 0x0024
            TasksOffset : 0x07a8
            PidOffset : 0x08a8
            TgidOffset : 0x08ac
            RealParentOffset : 0x08b8
            ParentOffset : 0x08c0
            MmOffset : 0x07f8
            StartTimeOffset : 0x09d0
            CommOffset : 0x0a50
            SignalOffset : 0x0aa0
            ExitCodeOffset : 0x0848
            ThreadNodeOffset : 0x0000
            ThreadGroupOffset : 0x0000
            CredOffset : 0x0a40
            FsOffset : 0x0a88
            FilesOffset : 0x0a90
            NsProxyOffset : 0x0a98
            GroupLeader: 0x08e8
            InExecve: 0x0868
            ExitSignal: 0x084c
            ThreadStructSp : 0x0018
            AltStackSp: 0x0ae0

        Fs: !opaque_fields
            RootOffset : 0x0018
            PwdOffset : 0x0028
            Sizeof : 0x0038

        FdTable: !opaque_fields
            MaxFdsOffset : 0x0000
            FdOffset : 0x0008

        Files: !opaque_fields
            FdtOffset : 0x0020
            Sizeof : 0x02c0

        Inode: !opaque_fields
            ImodeOffset : 0x0000
            UidOffset   : 0x0004
            GidOffset   : 0x0008
            Sizeof : 0x0258

        Socket: !opaque_fields
            StateOffset : 0x0000
            TypeOffset : 0x0004
            FlagsOffset : 0x0008
            SkOffset : 0x0020

        Sock: !opaque_fields
            NumOffset : 0x000e
            DportOffset : 0x000c
            DaddrOffset : 0x0000
            RcvSaddrOffset : 0x0004
            FamilyOffset : 0x0010
            StateOffset : 0x0012
            ProtoOffset : 0x0028
            V6DaddrOffset : 0x0038
            V6RcvSaddrOffset : 0x0048
            Sizeof : 0x02c8

        Cred: !opaque_fields
            UsageOffset : 0x0000
            RcuOffset : 0x0098
            Sizeof : 0x00a8

        Ungrouped: !opaque_fields
            FileDentryOffset : 0x0018
            ProtoNameOffset : 0x0158
            SignalListHeadOffset : 0x0010
            SocketAllocVfsInodeOffset : 0x0030
            Running : 2