Bitdefender Hypervisor Memory Introspection
|
This module is responsibility for reading guest memory that may be swapped out. More...
Go to the source code of this file.
Data Structures | |
struct | _SWAPMEM_TRANSACTION |
struct | _SWAPMEM_PAGE |
struct | _SWAPMEM_STATE |
Typedefs | |
typedef struct _SWAPMEM_TRANSACTION | SWAPMEM_TRANSACTION |
typedef struct _SWAPMEM_TRANSACTION * | PSWAPMEM_TRANSACTION |
typedef struct _SWAPMEM_PAGE | SWAPMEM_PAGE |
typedef struct _SWAPMEM_PAGE * | PSWAPMEM_PAGE |
typedef struct _SWAPMEM_STATE | SWAPMEM_STATE |
typedef struct _SWAPMEM_STATE * | PSWAPMEM_STATE |
Functions | |
static INTSTATUS | IntSwapMemCleanupCallback (void *DataAddress, QWORD DataInfo) |
Cleans up a transaction, by freeing the data buffer, the context and the transaction itself. More... | |
static INTSTATUS | IntSwapMemCancelTransaction (PSWAPMEM_TRANSACTION Transaction) |
Cancels a transaction. More... | |
static INTSTATUS | IntSwapMemHandleBreakpointAgent (QWORD GuestVirtualAddress, DWORD AgentTag, void *Context) |
Handles a breakpoint agent. More... | |
static INTSTATUS | IntSwapMemInjectMiniSwapper (QWORD VirtualAddress) |
Injects the mini swapper, which is basically just a breakpoint agent. More... | |
static INTSTATUS | IntSwapMemPageSwappedIn (void *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize) |
Handle a page swap-in event. More... | |
INTSTATUS | IntSwapMemReadData (QWORD Cr3, QWORD VirtualAddress, DWORD Length, DWORD Options, void *Context, DWORD ContextTag, PFUNC_PagesReadCallback Callback, PFUNC_PreInjectCallback PreInject, void **SwapHandle) |
Reads a region of guest virtual memory, and calls the indicated callback when all the data is available. More... | |
static PSWAPMEM_PAGE | IntSwapMemFindPendingPage (QWORD VirtualAddress, QWORD Cr3) |
Finds a pending page, given the guest virtual address and the Cr3. More... | |
INTSTATUS | IntSwapMemInjectPendingPF (void) |
Inject a PF for a pending page. More... | |
void | IntSwapMemCancelPendingPF (QWORD VirtualAddress) |
Cancel a pending PF. More... | |
void | IntSwapMemReinjectFailedPF (void) |
Reinject timed-out PFs. More... | |
INTSTATUS | IntSwapMemRemoveTransaction (void *Transaction) |
Remove a transaction. More... | |
INTSTATUS | IntSwapMemRemoveTransactionsForVaSpace (QWORD Cr3) |
Remove all transactions initiated for a virtual address space. More... | |
INTSTATUS | IntSwapMemInit (void) |
Init the swapmem system. More... | |
INTSTATUS | IntSwapMemUnInit (void) |
Uninit the swapmem system. More... | |
void | IntSwapMemDump (void) |
Dump all active transactions & pages. More... | |
Variables | |
static SWAPMEM_STATE | gSwapState = { 0 } |
This module is responsibility for reading guest memory that may be swapped out.
This module handles reading guest virtual memory. Usually, a required range of guest memory will not be present entirely inside the guest physical memory. The missing pages may be swapped in at some time in the future, but there's no guarantee. In order to ensure that we will gain access to the swapped out data, this module will schedule page-fault injections for missing pages. The doesn't have to deal with low-level aspects of paging, all it has to do is call IntSwapMemReadData, and wait for the data read callback to be called. Internally, this module will read all the data already available, and, for the missing pages, it will inject PFs inside the guest. NOTE: We can inject only a single PF at any given moment, even if more than 1 VCPU are present. This greatly simplifies the scheduler and any PF tracking logic, by also avoiding too many PFs to be injected at the same time (for example, on a guest with multiple VCPUs, we may cause a significant performance overhead if we were to inject a PF on each VCPU). NOTE: When reading kernel memory, the PF will be injected in the context of a SYSCALL, where interrupt are enabled, no locks are being held and we are in paged context. NOTE: The caller may wish to avoid injecting a PF. If it desires to, it can use the SWAPMEM_OPT_NO_FAULT option, which will tell this module to not inject any PF in order to read the indicated region of memory. Instead, it will simply wait for the required pages to be naturally swapped in as a result of guest access. NOTE: When we inject a PF, there is no guarantee that the PF will indeed reach the guest; sometimes, the HV may decide to inject something else (for example, an IPI, or another exception), and, as a result, the PF that we tried to inject will be lost. However, in order to address this, there is the event injection callback (IntHandleEventInjection in callbacks.c) which gets called immediately after injecting an event inside the guest, if we had a pending PF (or another pending exception). This callback will receive as arguments the injected vector, the CR2 and the error code; using these arguments, we can check if the PF which we wanted got injected, and if it wasn't, we can simply retry injecting it later. This allows the scheduler to know exactly if a swapmem PF was injected or not.
Definition in file swapmem.c.
typedef struct _SWAPMEM_PAGE * PSWAPMEM_PAGE |
typedef struct _SWAPMEM_STATE * PSWAPMEM_STATE |
typedef struct _SWAPMEM_TRANSACTION * PSWAPMEM_TRANSACTION |
typedef struct _SWAPMEM_PAGE SWAPMEM_PAGE |
Describes one page that will be read. A transaction contains a page entry for each page of virtual memory it needs to read from guest space.
typedef struct _SWAPMEM_STATE SWAPMEM_STATE |
Global swapmem state.
typedef struct _SWAPMEM_TRANSACTION SWAPMEM_TRANSACTION |
This context is used only internally. It represents a swapmem transaction. A transaction is a request to read a contiguous portion of guest virtual memory, parts of which may not be present in physical memory.
void IntSwapMemCancelPendingPF | ( | QWORD | VirtualAddress | ) |
Cancel a pending PF.
Cancel the pending PF on the provided VirtualAddress. This can happen if we requested a PF injection, but the HV had to inject something else. In this case, we cancel the injection, and we mark the page Ready, allowing it to be re-injected later.
[in] | VirtualAddress | The virtual address the PF was requested for. |
Definition at line 879 of file swapmem.c.
Referenced by IntHandleEventInjection(), and IntSwapMemHandleBreakpointAgent().
|
static |
Cancels a transaction.
[in] | Transaction | The transaction to be canceled. |
INT_STATUS_SUCCESS | On success. |
Definition at line 141 of file swapmem.c.
Referenced by IntSwapMemReadData(), IntSwapMemRemoveTransaction(), and IntSwapMemRemoveTransactionsForVaSpace().
Cleans up a transaction, by freeing the data buffer, the context and the transaction itself.
[in] | DataAddress | Transaction address. |
[in] | DataInfo | Unused. |
INT_STATUS_SUCCESS | On success. |
Definition at line 105 of file swapmem.c.
Referenced by IntSwapMemCancelTransaction(), and IntSwapMemPageSwappedIn().
void IntSwapMemDump | ( | void | ) |
|
static |
|
static |
Handles a breakpoint agent.
This callback is called as soon as a breakpoint agent gets triggered inside the guest. Inside this function, we will simply inject a PF, which is usually for a kernel address. The PF is injected on the SYSCALL flow, where interrupts are enabled, and no locks are held, making it safe.
[in] | GuestVirtualAddress | Unused. |
[in] | AgentTag | Unused. |
[in] | Context | The kernel guest virtual address we inject the PF for. |
INT_STATUS_SUCCESS | On success. |
Definition at line 195 of file swapmem.c.
Referenced by IntSwapMemInjectMiniSwapper().
INTSTATUS IntSwapMemInit | ( | void | ) |
Init the swapmem system.
INT_STATUS_SUCCESS | On success. |
Definition at line 1026 of file swapmem.c.
Referenced by IntGuestInit().
Injects the mini swapper, which is basically just a breakpoint agent.
[in] | VirtualAddress | The kernel virtual address to be swapped in. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_OPERATION_NOT_IMPLEMENTED | If the OS is not Windows. |
Definition at line 235 of file swapmem.c.
Referenced by IntSwapMemInjectPendingPF().
INTSTATUS IntSwapMemInjectPendingPF | ( | void | ) |
Inject a PF for a pending page.
This is the main PF scheduling algorithm. This is called before returning from every callback, and it checks the list of existing transactions & pages, in order to inject a PF inside the guest. Note that only a single PF can be injected at any given time (even if we have multiple VCPUs), as this makes the scheduler much simpler, and it avoids spamming the guest kernel with unexpected PFs. Before injecting a PF, it makes sure the context is right (user/kernel, CR3), and it calls the PreInjectCallback; of the PreInject callback does not return INT_STATUS_SUCCESS, the PF for that page will not be injected, and another page will be selected. PFs can be either injected directly (user-mode PFs, when the context is right) or indirectly (kernel-mode PFs) via the breakpoint agent. No other PFs will be injected until the pending one has been handled (the page has been swapped in).
INT_STATUS_SUCCESS | On success. |
Definition at line 698 of file swapmem.c.
Referenced by IntGuestPreReturnCallback().
|
static |
Handle a page swap-in event.
This function is called when the translation of a page we wish to read is modified. We will check if the page has been swapped in, and if it has, we will read its contents. Once the entire memory region is read (which may include more than one page), the swap-in callback will be called, sending along a pointer to the read data. After a page has been swapped in and read, the page-table hooks set for that page translation is removed.
[in] | Context | Optional context, points to the swapped in page structure, SWAPMEM_PAGE. |
[in] | VirtualAddress | The guest virtual address whose translation has just been modified. |
[in] | OldEntry | Old page table entry. |
[in] | NewEntry | New page table entry. |
[in] | OldPageSize | Old page size. |
[in] | NewPageSize | New page size. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_INVALID_PARAMETER | If an invalid parameter is supplied. |
Definition at line 259 of file swapmem.c.
Referenced by IntSwapMemReadData().
INTSTATUS IntSwapMemReadData | ( | QWORD | Cr3, |
QWORD | VirtualAddress, | ||
DWORD | Length, | ||
DWORD | Options, | ||
void * | Context, | ||
DWORD | ContextTag, | ||
PFUNC_PagesReadCallback | Callback, | ||
PFUNC_PreInjectCallback | PreInject, | ||
void ** | SwapHandle | ||
) |
Reads a region of guest virtual memory, and calls the indicated callback when all the data is available.
The function will read Length bytes from VirtualAddress inside Cr3 address space. The function may either read the data directly, if it is present inside the physical memory, or it may inject a page fault in order to force a swap-in of the pages containing the data. Callback will be invoked when all the data has been read. The callback may be invoked synchronously or asynchronously: if a page fault is needed to read parts of the data, it will invoked asynchronously. Otherwise, it will be invoked synchronously. The flag SWAPMEM_FLAG_ASYNC_CALL will be set in the Flags argument of the callback for asynchronously calls. The function can be used to read data of arbitrary length (including data spanning multiple pages). Some pages may be swapped in as a result of us injecting a PF for them, others may be swapped in naturally due to the normal guest activity, and other pages may already be present when calling this function. The PreInject callback will be called before actually injecting a PF inside the guest. This callback is optional, but if it present and it returns INT_STATUS_NOT_NEEDED_HINT, the PF will be inhibited, and therefore, it will not be injected.
[in] | Cr3 | Virtual address space where we wish to read data from. |
[in] | VirtualAddress | Guest virtual address we wish to read from. |
[in] | Length | Length, in bytes. Can span multiple pages. |
[in] | Options | Options. Check out SWAPMEM_OPT* for more info. |
[in] | Context | Optional context to be passed to the callbacks. |
[in] | ContextTag | If Context is provided and ContextTag is not 0, the Context will be freed when removing the transaction. |
[in] | Callback | Called once all the data is available. |
[in] | PreInject | Callback invoked BEFORE injecting the PF. If this returns INT_STATUS_NOT_NEEDED_HINT, the PF will not be injected. |
[out] | SwapHandle | Contains, upon return, a handle to the swap object. If SWAPMEM_OPT_NO_DUPS is used and a transaction is already present, SwapHandle will be set to NULL. Since the transactions are not reference counted, it would be problematic to return a handle, as it can then get freed more than once. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_INSUFFICIENT_RESOURCES | If a memory alloc fails. |
Definition at line 417 of file swapmem.c.
Referenced by DbgSwap(), IntWinDagentCheckSuspiciousDllLoad(), IntWinDagentHandleVerifierReason(), IntWinDrvProtect(), IntWinGetPrcoCmdLineHandleCmdLineInMemory(), IntWinGetPrcoCmdLineHandleUserParamsInMemory(), IntWinGuestFindDriversNamespace(), IntWinGuestNew(), IntWinGuestReadKernel(), IntWinHalFindPerformanceCounter(), IntWinHalReadHal(), IntWinModBlockBlockModuleLoad(), IntWinModHandleModulePathInMemory(), IntWinObjHandleDirectoryEntryInMemory(), IntWinObjHandleDriverDirectoryEntryInMemory(), IntWinObjHandleObjectInMemory(), IntWinObjHandleRootDirTagInMemory(), IntWinObjParseDriverDirectory(), IntWinProcReadCommandLine(), IntWinStackHandleUserStackPagedOut(), IntWinUmModCacheFillExports(), IntWinVadFetchImageName(), and IntWinVadIsExecSuspicious().
void IntSwapMemReinjectFailedPF | ( | void | ) |
Reinject timed-out PFs.
Sometimes, injected PFs may get lost, mainly due to the HV, or may be dropped unexpectedly inside the guest. We detect this by maintaining a time-stamp for each pending PF, and retrying the injection after aprox. 1s. Note that reinjecting a PF more than once is not an issue, as the OS can handle this kind of spurious PFs.
Definition at line 913 of file swapmem.c.
Referenced by IntHandleTimer().
INTSTATUS IntSwapMemRemoveTransaction | ( | void * | Transaction | ) |
Remove a transaction.
Once a transaction is removed, the callback will no longer be called, and the swap hooks will be removed. The data will no longer be available. This should be called, for example, when there is a pending read for a process that is just terminating.
[in] | Transaction | The transaction to be removed. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_INVALID_PARAMETER | If an invalid parameter is supplied. |
Definition at line 942 of file swapmem.c.
Referenced by IntWinDrvUnprotect(), IntWinGuestCancelKernelRead(), IntWinHalCancelRead(), IntWinModBlockRemoveBlockObject(), IntWinModCancelExportTransactions(), IntWinModRemoveModule(), IntWinObjCancelRootTransactions(), IntWinObjCleanup(), IntWinProcRemoveProcess(), IntWinProcSwapOut(), and IntWinVadDestroyObject().
Remove all transactions initiated for a virtual address space.
Will remove all active transactions for the given VA space. The read-data callback will not be called for any of the aborted transactions. Useful when a process is terminating.
[in] | Cr3 | The virtual address space. |
INT_STATUS_SUCCESS | On success. |
Definition at line 982 of file swapmem.c.
Referenced by IntWinProcRemoveProcess(), and IntWinProcSwapOut().
INTSTATUS IntSwapMemUnInit | ( | void | ) |
Uninit the swapmem system.
INT_STATUS_SUCCESS | On success. |
INT_STATUS_NOT_INITIALIZED_HINT | If the system has not been initialized. |
Definition at line 1044 of file swapmem.c.
Referenced by IntGuestUninit().
|
static |