HyperDbg/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c

476 lines
12 KiB
C
Raw Permalink Normal View History

2020-04-11 06:52:24 -07:00
/**
* @file PoolManager.c
2022-01-18 22:38:56 +03:30
* @author Sina Karvandi (sina@hyperdbg.org)
2020-04-11 06:52:24 -07:00
* @brief The pool manager used in vmx root
* @details As we cannot allocate pools in vmx root, we need a pool
* manager to manage the pools
*
2020-04-11 06:52:24 -07:00
* @version 0.1
* @date 2020-04-11
*
2020-04-11 06:52:24 -07:00
* @copyright This project is released under the GNU Public License v3.
*
2020-04-11 06:52:24 -07:00
*/
#include "pch.h"
2020-03-24 06:35:02 -07:00
// ----------------------------------------------------------------------------
// Private Interfaces
//
BOOLEAN
PlmgrAllocateRequestNewAllocation(SIZE_T NumberOfBytes)
{
//
// Allocate global requesting variable
//
g_RequestNewAllocation = PlatformMemAllocateZeroedNonPagedPool(NumberOfBytes);
if (!g_RequestNewAllocation)
{
return FALSE;
}
return TRUE;
}
VOID
PlmgrFreeRequestNewAllocation(VOID)
{
PlatformMemFreePool(g_RequestNewAllocation);
2023-07-06 16:12:28 +09:00
g_RequestNewAllocation = NULL;
}
// ----------------------------------------------------------------------------
// Public Interfaces
//
2020-04-11 06:52:24 -07:00
/**
* @brief Initializes the pool manager
*
* @return BOOLEAN
2020-04-11 06:52:24 -07:00
*/
2020-04-10 00:57:32 -07:00
BOOLEAN
PoolManagerInitialize()
{
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// Allocate global requesting variable
2020-04-11 06:52:24 -07:00
//
SIZE_T BufferSize = MaximumRequestsQueueDepth * sizeof(REQUEST_NEW_ALLOCATION);
if (!PlmgrAllocateRequestNewAllocation(BufferSize))
2020-04-10 00:57:32 -07:00
{
2021-08-30 17:23:17 +04:30
LogError("Err, insufficient memory");
2020-04-10 00:57:32 -07:00
return FALSE;
}
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// Initialize list head
2020-04-11 06:52:24 -07:00
//
2020-08-28 04:03:12 -07:00
InitializeListHead(&g_ListOfAllocatedPoolsHead);
2020-04-11 08:40:02 -07:00
2020-08-28 04:03:12 -07:00
//
2026-06-05 20:12:30 +02:00
// Nothing to deallocate or allocate at the beginning
2020-08-28 04:03:12 -07:00
//
2026-06-05 20:12:30 +02:00
g_IsNewRequestForDeAllocation = FALSE;
g_IsNewRequestForAllocationReceived = FALSE;
//
// Memory allocator is initialized
//
g_PoolManagerInitialized = TRUE;
2020-08-28 04:03:12 -07:00
2020-04-11 06:52:24 -07:00
//
// Initialized successfully
2020-04-11 06:52:24 -07:00
//
return TRUE;
2020-03-24 06:35:02 -07:00
}
2020-04-11 06:52:24 -07:00
/**
* @brief Uninitialize the pool manager (free the buffers, etc.)
*
* @return VOID
2020-04-11 06:52:24 -07:00
*/
2020-04-10 00:57:32 -07:00
VOID
PoolManagerUninitialize()
{
PLIST_ENTRY Link;
2026-06-05 20:12:30 +02:00
//
// Pool manager is not initialized anymore
//
g_PoolManagerInitialized = FALSE;
2020-03-24 06:35:02 -07:00
SpinlockLock(&LockForReadingPool);
Link = g_ListOfAllocatedPoolsHead.Flink;
while (Link != &g_ListOfAllocatedPoolsHead)
2020-04-10 00:57:32 -07:00
{
PLIST_ENTRY Next = Link->Flink;
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// Get the head of the record
2020-04-11 06:52:24 -07:00
//
PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(Link, POOL_TABLE, PoolsList);
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-08-28 04:03:12 -07:00
// Free the alloocated buffer (if not already changed)
2020-04-11 06:52:24 -07:00
//
2020-08-28 04:03:12 -07:00
if (!PoolTable->AlreadyFreed)
{
PlatformMemFreePool((PVOID)PoolTable->Address);
2020-08-28 04:03:12 -07:00
}
//
// Unlink the PoolTable
//
RemoveEntryList(Link);
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// Free the record itself
2020-04-11 06:52:24 -07:00
//
PlatformMemFreePool(PoolTable);
Link = Next;
2020-04-10 00:57:32 -07:00
}
2020-03-24 06:35:02 -07:00
InitializeListHead(&g_ListOfAllocatedPoolsHead);
g_IsNewRequestForDeAllocation = FALSE;
g_IsNewRequestForAllocationReceived = FALSE;
SpinlockUnlock(&LockForReadingPool);
PlmgrFreeRequestNewAllocation();
2020-08-28 04:03:12 -07:00
}
/**
* @brief This function set a pool flag to be freed, and it will be freed
* on the next IOCTL when it's safe to remove
*
2020-08-28 04:03:12 -07:00
* @param AddressToFree The pool address that was previously obtained from the pool manager
* @return BOOLEAN If the address was already in the list of allocated pools by pool
2020-08-28 04:03:12 -07:00
* manager then it returns TRUE; otherwise, FALSE
*/
BOOLEAN
PoolManagerFreePool(UINT64 AddressToFree)
{
PLIST_ENTRY ListTemp = 0;
BOOLEAN Result = FALSE;
ListTemp = &g_ListOfAllocatedPoolsHead;
SpinlockLock(&LockForReadingPool);
while (&g_ListOfAllocatedPoolsHead != ListTemp->Flink)
{
ListTemp = ListTemp->Flink;
//
// Get the head of the record
//
PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(ListTemp, POOL_TABLE, PoolsList);
if (PoolTable->Address == AddressToFree)
{
//
// We found an entry that matched the detailed from
// previously allocated pools
//
PoolTable->ShouldBeFreed = TRUE;
Result = TRUE;
g_IsNewRequestForDeAllocation = TRUE;
break;
}
}
SpinlockUnlock(&LockForReadingPool);
return Result;
2020-03-24 06:35:02 -07:00
}
/**
* @brief Shows list of pre-allocated pools available (used for debugging purposes)
*
* @return VOID
*/
VOID
PoolManagerShowPreAllocatedPools()
{
PLIST_ENTRY ListTemp = 0;
ListTemp = &g_ListOfAllocatedPoolsHead;
while (&g_ListOfAllocatedPoolsHead != ListTemp->Flink)
{
ListTemp = ListTemp->Flink;
//
// Get the head of the record
//
PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(ListTemp, POOL_TABLE, PoolsList);
LogInfo("Pool details, Pool intention: %x | Pool address: %llx | Pool state: %s | Should be freed: %s | Already freed: %s\n",
PoolTable->Intention,
PoolTable->Address,
PoolTable->IsBusy ? "used" : "free",
PoolTable->ShouldBeFreed ? "true" : "false",
PoolTable->AlreadyFreed ? "true" : "false");
}
}
2020-04-11 06:52:24 -07:00
/**
* @brief This function should be called from vmx-root in order to get a pool from the list
* @details If RequestNewPool is TRUE then Size is used, otherwise Size is useless
* Note : Most of the times this function called from vmx root but not all the time
*
2020-04-11 06:52:24 -07:00
* @param Intention The intention why we need this pool for (buffer tag)
* @param RequestNewPool Create a request to allocate a new pool with the same size, next time
* that it's safe to allocate (this way we never ran out of pools for this "Intention")
* @param Size If the RequestNewPool is true the we should specify a size for the new pool
2024-03-17 18:17:38 +09:00
* @return UINT64 Returns a pool address or returns null if there was an error
2020-04-11 06:52:24 -07:00
*/
2020-04-10 00:57:32 -07:00
UINT64
PoolManagerRequestPool(POOL_ALLOCATION_INTENTION Intention, BOOLEAN RequestNewPool, UINT32 Size)
2020-03-24 06:35:02 -07:00
{
UINT64 Address = 0;
ScopedSpinlock(
LockForReadingPool,
LIST_FOR_EACH_LINK(g_ListOfAllocatedPoolsHead, POOL_TABLE, PoolsList, PoolTable) {
if (PoolTable->Intention == Intention && PoolTable->IsBusy == FALSE)
{
PoolTable->IsBusy = TRUE;
Address = PoolTable->Address;
break;
}
});
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-04-11 08:40:02 -07:00
// Check if we need additional pools e.g another pool or the pool
2020-04-11 06:52:24 -07:00
// will be available for the next use blah blah
//
2020-04-10 00:57:32 -07:00
if (RequestNewPool)
{
PoolManagerRequestAllocation(Size, 1, Intention);
}
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// return Address might be null indicating there is no valid pools
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
return Address;
2020-03-24 06:35:02 -07:00
}
2020-04-11 06:52:24 -07:00
/**
* @brief Allocate the new pools and add them to pool table
* @details This function doesn't need lock as it just calls once from PASSIVE_LEVEL
*
2020-04-11 06:52:24 -07:00
* @param Size Size of each chunk
* @param Count Count of chunks
* @param Intention The Intention of the buffer (buffer tag)
2023-03-23 18:42:33 +09:00
* @return BOOLEAN If the allocation was successful it returns true and if it was
2020-04-11 06:52:24 -07:00
* unsuccessful then it returns false
*/
2020-04-10 00:57:32 -07:00
BOOLEAN
PoolManagerAllocateAndAddToPoolTable(SIZE_T Size, UINT32 Count, POOL_ALLOCATION_INTENTION Intention)
2020-03-24 06:35:02 -07:00
{
for (SIZE_T i = 0; i < Count; i++)
2020-04-10 00:57:32 -07:00
{
POOL_TABLE * SinglePool = NULL;
SinglePool = PlatformMemAllocateZeroedNonPagedPool(sizeof(POOL_TABLE));
2020-03-24 06:35:02 -07:00
2020-04-10 00:57:32 -07:00
if (!SinglePool)
{
2021-08-30 17:23:17 +04:30
LogError("Err, insufficient memory");
2020-04-10 00:57:32 -07:00
return FALSE;
}
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// Allocate the buffer
2020-04-11 06:52:24 -07:00
//
SinglePool->Address = (UINT64)PlatformMemAllocateZeroedNonPagedPool(Size);
2020-03-24 06:35:02 -07:00
2020-04-10 00:57:32 -07:00
if (!SinglePool->Address)
{
PlatformMemFreePool(SinglePool);
2021-08-30 17:23:17 +04:30
LogError("Err, insufficient memory");
2020-04-10 00:57:32 -07:00
return FALSE;
}
2020-03-24 06:35:02 -07:00
2020-04-10 00:57:32 -07:00
SinglePool->Intention = Intention;
SinglePool->IsBusy = FALSE;
SinglePool->ShouldBeFreed = FALSE;
2020-08-28 04:03:12 -07:00
SinglePool->AlreadyFreed = FALSE;
2020-04-10 00:57:32 -07:00
SinglePool->Size = Size;
2020-03-24 06:35:02 -07:00
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// Add it to the list
2020-04-11 06:52:24 -07:00
//
2020-08-28 04:03:12 -07:00
InsertHeadList(&g_ListOfAllocatedPoolsHead, &(SinglePool->PoolsList));
2020-04-10 00:57:32 -07:00
}
2022-03-15 16:30:37 +08:00
return TRUE;
2020-03-24 06:35:02 -07:00
}
2020-04-11 06:52:24 -07:00
/**
2020-08-28 04:03:12 -07:00
* @brief This function performs allocations from VMX non-root based on g_RequestNewAllocation
*
2023-07-13 16:05:42 +09:00
* @return BOOLEAN If the pool manager allocates buffer or there was no buffer to allocate
2020-04-11 06:52:24 -07:00
* then it returns true, if there was any error then it returns false
*/
2020-04-10 00:57:32 -07:00
BOOLEAN
2020-08-28 04:03:12 -07:00
PoolManagerCheckAndPerformAllocationAndDeallocation()
2020-04-10 00:57:32 -07:00
{
BOOLEAN Result = TRUE;
2020-04-10 00:57:32 -07:00
2020-04-11 06:52:24 -07:00
//
2026-06-05 20:12:30 +02:00
// Make sure we're on vmx non-root and also we have new allocation
// and also pool manager is initialized, otherwise we shouldn't allocate or deallocate
2020-04-11 06:52:24 -07:00
//
2026-06-05 20:12:30 +02:00
if (!g_PoolManagerInitialized || VmFuncVmxGetCurrentExecutionMode() == TRUE)
2020-04-10 00:57:32 -07:00
{
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// allocation's can't be done from vmx root
2026-06-05 20:12:30 +02:00
// or pool manager is not initialized yet
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
return FALSE;
}
//
// Make sure paging works properly
//
2020-04-10 00:57:32 -07:00
PAGED_CODE();
SpinlockLock(&LockForReadingPool);
2020-08-28 04:03:12 -07:00
//
// Check for new allocation
//
if (g_IsNewRequestForAllocationReceived)
2020-04-10 00:57:32 -07:00
{
for (SIZE_T i = 0; i < MaximumRequestsQueueDepth; i++)
2020-08-28 04:03:12 -07:00
{
REQUEST_NEW_ALLOCATION * CurrentItem = &g_RequestNewAllocation[i];
if (CurrentItem->Size != 0)
2020-08-28 04:03:12 -07:00
{
Result = PoolManagerAllocateAndAddToPoolTable(CurrentItem->Size,
CurrentItem->Count,
CurrentItem->Intention);
2020-08-28 04:03:12 -07:00
//
// Free the data for future use
//
CurrentItem->Count = 0;
CurrentItem->Intention = 0;
CurrentItem->Size = 0;
2020-08-28 04:03:12 -07:00
}
}
2020-04-10 00:57:32 -07:00
}
2020-08-28 04:03:12 -07:00
//
// Check for deallocation
//
if (g_IsNewRequestForDeAllocation)
2020-04-10 00:57:32 -07:00
{
PLIST_ENTRY Link = g_ListOfAllocatedPoolsHead.Flink;
2020-04-11 08:40:02 -07:00
while (Link != &g_ListOfAllocatedPoolsHead)
2020-08-28 04:03:12 -07:00
{
PLIST_ENTRY Next = Link->Flink;
2020-08-28 04:03:12 -07:00
//
// Get the head of the record
//
PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(Link, POOL_TABLE, PoolsList);
2020-08-28 04:03:12 -07:00
//
2024-03-17 18:17:38 +09:00
// Check whether this pool should be freed or not and
2020-08-28 04:03:12 -07:00
// also check whether it's already freed or not
//
if (PoolTable->ShouldBeFreed && !PoolTable->AlreadyFreed)
{
//
// Set the flag to indicate that we freed
//
PoolTable->AlreadyFreed = TRUE;
//
// This item should be freed
//
PlatformMemFreePool((PVOID)PoolTable->Address);
2020-08-28 04:03:12 -07:00
//
// Now we should remove the entry from the g_ListOfAllocatedPoolsHead
//
RemoveEntryList(Link);
2020-08-28 04:03:12 -07:00
//
// Free the structure pool
//
PlatformMemFreePool(PoolTable);
2020-08-28 04:03:12 -07:00
}
Link = Next;
2020-08-28 04:03:12 -07:00
}
2020-04-10 00:57:32 -07:00
}
2020-08-28 04:03:12 -07:00
//
2024-03-17 18:17:38 +09:00
// All allocation and deallocation are performed
2020-08-28 04:03:12 -07:00
//
g_IsNewRequestForDeAllocation = FALSE;
g_IsNewRequestForAllocationReceived = FALSE;
2020-04-10 00:57:32 -07:00
SpinlockUnlock(&LockForReadingPool);
2020-04-10 00:57:32 -07:00
return Result;
2020-03-24 06:35:02 -07:00
}
2020-04-11 06:52:24 -07:00
/**
* @brief Request to allocate new buffers
*
* @param Size Request new buffer to allocate
2020-04-11 06:52:24 -07:00
* @param Count Count of chunks
* @param Intention The intention of chunks (buffer tag)
* @return BOOLEAN If the request is save it returns true otherwise it returns false
*/
2020-04-10 00:57:32 -07:00
BOOLEAN
PoolManagerRequestAllocation(SIZE_T Size, UINT32 Count, POOL_ALLOCATION_INTENTION Intention)
2020-03-24 06:35:02 -07:00
{
2020-08-28 04:03:12 -07:00
BOOLEAN FoundAPlace = FALSE;
2020-04-11 06:52:24 -07:00
//
// ******** We check to find a free place to store ********
//
2020-09-13 13:11:35 -07:00
2020-04-10 00:57:32 -07:00
SpinlockLock(&LockForRequestAllocation);
for (SIZE_T i = 0; i < MaximumRequestsQueueDepth; i++)
2020-04-10 00:57:32 -07:00
{
REQUEST_NEW_ALLOCATION * CurrentItem = &g_RequestNewAllocation[i];
if (CurrentItem->Size == 0)
2020-08-28 04:03:12 -07:00
{
CurrentItem->Count = Count;
CurrentItem->Intention = Intention;
CurrentItem->Size = Size;
2020-08-28 04:03:12 -07:00
FoundAPlace = TRUE;
break;
}
2020-04-10 00:57:32 -07:00
}
2020-08-28 04:03:12 -07:00
if (!FoundAPlace)
2020-04-10 00:57:32 -07:00
{
SpinlockUnlock(&LockForRequestAllocation);
return FALSE;
}
2020-04-11 06:52:24 -07:00
//
2020-04-10 00:57:32 -07:00
// Signals to show that we have new allocations
2020-04-11 06:52:24 -07:00
//
g_IsNewRequestForAllocationReceived = TRUE;
2020-04-10 00:57:32 -07:00
SpinlockUnlock(&LockForRequestAllocation);
return TRUE;
2020-04-10 01:32:59 -07:00
}