#include "ProcessMemory.h"
#include "Process.h"
#include "../Misc/Trace.hpp"
namespace blackbone
{
ProcessMemory::ProcessMemory( Process* process )
: RemoteMemory( process )
, _process( process )
, _core( process->core() )
{
}
ProcessMemory::~ProcessMemory()
{
}
///
/// Allocate new memory block
///
/// Block size
/// Memory protection
/// Desired base address of new block
/// false if caller will be responsible for block deallocation
/// Memory block. If failed - returned block will be invalid
call_result_t ProcessMemory::Allocate( size_t size, DWORD protection /*= PAGE_EXECUTE_READWRITE*/, ptr_t desired /*= 0*/, bool own /*= true*/ )
{
return MemBlock::Allocate( *this, size, desired, protection, own );
}
///
/// Free memory
///
/// Memory address to release.
/// Region size
/// Release/decommit
/// Status
NTSTATUS ProcessMemory::Free( ptr_t pAddr, size_t size /*= 0*/, DWORD freeType /*= MEM_RELEASE*/ )
{
#ifdef _DEBUG
assert( freeType != MEM_RELEASE || size == 0 );
if (freeType == MEM_DECOMMIT) {
BLACKBONE_TRACE( L"Free: Decommit at address 0x%p (0x%x bytes)", static_cast(pAddr), size );
} else {
BLACKBONE_TRACE( L"Free: Free at address 0x%p", static_cast(pAddr) );
}
#endif
return _core.native()->VirtualFreeExT( pAddr, size, freeType );
}
///
/// Get memory region info
///
/// Memory address
/// Retrieved info
/// Status
NTSTATUS ProcessMemory::Query( ptr_t pAddr, PMEMORY_BASIC_INFORMATION64 pInfo )
{
return _core.native()->VirtualQueryExT( pAddr, pInfo );
}
///
/// Change memory protection
///
/// Memory address
/// Region size
/// New memory protection flags
/// Old protection flags
/// Status
NTSTATUS ProcessMemory::Protect( ptr_t pAddr, size_t size, DWORD flProtect, DWORD *pOld /*= NULL*/ )
{
DWORD junk = 0;
if (pOld == nullptr)
pOld = &junk;
return _core.native()->VirtualProtectExT( pAddr, size, CastProtection( flProtect, _core.DEP() ), pOld );
}
///
/// Read data
///
/// Memoey address to read from
/// Size of data to read
/// Output buffer
///
/// If true, function will try to read all committed pages in range ignoring uncommitted ones.
/// Otherwise function will fail if there is at least one non-committed page in region.
///
/// Status
NTSTATUS ProcessMemory::Read( ptr_t dwAddress, size_t dwSize, PVOID pResult, bool handleHoles /*= false*/ )
{
DWORD64 dwRead = 0;
if (dwAddress == 0)
return STATUS_INVALID_ADDRESS;
// Simple read
if (!handleHoles)
{
return _core.native()->ReadProcessMemoryT( dwAddress, pResult, dwSize, &dwRead );
}
// Read all committed memory regions
else
{
MEMORY_BASIC_INFORMATION64 mbi = { 0 };
for (ptr_t memptr = dwAddress; memptr < dwAddress + dwSize; memptr = mbi.BaseAddress + mbi.RegionSize)
{
if (_core.native()->VirtualQueryExT( memptr, &mbi ) != STATUS_SUCCESS)
continue;
// Filter empty regions
if (mbi.State != MEM_COMMIT || mbi.Protect == PAGE_NOACCESS)
continue;
uint64_t region_ptr = memptr - dwAddress;
auto status = _core.native()->ReadProcessMemoryT(
mbi.BaseAddress,
reinterpret_cast(pResult) + region_ptr,
static_cast(mbi.RegionSize),
&dwRead
);
if (!NT_SUCCESS( status ))
return status;
}
}
return STATUS_SUCCESS;
}
///
/// Read data
///
/// Base address + list of offsets
/// Size of data to read
/// Output buffer
///
/// If true, function will try to read all committed pages in range ignoring uncommitted ones.
/// Otherwise function will fail if there is at least one non-committed page in region.
///
/// Status
NTSTATUS ProcessMemory::Read( const std::vector& adrList, size_t dwSize, PVOID pResult, bool handleHoles /*= false */ )
{
if (adrList.empty())
return STATUS_INVALID_PARAMETER;
if(adrList.size() == 1)
return Read( adrList.front(), dwSize, pResult, handleHoles );
bool wow64 = _process->barrier().targetWow64;
ptr_t ptr = wow64 ? Read( adrList[0] ).result( 0 ) : Read( adrList[0] ).result( 0 );
for (size_t i = 1; i < adrList.size() - 1; i++)
ptr = wow64 ? Read( ptr + adrList[i] ).result( 0 ) : Read( ptr + adrList[i] ).result( 0 );
return Read( ptr + adrList.back(), dwSize, pResult, handleHoles );
}
///
/// Write data
///
/// Memory address to write to
/// Size of data to write
/// Buffer to write
/// Status
NTSTATUS ProcessMemory::Write( ptr_t pAddress, size_t dwSize, const void* pData )
{
return _core.native()->WriteProcessMemoryT( pAddress, pData, dwSize );
}
///
/// Write data
///
/// Base address + list of offsets
/// Size of data to write
/// Buffer to write
/// Status
NTSTATUS ProcessMemory::Write( const std::vector& adrList, size_t dwSize, const void* pData )
{
if (adrList.empty())
return STATUS_INVALID_PARAMETER;
if (adrList.size() == 1)
return Write( adrList.front(), dwSize, pData );
bool wow64 = _process->barrier().targetWow64;
ptr_t ptr = wow64 ? Read( adrList[0] ).result( 0 ) : Read( adrList[0] ).result( 0 );
for (size_t i = 1; i < adrList.size() - 1; i++)
ptr = wow64 ? Read( ptr + adrList[i] ).result( 0 ) : Read( ptr + adrList[i] ).result( 0 );
return Write( ptr + adrList.back(), dwSize, pData );
}
///
/// Enumerate valid memory regions
///
/// If true - non-allocated regions will be included in list
/// Found regions
std::vector ProcessMemory::EnumRegions( bool includeFree /*= false*/ )
{
return _core.native()->EnumRegions( includeFree );
}
}