Bitdefender Hypervisor Memory Introspection
winagent.c File Reference

This file deals with Windows agents injection. More...

#include "winagent.h"
#include "alerts.h"
#include "guests.h"
#include "hnd_loggather.h"
#include "hnd_remediation.h"
#include "icache.h"
#include "kernvm.h"
#include "loader.h"
#include "memcloak.h"
#include "ptfilter.h"
#include "slack.h"
#include "vecore.h"
#include "winbootdrv_Win32.h"
#include "winbootdrv_x64.h"
#include "winpe.h"
#include "winprocesshp.h"
#include "winstubs.h"

Go to the source code of this file.

Data Structures

struct  _AGENT_NAME
 
struct  _AGENT_STATE
 

Macros

#define REM_DRV(arch64)   ((arch64) ? (gBootDriverx64) : (gBootDriverWin32))
 
#define REM_SIZE(arch64)   ((arch64) ? (sizeof(gBootDriverx64)) : sizeof((gBootDriverWin32)))
 
#define AGENT_FLAG_STARTED   0x00000001
 
#define AGENT_FLAG_ALLOCATED   0x00000002
 
#define AGENT_FLAG_ACTIVE   0x00000004
 
#define AGENT_FLAG_COMPLETED   0x00000008
 
#define AGENT_FLAG_ALL_DONE   0x0000000F
 

Typedefs

typedef struct _AGENT_NAME AGENT_NAME
 
typedef struct _AGENT_NAMEPAGENT_NAME
 
typedef struct _AGENT_STATE AGENT_STATE
 
typedef struct _AGENT_STATEPAGENT_STATE
 

Functions

BOOLEAN IntWinAgentIsRipInsideCurrentAgent (QWORD Rip)
 Return true if the given RIP points inside the currently active boot driver. More...
 
INTSTATUS IntWinAgentSelectBootstrapAddress (DWORD Size, QWORD *Address)
 Finds an in-guest adders that can be used to host the bootstrap code. More...
 
INTSTATUS IntWinAgentReleaseBootstrapAddress (QWORD Address)
 Releases the slack space allocated for the bootstrap code. More...
 
static INTSTATUS IntWinAgentFree (PWIN_AGENT Agent, QWORD DataInfo)
 Frees an agent. More...
 
static INTSTATUS IntWinAgentRemove (PWIN_AGENT Agent)
 Removes the given agent. More...
 
INTSTATUS IntWinAgentInjectTrampoline (void)
 Inject the agent trampoline inside the guest. More...
 
static INTSTATUS IntWinAgentDeployWinDriver (PWIN_AGENT Agent)
 Inject the Windows boot driver. More...
 
static INTSTATUS IntWinAgentFindPropperSyscall (QWORD *PropperSyscall)
 Find the main SYSCALL handler. More...
 
static INTSTATUS IntWinAgentFindSyscallLinkage (DWORD SyscallNumber, QWORD *LinkageAddress)
 Find the address of the kernel linkage of a syscall. More...
 
static INTSTATUS IntWinAgentFindInstruction (BYTE MinLen, QWORD *InstructionVa, BYTE *InstructionLen, BYTE *InstructionBytes)
 Searches for a suitable instruction to replace with a CALL to the trampoline code. More...
 
INTSTATUS IntWinAgentActivatePendingAgent (void)
 Activates a pending agent that waits to be injected. More...
 
