Integration Guideline

Introcore can be used with any hypervisor that meets the minimum requirements.

There are two interfaces that must be implemented by an integrator:

  • upper, which exposes low level services to Introcore (for example, memory management or logging);
  • glue, which exposes guest management and virtualization APIs (for example, for querying the guest register state or for modifying EPT access rights);

These interfaces are defined in the Introcore public headers.

For an example, see the daemon directory in the HVMI repository.

Public headers

All the public headers can be found in the include/public directory in the root of the repository, as well as in the include directory inside an Introcore SDK.

The public headers are:

  • intro_types.h - contains all the public definitions that are needed by an integrator, from the data types used by Introcore APIs to alert and event structures;
  • upperiface.h - contains the definitions needed for the upper interface implementation;
  • glueiface.h - contains the definitions needed for the glue interface implementation;
  • introstatus.h - contains the definitions for the INTSTATUS data type and possible return values for Introcore APIs.

These headers have dependencies on only standard C headers: stdef.hstdint.h, and stdbool.h.

There are two more headers included for convenience:

  • env.h - used to detect the build environment; based on this, certain definitions might change to better fit a certain compiler, but no functionality should change;
  • intro_sal.h - Introcore uses SAL annotations, but the definitions are not always available (for example, for GCC). To ease the integration process, this header exposes dummy definitions for the SAL definitions used in the public headers.

The intro_types.h header defines some basic data types used by Introcore, such as BOOLEAN, BYTE, DWORD, etc, as well as TRUE and FALSE values. If an integrator already has these defined it can disable the Introcore definitions by defining INTROCORE_NOCOMPAT before including intro_types.h.

Introcore also supports multiple integration environments, as discussed in Overall Architecture.

The default Linux build settings assume that Introcore will be used inside a SVA, while the default Windows build settings assume that Introcore will run inside VMX root. This can be controlled by defining (or not defining) USER_MODE at build time.

Binaries

Introcore can be built both as a Linux library, as well as a Windows library with no external dependencies. The only dependencies it has are statically linked in the binary.

After a successful build, the Introcore binary will be found in the bin/x64/Debug or bin/x64/Release directory.

An integrator must be able to load this library, as well as a guest support file, and optionally an exceptions file.

Upper Interface

The upper interface exposes various low-level services to the introspection engine. This must be fully implemented by an integrator. It can be implemented without any support from the hypervisor. Technical details about each API can be found in the upperiface.h documentation. From a high level point of view, the following functionalities must be provided:

  • Logging;
  • Synchronization - this includes APIs for initializing, freeing, acquiring, and releasing locks;
  • Memory management - this includes APIs for allocating and freeing memory, as well as querying the amount of free memory available to Introcore;
  • Debugging.

Note

The interface may change in time. Breaking changes are signaled by changing the UPPER_IFACE_VERSION_1 and UPPER_IFACE_VERSION_1_SIZE definitions in upperiface.h. Each instance of the interface must have the Version and Size fields set to these values. These will be checked at run time to ensure that the version of the library and the headers used by an integrator match and Introcore will refuse to load if incompatibilities are detected.

Glue Interface

The glue interface allows Introcore to communicate with the integrator and the underlying hypervisor, as well as exposing APIs with which the introspection engine can be controlled. It is split into two parts:

  • The first part is implemented by the integrator and is used by Introcore to control certain aspects related to the guest management and virtualization features;
  • The second part is implemented by Introcore and allows an integrator to control the introspection engine.

Technical details about each API can be found in the glueiface.h documentation.

From a high level point of view, the integrator must provide functionalities for:

  • Querying the guest state, such as the register state, the CPU count, etc;
  • Modifying the guest state;
  • Querying support for certain virtualization features, such as #VE, SPP, etc;
  • Notifying the integrator about alerts and events or error encountered by Introcore;
  • Accessing guest memory;
  • Querying and modifying EPT access rights;
  • Activating or deactivating various VMEXITs (for example, for MSR accesses, INT3 executions, etc);
  • Pausing and resuming VCPUs;
  • Injecting exceptions inside the guest.

Most of these follow the requirements in the Prerequisites from the HV section.

From a high level point of view, Introcore exposes functionalities for:

  • Starting and stopping the introspection process;
  • Updating exceptions or CAMI files;
  • Modifying the protection policies and settings;
  • Injecting agents;
  • Querying information about the guest (such as the guest OS version).

Note

The interface may change in time. Breaking changes are signaled by changing the GLUE_IFACE_VERSION_1 and GLUE_IFACE_VERSION_1_SIZE definitions in glueiface.h. Each instance of the interface must have the Version and Size fields set to these values. These will be checked at run time to ensure that the version of the library and the headers used by an integrator match, and Introcore will refuse to load if incompatibilities are detected.

Stand-alone functions

Apart from these interfaces, Introcore exposes a few other functions for managing the library itself:

  • IntInit - used to initialize the library;
  • IntPreInit - used to ensure that the global library state is in a good state; should be called before calling IntInit;
  • IntUninit - completely stops the introspection engine;
  • IntCheckCompatibility - can be used to check compatibility with the Introcore library.