#include "MemBlock.h" #include "ProcessMemory.h" #include "ProcessCore.h" #include "../Subsystem/NativeSubsystem.h" #include "../Misc/Trace.hpp" namespace blackbone { /// /// MemBlock ctor /// /// Process memory routines /// Memory address /// Block size /// Memory protection /// false if caller will be responsible for block deallocation MemBlock::MemBlock( ProcessMemory* mem, ptr_t ptr, size_t size, DWORD prot, bool own /*= true*/, bool physical /*= false*/ ) : _pImpl( new MemBlockImpl( mem, ptr, size, prot, own, physical ) ) { } /// /// MemBlock_p ctor /// /// Process memory routines /// Memory address /// Block size /// Memory protection /// false if caller will be responsible for block deallocation MemBlock::MemBlockImpl::MemBlockImpl( class ProcessMemory* mem, ptr_t ptr, size_t size, DWORD prot, bool own /*= true*/, bool physical /*= false */ ) : _ptr( ptr ) , _size( size ) , _protection( prot ) , _own( own ) , _physical( physical ) , _memory( mem ) { } /// /// MemBlock ctor /// /// Process memory routines /// Memory address /// false if caller will be responsible for block deallocation MemBlock::MemBlock( ProcessMemory* mem, ptr_t ptr, bool own /*= true*/ ) : _pImpl( new MemBlockImpl ) { _pImpl->_ptr = ptr; _pImpl->_own = own; _pImpl->_memory = mem; MEMORY_BASIC_INFORMATION64 mbi = { 0 }; mem->Query( _pImpl->_ptr, &mbi ); _pImpl->_protection = mbi.Protect; _pImpl->_size = (size_t)mbi.RegionSize; } /// /// Allocate new memory block /// /// Process memory routines /// Block size /// Desired base address of new block /// Memory protection /// false if caller will be responsible for block deallocation /// Memory block. If failed - returned block will be invalid call_result_t MemBlock::Allocate( ProcessMemory& process, size_t size, ptr_t desired /*= 0*/, DWORD protection /*= PAGE_EXECUTE_READWRITE */, bool own /*= true*/ ) { ptr_t desired64 = desired; DWORD newProt = CastProtection( protection, process.core().DEP() ); NTSTATUS status = process.core().native()->VirtualAllocExT( desired64, size, MEM_RESERVE | MEM_COMMIT, newProt ); if (!NT_SUCCESS( status )) { desired64 = 0; status = process.core().native()->VirtualAllocExT( desired64, size, MEM_COMMIT, newProt ); if (NT_SUCCESS( status )) return call_result_t( MemBlock( &process, desired64, size, protection, own ), STATUS_IMAGE_NOT_AT_BASE ); else return status; } #ifdef _DEBUG BLACKBONE_TRACE(L"Allocate: Allocating at address 0x%p (0x%X bytes)", static_cast(desired64), size); #endif return MemBlock( &process, desired64, size, protection, own ); } /// /// Reallocate existing block for new size /// /// New block size /// Desired base address of new block /// Memory protection /// New block address call_result_t MemBlock::Realloc( size_t size, ptr_t desired /*= 0*/, DWORD protection /*= PAGE_EXECUTE_READWRITE*/ ) { if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; ptr_t desired64 = desired; auto status = _pImpl->_memory->core().native()->VirtualAllocExT( desired64, size, MEM_COMMIT, protection ); if (!desired64) { desired64 = 0; status = _pImpl->_memory->core().native()->VirtualAllocExT( desired64, size, MEM_COMMIT, protection ); if (!NT_SUCCESS( status )) return status; status = STATUS_IMAGE_NOT_AT_BASE; } // Replace current instance if (desired64) { Free(); _pImpl->_ptr = desired64; _pImpl->_size = size; _pImpl->_protection = protection; } return call_result_t( desired64, status ); } /// /// Change memory protection /// /// New protection flags /// Memory offset in block /// Block size /// Old protection flags /// Status NTSTATUS MemBlock::Protect( DWORD protection, uintptr_t offset /*= 0*/, size_t size /*= 0*/, DWORD* pOld /*= nullptr */ ) { if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; auto prot = CastProtection( protection, _pImpl->_memory->core().DEP() ); if (size == 0) size = _pImpl->_size; return _pImpl->_physical ? Driver().ProtectMem( _pImpl->_memory->core().pid(), _pImpl->_ptr + offset, size, prot ) : _pImpl->_memory->Protect( _pImpl->_ptr + offset, size, prot, pOld ); } /// /// Free memory /// /// Size of memory chunk to free. If 0 - whole block is freed NTSTATUS MemBlock::Free( size_t size /*= 0*/ ) { if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; return _pImpl->Free(); } /// /// Free memory /// /// Size of memory chunk to free. If 0 - whole block is freed NTSTATUS MemBlock::MemBlockImpl::Free( size_t size /*= 0 */ ) { if (_ptr == 0) return STATUS_MEMORY_NOT_ALLOCATED; size = Align( size, 0x1000 ); NTSTATUS status = _physical ? Driver().FreeMem( _memory->core().pid(), _ptr, size, MEM_RELEASE ) : _memory->Free( _ptr, size, size == 0 ? MEM_RELEASE : MEM_DECOMMIT ); if (!NT_SUCCESS( status )) return LastNtStatus(); if (size == 0) { _ptr = 0; _size = 0; _protection = 0; } else { _ptr += size; _size -= size; } return STATUS_SUCCESS; } /// /// Read data /// /// Data offset in block /// Size of data to read /// Output buffer /// /// If true, function will try to read all committed pages in range ignoring uncommitted. /// Otherwise function will fail if there is at least one non-committed page in region. /// /// Status NTSTATUS MemBlock::Read( uintptr_t offset, size_t size, PVOID pResult, bool handleHoles /*= false*/ ) { if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; return _pImpl->_memory->Read( _pImpl->_ptr + offset, size, pResult, handleHoles ); } /// /// Write data /// /// Data offset in block /// Size of data to write /// Buffer to write /// Status NTSTATUS MemBlock::Write( uintptr_t offset, size_t size, const void* pData ) { if (!_pImpl) return STATUS_MEMORY_NOT_ALLOCATED; return _pImpl->_memory->Write( _pImpl->_ptr + offset, size, pData ); } /// /// Try to free memory and reset pointers /// void MemBlock::Reset() { _pImpl.reset(); } }