static INTSTATUS IntWinAgentRestoreState64 (PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
 Restore the general purpose registers state. More...
 
static INTSTATUS IntWinAgentRestoreState32 (PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
 Restore the general purpose registers state. More...
 
static INTSTATUS IntWinAgentHandleBreakpointAgent (PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
 Handle an INT3 that was initiated by a breakpoint agent. More...
 
static INTSTATUS IntWinAgentHandleLoader1Hypercall (WIN_AGENT *Agent, IG_ARCH_REGS *Registers)
 Handle a hyper call initiated by the trampoline code. More...
 
static INTSTATUS IntWinAgentReleaseBootstrap (WIN_AGENT *Agent, IG_ARCH_REGS *Registers)
 Releases the bootstrap allocated inside the guest. More...
 
static INTSTATUS IntWinAgentRemoveAgentAndResetState (WIN_AGENT *Agent)
 Removes the indicated agent. More...
 
static INTSTATUS IntWinAgentReleaseBootstrapAndRemoveAgent (WIN_AGENT *Agent, IG_ARCH_REGS *Registers)
 Releases the bootstrap address and removes the agent. More...
 
static INTSTATUS IntWinAgentHandleLoader2Hypercall (PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
 Handles VMCALLs issued by the bootstrap code. More...
 
static INTSTATUS IntWinAgentHandleDriverVmcall (PWIN_AGENT Agent, PIG_ARCH_REGS Registers)
 This function handles VMCALLs issued by the boot driver. More...
 
static INTSTATUS IntWinAgentHandleAppVmcall (void *Reserved, PIG_ARCH_REGS Registers)
 Handles a VMCALL issued by an application that has been injected inside the guest. More...
 
INTSTATUS IntWinAgentHandleInt3 (QWORD Rip, DWORD CpuNumber)
 Handle a breakpoint that was initiated inside the guest. More...
 
INTSTATUS IntWinAgentHandleVmcall (QWORD Rip)
 Handle a VMCALL that was executed inside the guest. More...
 
static void IntWinAgentSelectTokens (QWORD *Token1, QWORD *Token2, QWORD *Token3)
 Randomly select 3 tokens to be used by the bootstrap code when issuing hyper calls. More...
 
INTSTATUS IntWinAgentInject (PFUNC_AgentInjection InjectionCallback, PFUNC_AgentCompletion CompletionCallback, PFUNC_AgentDeliver DeliverCallback, void *Context, PBYTE AgentContent, DWORD AgentSize, BOOLEAN AgentInternal, DWORD AgentTag, AGENT_TYPE AgentType, const CHAR *Name, DWORD Options, const CHAR *Args, DWORD Pid, PWIN_AGENT *Agent)
 Schedule an agent injection inside the guest. More...
 
INTSTATUS IntWinAgentInjectBreakpoint (PFUNC_AgentInjection InjectionCallback, void *Context, PWIN_AGENT *Agent)
 Injects a breakpoint agent inside the guest. More...
 
INTSTATUS IntWinAgentEnableInjection (void)
 enables agent injections. More...
 
void IntWinAgentCheckIfProcessAgentAndIncrement (CHAR *ImageName, BOOLEAN *IsAgent, DWORD *Tag)
 Checks if a process is an agent or not, and increments the ref count of that name. More...
 
void IntWinAgentCheckIfProcessAgentAndDecrement (CHAR *ImageName, BOOLEAN *IsAgent, DWORD *Tag, BOOLEAN *Removed)
 Checks if a process is an agent or not, and decrements the ref count of that name. More...
 
void IntWinAgentRemoveEntryByAgid (DWORD Counter, DWORD *Tag)
 Removes an agent name from the list of names, using the ID. More...
 
BOOLEAN IntWinAgentIsPtrInTrampoline (QWORD Ptr, THS_PTR_TYPE Type)
 Check if the provided address points inside the agent trampoline. More...
 
AG_WAITSTATE IntWinAgentGetState (DWORD *Tag)
 Gets the global agents state. More...
 
void IntWinAgentDisablePendingAgents (void)
 Disables all pending agents. More...
 
void IntWinAgentInit (void)
 Initialize the agents state. More...
 
INTSTATUS IntWinAgentUnInit (void)
 Uninit the agents state. More...
 

Variables

BYTE gTrampolineZero [4096] = {0}
 Just a page filled with zeros. More...
 
static AGENT_STATE gAgentState = {0}
 

Detailed Description

This file deals with Windows agents injection.

Introcore supports injecting agents inside the guest VM. An agent is a file, process or driver which is deployed inside the guest, and which is removed when it is not needed anymore. Typical agents usage include:

  • Inject a file (archive) inside the guest;
  • Inject a process (an application which is started inside the guest);
  • Inject a driver (for example, the VE or PT driver). The injection mechanism is composed of three main components:

0. Detoured instruction. This is an instruction inside the SYSCALL handler, which gets executed by the operating system, and which we replace with a "CALL" to the trampoline code (see the next section). The instruction must be at least 5 bytes in size (since 5 is the length of the relative "CALL" we use). Once a SYSCALL is generated and the detoured instruction is hit, the trampoline code gets activated. In case we want to inject a breakpoint agent, we replace this instruction with just a breakpoint, so instead of going through the trampoline, the breakpoint would be handled directly, without the intervention of the trampoline.

  1. The trampoline. The trampoline is a small chunk of code which is injected inside the guest as soon as Introcore is initialized, and remains inside the guest until Introcore unloads. This piece of code has the responsibility of jumping to the bootstrap code; the reason we need this trampoline is because we have no guarantee that we we will be able to allocate the bootstrap code within [-2G, +2G] of the NT image, and because we need a simple mechanism of handling the bootstrap thread termination; therefore, this trampoline code contains an indirect branch which will jump to the bootstrap no matter where it was allocated. The trampoline looks like this (on 64 bit, on 32 bit it is nearly identical): _start: push rax int3 test rax, rax jz _skip call rax _skip: pop rax retn _stop: int3 xor eax, eax retn The anatomy of the trampoline is the following (remember that the trampoline is executed when the detoured instruction, replaced by a "CALL", is executed and branches to it):
  • On entry, it saves RAX on the stack;
  • It issues a hyper call using INT3; this will end up executing IntWinAgentHandleLoader1Hypercall;
  • Inside Introcore, IntWinAgentHandleLoader1Hypercall will:
    • Store in the RAX register the address of the bootstrap code;
    • Modify the return address saved by the "CALL" to point 5 bytes backwards - exactly to the address of the detoured instruction;
    • Restore the detoured instruction inside guest memory.
  • Once back inside the guest, we test the value in RAX; if it is 0, it means that Introcore does not want the trampoline to jump to the bootstrap; this can happen if two VCPUs executed the INT3 at the same time; we only need it one time, so the second time we end up handling the INT3, we can safely ignore it, and this is signaled to the trampoline by returning 0 in RAX. If RAX is not 0, then it contains the address of the bootstrap, and the trampoline will call it right away;
  • The _stop label is hit once the bootstrap finishes its job; once the bootstrap thread is done, it will jump to this label, which does nothing else but returning 0 and terminating the current thread, as this executes in the context of a new thread created inside the bootstrap code;
  • the _skip label is reached once the bootstrap initiates the second hyper call, which is immediately after it created a thread inside the bootstrap; the instructions here simply restore RAX and return execution to the instruction that has been interrupted; Please find the trampoline code in the following files:
  • trampoline32.asm (32 bit, VMCALL hyper call)
  • trampoline32_bp.asm (32 bit, INT3 hyper call)
  • trampoline64.asm (64 bit, VMCALL hyper call)
  • trampoline64_bp.asm (64 bit, INT3 hyper call)
  1. The bootstrap. The bootstrap code is a larger piece of code that gets injected and removed dynamically with each injection request. The bootstrap contains two sections:
  • The initial section is called by the trampoline code via the "CALL rax" instruction (due to this indirect call we can allocate the bootstrap code anywhere in memory). This section does the following:
    • Save all general purpose registers on the stack;
    • Allocate memory for the boot driver;
    • Issue a hyper call to notify Introcore about the allocated memory;
    • The hyper call will be handled by the IntWinAgentHandleLoader2Hypercall function, which will write the boot driver inside the guest memory;
    • Create a thread into the second section of the bootstrap;
    • Issue a hyper call to notify Introcore about the started thread;
    • The hyper call will be handled by the IntWinAgentHandleLoader2Hypercall, which will modify the general purpose registers state by restoring all the registers saved on the stack, RIP included, which will now point to the trampoline:_skip label;
    • Upon guest re-entry, the trampoline!_skip label gets executed, which simply returns to the interrupted instruction, and guest execution resumes normally;
  • The thread section is executed as a different thread as soon as the initial section creates it:
    • Call the entry point of the boot driver (previously injected by Introcore during the first hyper call issued by the bootstrap code)
    • On boor driver return, jmp to the trampoline!_stop label;
    • As already described, this label will issue a hyper call, handled by the IntWinAgentHandleLoader1Hypercall, which will free the bootstrap from guest memory, and terminate the thread by returning 0; Please find the bootstrap code in the following files:
  • agent32.asm (32 bit)
  • agent64.asm (64 bit)
  1. The boot driver. The boot driver is a separate project - introbootdrv. It has the main responsibility of handling commands from Introcore; currently defined commands are:
  • Drop a file inside the guest;
  • Start process inside the guest;
  • Load/unload the VE driver;
  • Load/unload the PT driver; Please find the boot driver in the introbootdrv project.

NOTE: There can be only one active agent at any time (by active agent we mean an instance of the boot driver). NOTE: When injecting a process, the boot driver will return as soon as the process was started; this means that it does not wait for the process to finish, allowing Introcore to inject other agents in the meantime, so there can be an arbitrary number of active injected processes at any given time. NOTE: When injecting processes, the IntWinAgentInject function accepts a PID argument which represents the process the agent should be started from. If this argument is 0, the injected process agent will be started in the context of the winlogon.exe process, with SYSTEM privileges!!! NOTE: IntKernVirtMemWrite can be used only for buffers that were allocated/validated by Introcore. For buffers sent from within the guest, use IntVirtMemSafeWrite instead, which validates both the guest page tables and the EPT and ring rights in order to see if the buffer is safe to be written.

Definition in file winagent.c.

Macro Definition Documentation

◆ AGENT_FLAG_ACTIVE

#define AGENT_FLAG_ACTIVE   0x00000004

Definition at line 142 of file winagent.c.

Referenced by IntWinAgentReleaseBootstrap().

◆ AGENT_FLAG_ALL_DONE

#define AGENT_FLAG_ALL_DONE   0x0000000F

◆ AGENT_FLAG_ALLOCATED

#define AGENT_FLAG_ALLOCATED   0x00000002

Definition at line 141 of file winagent.c.

Referenced by IntWinAgentHandleLoader2Hypercall().

◆ AGENT_FLAG_COMPLETED

#define AGENT_FLAG_COMPLETED   0x00000008

Definition at line 143 of file winagent.c.

Referenced by IntWinAgentHandleLoader2Hypercall().

◆ AGENT_FLAG_STARTED

#define AGENT_FLAG_STARTED   0x00000001

◆ REM_DRV

#define REM_DRV (   arch64)    ((arch64) ? (gBootDriverx64) : (gBootDriverWin32))

Definition at line 137 of file winagent.c.

Referenced by IntWinAgentDeployWinDriver(), and IntWinAgentInject().

◆ REM_SIZE

#define REM_SIZE (   arch64)    ((arch64) ? (sizeof(gBootDriverx64)) : sizeof((gBootDriverWin32)))

Definition at line 138 of file winagent.c.

Referenced by IntWinAgentDeployWinDriver(), and IntWinAgentInject().

Typedef Documentation

◆ AGENT_NAME

typedef struct _AGENT_NAME AGENT_NAME

Describes the name of an injected process agent. Whenever a named agent is injected, we allocate such an entry. Whenever a process is created, we check if its name matches the name of an injected agent; if it does, it will be flagged as being an agent. Therefore, it is advisable to use complicated names for the agents, in order to avoid having regular processes marked as agents.

◆ AGENT_STATE

typedef struct _AGENT_STATE AGENT_STATE

Global agents state.

◆ PAGENT_NAME

typedef struct _AGENT_NAME * PAGENT_NAME

◆ PAGENT_STATE

typedef struct _AGENT_STATE * PAGENT_STATE

Function Documentation

◆ IntWinAgentActivatePendingAgent()

INTSTATUS IntWinAgentActivatePendingAgent ( void  )

Activates a pending agent that waits to be injected.

This function will inject a pending agent. The steps required are:

  1. Allocate slack space for the bootstrap code (NT slack)
  2. Inject & hide the bootstrap code inside the allocated slack space.
  3. Find a suitable instruction of length min 5 to detour
  4. Replace the instruction with a CALL to the trampoline code Once the trampoline hyper call is hit, we will move execution to the bootstrap code.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INSUFFICIENT_RESOURCESIf a memory alloc fails.

Definition at line 920 of file winagent.c.

Referenced by IntAgentActivatePendingAgent(), IntWinAgentEnableInjection(), IntWinAgentHandleInt3(), IntWinAgentHandleVmcall(), IntWinAgentInject(), and IntWinAgentInjectBreakpoint().

◆ IntWinAgentCheckIfProcessAgentAndDecrement()

void IntWinAgentCheckIfProcessAgentAndDecrement ( CHAR ImageName,
BOOLEAN IsAgent,
DWORD Tag,
BOOLEAN Removed 
)

Checks if a process is an agent or not, and decrements the ref count of that name.

Each time a process terminates, we check if it was an agent, and we decrement the reference count if its name. Once the reference count of an agent name reaches 0, it will be removed.

Parameters
[in]ImageNameThe image name of the process which is checked.
[out]IsAgentTrue if the process is agent, false otherwise.
[out]TagThe agent tag, if the process is found to be an agent.
[out]RemovedTrue if the agent was removed.

Definition at line 3084 of file winagent.c.

Referenced by IntWinProcDeleteProcessObject().

◆ IntWinAgentCheckIfProcessAgentAndIncrement()

void IntWinAgentCheckIfProcessAgentAndIncrement ( CHAR ImageName,
BOOLEAN IsAgent,
DWORD Tag 
)

Checks if a process is an agent or not, and increments the ref count of that name.

Each time a process is created, we check if its name matches the name of a previously injected agent. If it does, we flag that process as an agent, and we increment the reference count of the name.

Parameters
[in]ImageNameThe image name of the process which is checked.
[out]IsAgentTrue if the process is agent, false otherwise.
[out]TagThe agent tag, if the process is found to be an agent.

Definition at line 3028 of file winagent.c.

Referenced by IntWinProcCreateProcessObject().

◆ IntWinAgentDeployWinDriver()

static INTSTATUS IntWinAgentDeployWinDriver ( PWIN_AGENT  Agent)
static

Inject the Windows boot driver.

This function injects the Windows boot driver inside the guest. The boot driver will be written at an address that was previously allocated by the boot code. Once the Windows boot driver is written inside the guest memory, the boot code will start a thread pointing inside the boot driver.

Parameters
[in]AgentThe agent handle.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_OPERATION_NOT_IMPLEMENTEDIf the guest OS is not Windows.
INT_STATUS_INSUFFICIENT_RESOURCESIf a memory alloc fails.
INT_STATUS_NOT_FOUNDIf the entry point of the boot driver is not found.

Definition at line 451 of file winagent.c.

Referenced by IntWinAgentHandleLoader2Hypercall().

◆ IntWinAgentDisablePendingAgents()

void IntWinAgentDisablePendingAgents ( void  )

Disables all pending agents.

This function should be called during the uninit phase, as it will disable all the pending agents. These agents will never be injected inside the guest. The only exception is given by the VE or PT unloaders, which must be injected on uninit in order to remove the VE or PT drivers from the guest memory.

Definition at line 3291 of file winagent.c.

Referenced by IntAgentDisablePendingAgents().

◆ IntWinAgentEnableInjection()

INTSTATUS IntWinAgentEnableInjection ( void  )

enables agent injections.

Return values
INT_STATUS_SUCCESSOn success.

Definition at line 3010 of file winagent.c.

Referenced by IntAgentEnableInjection().

◆ IntWinAgentFindInstruction()

static INTSTATUS IntWinAgentFindInstruction ( BYTE  MinLen,
QWORD InstructionVa,
BYTE InstructionLen,
BYTE InstructionBytes 
)
static

Searches for a suitable instruction to replace with a CALL to the trampoline code.

Will try to find, starting with the SYSCALL/SYSENTER address, the first "STI" instruction and then the first instruction that's at least 5 bytes in length; this instruction will host our CALL towards the agent trampoline. On x64, we will seek the 3rd STI instruction. The first 2 get executed on a very rare path. If we inject a breakpoint agent, we will replace the instruction with an INT3 instead of a CALL.

Parameters
[in]MinLenUnused.
[in]InstructionVaThe guest virtual address where a suitable instruction was found.
[in]InstructionLenThe length of the identified instruction.
[in]InstructionBytesActual instruction bytes.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.
INT_STATUS_INSUFFICIENT_RESOURCESIf a memory alloc fails.

Definition at line 772 of file winagent.c.

Referenced by IntWinAgentActivatePendingAgent().

◆ IntWinAgentFindPropperSyscall()

static INTSTATUS IntWinAgentFindPropperSyscall ( QWORD PropperSyscall)
static

Find the main SYSCALL handler.

This function finds the main SYSCALL handler. Note that on KPTI systems, the SYSCALL MSR points to a shadow SYSCALL handler, which only does the CR3 switch, and then jumps to the main SYSCALL handler, which is interesting to us.

Parameters
[in]PropperSyscallWill contain, upon successful return, the address of the main SYSCALL handler.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 576 of file winagent.c.

Referenced by IntWinAgentFindInstruction().

◆ IntWinAgentFindSyscallLinkage()

static INTSTATUS IntWinAgentFindSyscallLinkage ( DWORD  SyscallNumber,
QWORD LinkageAddress 
)
static

Find the address of the kernel linkage of a syscall.

Will use kernel buffer if available.

Parameters
[in]SyscallNumberThe number of the syscall to be searched for.
[out]LinkageAddressThe address of the syscall kernel linkage.
Returns
INT_STATUS_SUCCESS on success or an appropriate INTSTATUS error value.

Definition at line 675 of file winagent.c.

Referenced by IntWinAgentHandleDriverVmcall().

◆ IntWinAgentFree()

static INTSTATUS IntWinAgentFree ( PWIN_AGENT  Agent,
QWORD  DataInfo 
)
static

Frees an agent.

This function frees an agent and all the resources allocated to it, for example, its contents. NOTE: The contents of the agent may have been memory-mapped by the integrator; if this is the case, we cannot correctly free it ourselves; instead, we call a dedicated glue API which notifies the integrator that it is safe to free the memory allocated for the agent.

Parameters
[in]AgentThe agent to be freed.
[in]DataInfoUnused.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 238 of file winagent.c.

Referenced by IntWinAgentDisablePendingAgents(), and IntWinAgentRemove().

◆ IntWinAgentGetState()

AG_WAITSTATE IntWinAgentGetState ( DWORD Tag)

Gets the global agents state.

Parameters
[out]TagOptional agent tag, if an agent is active or pending.
Return values
agActiveIf there's an active agent.
agWaitingIf there's a pending agent.
agNoneIf there are no active or pending agents.

Definition at line 3245 of file winagent.c.

Referenced by IntAgentGetState().

◆ IntWinAgentHandleAppVmcall()

static INTSTATUS IntWinAgentHandleAppVmcall ( void *  Reserved,
PIG_ARCH_REGS  Registers 
)
static

Handles a VMCALL issued by an application that has been injected inside the guest.

Each injected application should have its own private VMCALL structure, depending on what information it wants to report. Currently, Introcore can digest VMCALLs from two types of applications:

  1. AGENT_HCALL_REM_TOOL - the remediation tool. This is used to send scan statuses (detections, disinfections, etc.) to Introcore and to the integrator.
  2. AGENT_HCALL_GATHER_TOOL - the log gather tool. This tool is used to gather logs from the target virtual machine and this VMCALL is used to send to log chunks to Introcore and the integrator. NOTE: If the current process has not been marked as an agent (if it was not started directly by us or by a process which we injected), all the VMCALLs will be silently discarded.
Parameters
[in]ReservedReserved for future use.
[in]RegistersGeneral purpose registers state.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.

Definition at line 2162 of file winagent.c.

Referenced by IntWinAgentHandleVmcall().

◆ IntWinAgentHandleBreakpointAgent()

static INTSTATUS IntWinAgentHandleBreakpointAgent ( PWIN_AGENT  Agent,
PIG_ARCH_REGS  Registers 
)
static

Handle an INT3 that was initiated by a breakpoint agent.

This function simply calls the injection callback. This function is used by the breakpoint agents, which are simple agents that only trigger a breakpoint on the SYSCALL flow, where it is safe to inject kernel page-faults or call kernel APIs. Once the injection callback is called, the agent will be freed. NOTE: The ring must already be validated.

Parameters
[in]AgentThe agent to be injected.
[in]RegistersThe general purpose registers state.
Return values
INT_STATUS_SUCCESSOn success, if the breakpoint has already been handled.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.
INT_STATUS_NO_DETOUR_EMUIf the breakpoint was handled by this instance.

Definition at line 1332 of file winagent.c.

Referenced by IntWinAgentHandleInt3().

◆ IntWinAgentHandleDriverVmcall()

static INTSTATUS IntWinAgentHandleDriverVmcall ( PWIN_AGENT  Agent,
PIG_ARCH_REGS  Registers 
)
static

This function handles VMCALLs issued by the boot driver.

The boot driver may issue several VMCALLs which will be handled by Introcore. The defined VMCALLs are:

  1. AGENT_HCALL_FETCH_CMD - fetches the command; this will make Introcore write a AGENT_COMMAND structure inside the guest space, which will describe the operation that the boot driver must do.
  2. AGENT_HCALL_FETCH_CHUNK - fetches a chunk of the actual agent; the size of the chunk is variable, and the driver tells Introcore via the VMCALL parameters what is the max size it accepts.
  3. AGENT_HCALL_MOD_BASE - returns the base address of a loaded module.
  4. AGENT_HCALL_OWN_BASE - returns the base address of the boot driver itself.
  5. AGENT_HCALL_VE - calls the init/uninit callback for the VE driver.
  6. AGENT_HCALL_PT - calls the init/uninit callback for the PT driver.
  7. AGENT_HCALL_VCPUID - returns the current VCPU id.
  8. AGENT_HCALL_SYS_LNK - returns the address of a kernel syscall linkage.
  9. AGENT_HCALL_ERROR - notify an error to the Introspection engine. Errors may capture various failures along the agent injection path. For example, if a process agent is injected, and starting it inside the guest fails, this will be used to send the error that appeared. NOTE: Validations must be already made when calling this function.
Parameters
[in]AgentThe currently active agent.
[in]RegistersGeneral purpose registers state.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.

Definition at line 1816 of file winagent.c.

Referenced by IntWinAgentHandleVmcall().

◆ IntWinAgentHandleInt3()

INTSTATUS IntWinAgentHandleInt3 ( QWORD  Rip,
DWORD  CpuNumber 
)

Handle a breakpoint that was initiated inside the guest.

This function will search for an appropriate handler for the breakpoint. There are several causes for such breakpoints:

  1. Issued by the trampoline code;
  2. Issued by the bootstrap code;
  3. Issued directly by the instruction that was replaced with a breakpoint (direct breakpoint agents);
  4. Spuriously issued by the remnant of an agent (it can happen in multi-CPU systems, where a breakpoint remains pending on a VCPU until another VCPU gets to handle it). NOTE: The notion of VMCALL or hyper call is used generically to any method of communication between the in-guest Introcore components and Introcore. Generally, due to space constraints, we use breakpoint "hyper calls" instead of VMCALL, since a breakpoint is a single byte, instead of three.
Parameters
[in]RipUnused.
[in]CpuNumberThe VCPU number on which the event took place.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_NOT_FOUNDIf a handler is not found.

Definition at line 2273 of file winagent.c.

Referenced by IntAgentHandleInt3().

◆ IntWinAgentHandleLoader1Hypercall()

static INTSTATUS IntWinAgentHandleLoader1Hypercall ( WIN_AGENT Agent,
IG_ARCH_REGS Registers 
)
static

Handle a hyper call initiated by the trampoline code.

First HYPERCALL inside the trampoline hit. We have to store in RAX the address of the bootstrap, we have to restore the old, patched instruction, and we have to patch the return address for the call in order to return to the actual "interrupted" instruction.

NOTE: The ring must already be validated.

Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.
INT_STATUS_NOT_FOUNDIf the current RIP does not point inside the trampoline code.

Definition at line 1422 of file winagent.c.

Referenced by IntWinAgentHandleInt3().

◆ IntWinAgentHandleLoader2Hypercall()

static INTSTATUS IntWinAgentHandleLoader2Hypercall ( PWIN_AGENT  Agent,
PIG_ARCH_REGS  Registers 
)
static

Handles VMCALLs issued by the bootstrap code.

This function handles hyper calls issued by the bootstrap code. There are 3 hyper calls defined:

  1. Allocate. The first hyper call is called as soon as memory is allocated inside the guest for the boot driver. When handling this hyper call, we will simply deliver the boot driver in the freshly allocated memory space.
  2. Start. The second hyper call is called once the boot driver is started (a thread is created which executes the boot driver). Inside this hyper-call we will modify the general purpose registers context in order to divert code execution from the bootstrap to the trampoline, which will resume the execution of the detours instruction.
  3. Complete. The final hyper call is issued when the thread has returned and the space allocated for the boot driver has been freed. NOTE: The ring must already be validated.
Parameters
[in]AgentThe agent.
[in]RegistersThe general purpose registers state.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.
INT_STATUS_NO_DETOUR_EMUIf the VMCALL was handled by this function.
INT_STATUS_NOT_FOUNDIf the VMCALL was not issued by the bootstrap code.

Definition at line 1675 of file winagent.c.

Referenced by IntWinAgentHandleInt3().

◆ IntWinAgentHandleVmcall()

INTSTATUS IntWinAgentHandleVmcall ( QWORD  Rip)

Handle a VMCALL that was executed inside the guest.

This function handles VMCALLs that took place inside the guest. Since the small agent components (trampoline, bootstrap) use INT3, the VMCALL issued by the boot driver and the user-mode applications that get injected by Introcore.

Parameters
[in]RipThe RIP where the VMCALL was initiated.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 2397 of file winagent.c.

Referenced by IntAgentHandleVmcall().

◆ IntWinAgentInit()

void IntWinAgentInit ( void  )

Initialize the agents state.

Definition at line 3333 of file winagent.c.

Referenced by IntWinGuestNew().

◆ IntWinAgentInject()

INTSTATUS IntWinAgentInject ( PFUNC_AgentInjection  InjectionCallback,
PFUNC_AgentCompletion  CompletionCallback,
PFUNC_AgentDeliver  DeliverCallback,
void *  Context,
PBYTE  AgentContent,
DWORD  AgentSize,
BOOLEAN  AgentInternal,
DWORD  AgentTag,
AGENT_TYPE  AgentType,
const CHAR Name,
DWORD  Options,
const CHAR Args,
DWORD  Pid,
PWIN_AGENT Agent 
)

Schedule an agent injection inside the guest.

This function schedules the injection of an agent inside the guest space. If this function succeeds means that the injection has been successfully scheduled; it does not mean that the agent has been successfully injected. This function can be used to inject files or processes inside the guest. This function is also used to inject the VE and PT agents inside the guest. Due to the 3 callbacks architecture, it is very flexible and it allows the caller to extend this mechanism with his own defined callbacks.

Parameters
[in]InjectionCallbackThis callback is called after the boot driver has been successfully injected inside the guest.
[in]CompletionCallbackThis callback is called after the boot driver has finished execution, and the agent is being removed from memory.
[in]DeliverCallbackThis callback is called by VE and PT handlers inside the boot driver. This callback basically allows us to inject a next stage agent, and to initialize it, without having to rely on the guest for that.
[in]ContextOptional agent context.
[in]AgentContentPointer to a memory area containing the actual agent.
[in]AgentSizeThe size of the agent, in bytes.
[in]AgentInternalTrue if this is an internal agent (statically allocated inside Introcore).
[in]AgentTagAgent tag. Check out AGENT_TAG* for more info.
[in]AgentTypeAgent type. Check out AGENT_TYPE* for more info.
[in]NameAgent name.
[in]OptionsAgent options.
[in]ArgsAgent arguments. Passed as a command line for process agents.
[in]PidPID of the process that will be the parent of the agent process. if this is 0, winlogon will be chosen as a parent.
[in]AgentWill contain, in successful return, the handle to the newly scheduled agent.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.
INT_STATUS_OPERATION_NOT_IMPLEMENTEDIf the current guest is not Windows.
INT_STATUS_ALREADY_INITIALIZEDIf an agent with the same name has already been injected.
INT_STATUS_INSUFFICIENT_RESOURCESIf a memory alloc fails.

Definition at line 2608 of file winagent.c.

Referenced by IntPtiInjectPtFilter(), IntPtiRemovePtFilter(), IntVeDeployAgent(), IntVeRemoveAgent(), IntWinDepInjectFile(), and IntWinDepInjectProcess().

◆ IntWinAgentInjectBreakpoint()

INTSTATUS IntWinAgentInjectBreakpoint ( PFUNC_AgentInjection  InjectionCallback,
void *  Context,
PWIN_AGENT Agent 
)

Injects a breakpoint agent inside the guest.

This function injects a breakpoint agent inside the guest. These breakpoint agents are used simply to generate a breakpoint VM exit on the SYSCALL flow, since that flow is the safest to inject kernel faults or to call kernel APIs.

Parameters
[in]InjectionCallbackCallback to be called when the breakpoint is hit.
[in]ContextOptional context to be passed to the callback.
[out]AgentOptional agent handle.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_OPERATION_NOT_IMPLEMENTEDIf the guest is not Windows.
INT_STATUS_INSUFFICIENT_RESOURCESIf a memory alloc fails.

Definition at line 2921 of file winagent.c.

Referenced by IntSwapMemInjectMiniSwapper().

◆ IntWinAgentInjectTrampoline()

INTSTATUS IntWinAgentInjectTrampoline ( void  )

Inject the agent trampoline inside the guest.

The agent trampoline is a small chunk of code that gets injected inside a region of NT slack space. This trampoline is used to intermediate the code transfers to the bootstrap code, which is dynamically allocated and must be freed once the agent is done.

Return values
INT_STATUS_SUCCESSOn success.

Definition at line 364 of file winagent.c.

Referenced by IntWinGuestFinishInit().

◆ IntWinAgentIsPtrInTrampoline()

BOOLEAN IntWinAgentIsPtrInTrampoline ( QWORD  Ptr,
THS_PTR_TYPE  Type 
)

Check if the provided address points inside the agent trampoline.

Parameters
[in]PtrThe pointer to be checked.
[in]TypeThe pointer type: live RIP or stack value.
Returns
True if the address points inside the trampoline, false otherwise.

Definition at line 3219 of file winagent.c.

Referenced by IntAgentIsPtrInTrampoline().

◆ IntWinAgentIsRipInsideCurrentAgent()

BOOLEAN IntWinAgentIsRipInsideCurrentAgent ( QWORD  Rip)

Return true if the given RIP points inside the currently active boot driver.

Parameters
[in]RipThe RIP to be checked.
Returns
True if the Rip points inside an active boot driver, false otherwise.

Definition at line 197 of file winagent.c.

Referenced by IntWinDrvHandleRead().

◆ IntWinAgentReleaseBootstrap()

static INTSTATUS IntWinAgentReleaseBootstrap ( WIN_AGENT Agent,
IG_ARCH_REGS Registers 
)
static

Releases the bootstrap allocated inside the guest.

Parameters
[in]AgentThe agent handle.
[in]RegistersThe general purpose registers state.
Return values
INT_STATUS_NO_DETOUR_EMUAs the call happens on a VMCALL, and we move the RIP ourselves, we do not wish to have this emulated.

Definition at line 1565 of file winagent.c.

Referenced by IntWinAgentHandleLoader2Hypercall(), and IntWinAgentReleaseBootstrapAndRemoveAgent().

◆ IntWinAgentReleaseBootstrapAddress()

INTSTATUS IntWinAgentReleaseBootstrapAddress ( QWORD  Address)

Releases the slack space allocated for the bootstrap code.

Parameters
[in]AddressThe previously allocated bootstrap address.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 2548 of file winagent.c.

Referenced by IntWinAgentIsRipInsideCurrentAgent(), and IntWinAgentRemove().

◆ IntWinAgentReleaseBootstrapAndRemoveAgent()

static INTSTATUS IntWinAgentReleaseBootstrapAndRemoveAgent ( WIN_AGENT Agent,
IG_ARCH_REGS Registers 
)
static

Releases the bootstrap address and removes the agent.

Parameters
[in]AgentThe agent to be removed.
[in]RegistersThe general purpose registers state.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 1641 of file winagent.c.

Referenced by IntWinAgentHandleLoader2Hypercall().

◆ IntWinAgentRemove()

static INTSTATUS IntWinAgentRemove ( PWIN_AGENT  Agent)
static

Removes the given agent.

This function removes an agent. It will restore the hooked instruction and the boot code, and then it will free it.

Parameters
[in]AgentThe agent to be removed.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 272 of file winagent.c.

Referenced by IntWinAgentActivatePendingAgent(), IntWinAgentHandleBreakpointAgent(), IntWinAgentHandleLoader1Hypercall(), IntWinAgentRemoveAgentAndResetState(), and IntWinAgentUnInit().

◆ IntWinAgentRemoveAgentAndResetState()

static INTSTATUS IntWinAgentRemoveAgentAndResetState ( WIN_AGENT Agent)
static

Removes the indicated agent.

Parameters
[in]AgentThe agent to be removed.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 1612 of file winagent.c.

Referenced by IntWinAgentHandleLoader2Hypercall(), and IntWinAgentReleaseBootstrapAndRemoveAgent().

◆ IntWinAgentRemoveEntryByAgid()

void IntWinAgentRemoveEntryByAgid ( DWORD  Counter,
DWORD Tag 
)

Removes an agent name from the list of names, using the ID.

Parameters
[in]CounterThe counter/ID to be removed.
[out]TagOptional tag of the removed name.

Definition at line 3171 of file winagent.c.

Referenced by IntWinAgentHandleAppVmcall(), and IntWinAgentHandleDriverVmcall().

◆ IntWinAgentRestoreState32()

static INTSTATUS IntWinAgentRestoreState32 ( PWIN_AGENT  Agent,
PIG_ARCH_REGS  Registers 
)
static

Restore the general purpose registers state.

This function restores the general purpose registers state after the bootstrap code has finished execution. The transfer from the first section of the bootstrap to the trampoline must be done this way because in rare cases, the thread that we created in the second section of the bootstrap may finish execution BEFORE the first section of the bootstrap gets to return back to the trampoline, and it may lead to a use-after-free situation, where we free the bootstrap inside guest memory before it returned to the trampoline. By doing the transfer ourselves, and using a small semaphore inside the thread, we are ensured that the thread cannot finish execution before the bootstrap returned to the trampoline.

Parameters
[in]AgentThe agent.
[in]RegistersThe general purpose registers state.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 1263 of file winagent.c.

Referenced by IntWinAgentReleaseBootstrap().

◆ IntWinAgentRestoreState64()

static INTSTATUS IntWinAgentRestoreState64 ( PWIN_AGENT  Agent,
PIG_ARCH_REGS  Registers 
)
static

Restore the general purpose registers state.

This function restores the general purpose registers state after the bootstrap code has finished execution. The transfer from the first section of the bootstrap to the trampoline must be done this way because in rare cases, the thread that we created in the second section of the bootstrap may finish execution BEFORE the first section of the bootstrap gets to return back to the trampoline, and it may lead to a use-after-free situation, where we free the bootstrap inside guest memory before it returned to the trampoline. By doing the transfer ourselves, and using a small semaphore inside the thread, we are ensured that the thread cannot finish execution before the bootstrap returned to the trampoline.

Parameters
[in]AgentThe agent.
[in]RegistersThe general purpose registers state.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 1186 of file winagent.c.

Referenced by IntWinAgentReleaseBootstrap().

◆ IntWinAgentSelectBootstrapAddress()

INTSTATUS IntWinAgentSelectBootstrapAddress ( DWORD  Size,
QWORD Address 
)

Finds an in-guest adders that can be used to host the bootstrap code.

This function will try to allocate slack space inside NT. If no such space is found, it will attempt to find slack space inside another loaded module. This is safe, since the small trampoline code jumps to the bootstrap code via an indirect call (CALL rax), so the bootstrap code doesn't have to be withing [-2G, +2G] of the trampoline.

Parameters
[in]SizeThe size required for the bootstrap code.
[out]AddressGuest virtual address where the bootstrap has been allocated.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.

Definition at line 2481 of file winagent.c.

Referenced by IntWinAgentActivatePendingAgent(), and IntWinAgentIsRipInsideCurrentAgent().

◆ IntWinAgentSelectTokens()

static void IntWinAgentSelectTokens ( QWORD Token1,
QWORD Token2,
QWORD Token3 
)
static

Randomly select 3 tokens to be used by the bootstrap code when issuing hyper calls.

Since we don't want to allow random hyper calls from guest space, we randomly generate 3 tokens which will be passed to Introcore by the bootstrap code. Each token must match when calling Introcore.

Parameters
[out]Token1The first token.
[out]Token2The second token.
[out]Token3The third token.

Definition at line 2566 of file winagent.c.

Referenced by IntWinAgentInject().

◆ IntWinAgentUnInit()

INTSTATUS IntWinAgentUnInit ( void  )

Uninit the agents state.

Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_NOT_INITIALIZED_HINTIf the agents state has not been initialized yet.

Definition at line 3352 of file winagent.c.

Referenced by IntWinGuestUninit().

Variable Documentation

◆ gAgentState

AGENT_STATE gAgentState = {0}
static

Definition at line 192 of file winagent.c.

◆ gTrampolineZero

BYTE gTrampolineZero[4096] = {0}

Just a page filled with zeros.

Definition at line 164 of file winagent.c.