Closed soltrac closed 4 years ago
Hi,
I do not plan to add that code. I plan to keep this project small and focused on interaction with Intel-VTx specifics, instead of how to apply the technology for specific purposes.
Best, Satoshi
Hi,
I do not plan to add that code. I plan to keep this project small and focused on interaction with Intel-VTx specifics, instead of how to apply the technology for specific purposes.
Best, Satoshi
Hi Satoshi,
Sorry to revive this closed issue, however I am also interested in seeing the same code as requested by the original author of this issue. I understand that you want to keep the project small and focused on interaction with Intel-VTx specifics, but I was wondering if maybe you could add the example hook as a gist or maybe even in a separate branch? Or maybe point us to some references that may point us in the right direction. Specifically, I am confused on exactly when I should be placing the hook and how to intercept execution at that time frame.
Thanks.
@CoolA1d Learn about EPT hooking. Satoshi has tons of examples of this on his GitHub already (see: DdiMon). There's also a great article here: http://phrack.org/issues/69/15.html#article
Fair enough, and thanks for explaining challenge you are facing. I am going to take some time to explain basic approaches and how I implemented mine, with the focus on answering to your questions. As @hypervisor pointed, if you have never implemented the same hooking with type-2 hypervisors, you should do that first as I would focus on differences in type-1 hypervisors.
The basic idea I implemented is to place helper code in the guest, and to have the host redirect the guest execution to helper code to hook a function. Trigger of this is at KiSystemStartup
.
At the high-level, changes and execution flow look like this:
HandleInitializeGuestAgent
AsmGuestAgentEntryPoint
I have attached GuestAgent code I wrote: https://gist.github.com/tandasat/bf0189952f113518f75c4f008c1e8d04
*. The last thing to note is the pointer to the GuestAgent code must be in the virtual address. This requires a pointer conversion with ConvertPointer
, and not just g_AsmGuestAgentEntryPoint = (UINT64)AsmGuestAgentEntryPoint;
.
// (1)
case IA32_GS_BASE:
//
// Write to this MSR happens at KiSystemStartup for each processor.
// We intercept this only once that occurs on the BSP as a trigger
// point to initialize the guest agent. We do not emulate this
// operation and instead, make the guest retry after returning from
// the guest agent. At the 2nd try, VM-exit no longer occurs.
//
// Note that is place is too early to debug the guest agent. Move to
// later such as KeInitAmd64SpecificState for this. This place was
// chosen so that none of PatchGuard context is initialized.
//
UpdateMsrBitmaps(GuestContext->Contexts->SharedMsrBitmaps,
IA32_GS_BASE,
OperationWrite,
FALSE);
LOG_INFO("KiSystemStartup being executed. Initializing the guest agent.");
InjectGuestAgentTask(GuestContext, GuestAgentCommandInitialize);
return;
// (4)
HandleVmCall (
_Inout_ GUEST_CONTEXT* GuestContex
)
{
UINT64 hypercallNumber;
//
// Take care of the special VMCALL made by the guest agent.
//
if (IsGuestAgentReturnVmcall(GuestContext) != FALSE)
{
RestoreGuestContext(GuestContext);
goto Exit;
}
// Code used in (1) and (4)
VOID
AsmGuestAgentEntryPoint (
);
VOID
AsmGuestAgentEntryPointEnd (
);
static GUEST_AGENT_STACK g_HostGuestAgentContext;
UINT64 g_GuestAgentStack = (UINT64)&g_HostGuestAgentContext.u.Layout.Context;
UINT64 g_AsmGuestAgentEntryPoint = (UINT64)AsmGuestAgentEntryPoint;
UINT64 g_AsmGuestAgentEntryPointEnd = (UINT64)AsmGuestAgentEntryPointEnd;
static
BOOLEAN
IsGuestAgentReturnVmcall (
_In_ CONST GUEST_CONTEXT* GuestContext
)
{
return ((GuestContext->VmcsBasedRegisters.Rip > g_AsmGuestAgentEntryPoint) &&
(GuestContext->VmcsBasedRegisters.Rip < g_AsmGuestAgentEntryPointEnd) &&
(GuestContext->VmcsBasedRegisters.Rsp == g_GuestAgentStack));
}
static
VOID
RestoreGuestContext (
_In_ CONST GUEST_CONTEXT* GuestContext
)
{
UNREFERENCED_PARAMETER(GuestContext);
LOG_INFO("Returning from the guest agent.");
VmxWrite(VMCS_GUEST_RIP, g_HostGuestAgentContext.u.Layout.Context.OriginalGuestRip);
VmxWrite(VMCS_GUEST_RSP, g_HostGuestAgentContext.u.Layout.Context.OriginalGuestRsp);
}
static
VOID
InjectGuestAgentTask (
_In_ CONST GUEST_CONTEXT* GuestContext,
_In_ GUEST_AGENT_COMMAND CommandNumber
)
{
LOG_INFO("Transferring to the guest agent.");
g_HostGuestAgentContext.u.Layout.Context.OriginalGuestRip = GuestContext->VmcsBasedRegisters.Rip;
g_HostGuestAgentContext.u.Layout.Context.OriginalGuestRsp = GuestContext->VmcsBasedRegisters.Rsp;
g_HostGuestAgentContext.u.Layout.Context.CommandNumber = CommandNumber;
VmxWrite(VMCS_GUEST_RIP, g_AsmGuestAgentEntryPoint);
VmxWrite(VMCS_GUEST_RSP, g_GuestAgentStack);
}
// Pointer conversion
extern UINT64 g_GuestAgentStack;
extern UINT64 g_AsmGuestAgentEntryPoint;
extern UINT64 g_AsmGuestAgentEntryPointEnd;
static
VOID
EFIAPI
HandleSetVirtualAddressMap (
EFI_EVENT Event,
VOID* Context
)
{
EFI_STATUS status;
UINT64 guestAgentStack;
UINT64 asmGuestAgentEntryPoint;
UINT64 asmGuestAgentEntryPointEnd;
LOG_INFO("SetVirtualAddressMap was called.");
guestAgentStack = g_GuestAgentStack;
asmGuestAgentEntryPoint = g_AsmGuestAgentEntryPoint;
asmGuestAgentEntryPointEnd = g_AsmGuestAgentEntryPointEnd;
status = gRT->ConvertPointer(0, (VOID**)&g_GuestAgentStack);
MV_ASSERT(EFI_ERROR(status) == FALSE);
status = gRT->ConvertPointer(0, (VOID**)&g_AsmGuestAgentEntryPoint);
MV_ASSERT(EFI_ERROR(status) == FALSE);
status = gRT->ConvertPointer(0, (VOID**)&g_AsmGuestAgentEntryPointEnd);
MV_ASSERT(EFI_ERROR(status) == FALSE);
}
Thank you so much for the detailed reply! I will go over this later and let you know if I have any questions. You're amazing.
Thanks tandasat, all your posts and projects are amazing and good to learn from. You're also one of the nicest developers ^^
I'm struggling to integrate your post above but i'll keep trying.
Edit: Just so everyone understands as I was confused myself, this is literally inline patch the function early and let PG protect it/not blue screen you afterwards. This doesn't hide the changes like with EPT/ddimon. Still very cool for certain use cases.
Hello,
Would it be possible to add an example of how you hook for example ExAllocatePoolTag in your readme? I cannot find anything relate to it on the source code. I've only found how you avoid the initialization of PG.
Thanks.