#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 ); } }