Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1672 lines (1273 sloc) 39.8 KB
////////////////////////////////////////////////////////////////////////////////
// -------------------------------------------------------------------------- //
// //
// (C) 2010-2016 Robot Developers //
// See LICENSE for licensing info //
// //
// -------------------------------------------------------------------------- //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------//
// Prefaces //
//----------------------------------------------------------------------------//
#include "Process.h"
#include "Memory.h"
#include <assert.h>
#include <algorithm>
using std::search;
using std::vector;
#include <unordered_map>
using std::unordered_map;
#ifdef ROBOT_OS_LINUX
#include <string>
using std::string;
using std::sort;
#include <cstring>
#include <fstream>
using std::ios;
using std::ifstream;
using std:: fstream;
using std:: getline;
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
// Path to proc directory
#define PROC_PATH "/proc/"
#endif
#ifdef ROBOT_OS_MAC
#include <unistd.h>
#include <mach/mach_vm.h>
// Handle shortcut
#define ProcHandle \
((task_t) mData->Proc.GetHandle())
#endif
#ifdef ROBOT_OS_WIN
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// Handle shortcut
#define ProcHandle \
((HANDLE) mData->Proc.GetHandle())
#endif
ROBOT_NS_BEGIN
//----------------------------------------------------------------------------//
// Macros //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
#define SET_A_FLAG( i, aID, aV ) \
if (accessFlags[i] == '\0') \
goto skip; \
\
if (accessFlags[i] != ' ' && \
accessFlags[i] != '-' && \
accessFlags[i] != aID && \
accessFlags[i] !=(aID|32)) \
return result; \
\
aV =accessFlags[i] | 32;
////////////////////////////////////////////////////////////////////////////////
#define HEX_TO_INT( hiVal, loVal ) \
if (high) \
{ \
byte.Data = hiVal; \
byte.Wild = false; \
} \
\
else \
{ \
byte.Data += loVal; \
compiled.push_back (byte); \
} \
\
high = !high; break;
//----------------------------------------------------------------------------//
// Locals //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
static uintptr gMinVM = 0; // Minimum VM address
static uintptr gMaxVM_32 = 0; // Maximum VM address (32-Bit)
static uintptr gMaxVM_64 = 0; // Maximum VM address (64-Bit)
static uintptr gPageSize = 0; // Size of single page
#ifdef ROBOT_OS_LINUX
////////////////////////////////////////////////////////////////////////////////
struct Mapping
{
uintptr Start; // Start address of region
uintptr Stop; // Stop address of region
char Access[5]; // Region protection value
};
#endif
////////////////////////////////////////////////////////////////////////////////
struct PatternByte
{
uint8 Data; // Single byte of data
bool Wild; // If data is wildcard
};
////////////////////////////////////////////////////////////////////////////////
static bool SearchWild (uint8 c, const PatternByte& b)
{
// Check for wildcard values
return b.Wild || c == b.Data;
}
////////////////////////////////////////////////////////////////////////////////
static bool SearchNorm (uint8 c, const PatternByte& b)
{
// Ignore wildcards
return c == b.Data;
}
//----------------------------------------------------------------------------//
// Classes Memory //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
typedef unordered_map<uintptr, uintptr> PageMap;
////////////////////////////////////////////////////////////////////////////////
struct Memory::Data
{
Process Proc; // The process object
uint8* Cache; // Cached memory data
PageMap Pages; // Page pointers (R->C)
uintptr Next; // The next free page
uintptr BlockLength; // Single block size
uintptr BlockBuffer; // Maximum read size
uintptr CacheSize; // Current cache size
uintptr EnlargeSize; // Cache enlarge size
uintptr MaximumSize; // Maximum cache size
uint32 SystemReads; // Total system reads
uint32 CachedReads; // Total cached reads
uint32 SystemWrites; // Total system writes
uint32 AccessWrites; // Total access writes
uint32 ReadErrors; // Total read errors
uint32 WriteErrors; // Total write errors
int32 MemoryFile; // Linux proc mem file
};
//----------------------------------------------------------------------------//
// Operators Memory::Stats //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
bool Memory::Stats::operator == (const Stats& stats) const
{
return SystemReads == stats.SystemReads &&
CachedReads == stats.CachedReads &&
SystemWrites == stats.SystemWrites &&
AccessWrites == stats.AccessWrites &&
ReadErrors == stats. ReadErrors &&
WriteErrors == stats.WriteErrors;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Stats::operator != (const Stats& stats) const
{
return SystemReads != stats.SystemReads ||
CachedReads != stats.CachedReads ||
SystemWrites != stats.SystemWrites ||
AccessWrites != stats.AccessWrites ||
ReadErrors != stats. ReadErrors ||
WriteErrors != stats.WriteErrors;
}
//----------------------------------------------------------------------------//
// Functions Memory::Region //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::Contains (uintptr address) const
{
return Start <= address && address < Stop;
}
//----------------------------------------------------------------------------//
// Operators Memory::Region //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator < (uintptr address) const
{
return Start < address;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator > (uintptr address) const
{
return Start > address;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator <= (uintptr address) const
{
return Start <= address;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator >= (uintptr address) const
{
return Start >= address;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator < (const Region& region) const
{
return Start < region.Start;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator > (const Region& region) const
{
return Start > region.Start;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator <= (const Region& region) const
{
return Start <= region.Start;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator >= (const Region& region) const
{
return Start >= region.Start;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator == (const Region& region) const
{
return Valid == region.Valid &&
Bound == region.Bound &&
Start == region.Start &&
Stop == region.Stop &&
Size == region.Size &&
Readable == region.Readable &&
Writable == region.Writable &&
Executable == region.Executable &&
Access == region.Access &&
Private == region.Private &&
Guarded == region.Guarded;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::Region::operator != (const Region& region) const
{
return Valid != region.Valid ||
Bound != region.Bound ||
Start != region.Start ||
Stop != region.Stop ||
Size != region.Size ||
Readable != region.Readable ||
Writable != region.Writable ||
Executable != region.Executable ||
Access != region.Access ||
Private != region.Private ||
Guarded != region.Guarded;
}
//----------------------------------------------------------------------------//
// Constructors Memory //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
Memory::Memory (const Process& p)
: mData (new Memory::Data(),
[] (Memory::Data* data)
{
// Delete the memory cache
if (data->Cache != nullptr)
delete[] data->Cache;
#ifdef ROBOT_OS_LINUX
// Close memory file if open
if (data->MemoryFile != -1)
close (data->MemoryFile);
#endif
// Free data
delete data;
})
{
mData->Proc = p;
mData->Cache = nullptr;
mData->Next = 0;
mData->BlockLength = 0;
mData->BlockBuffer = 0;
mData-> CacheSize = 0;
mData->EnlargeSize = 0;
mData->MaximumSize = 0;
mData->SystemReads = 0;
mData->CachedReads = 0;
mData->SystemWrites = 0;
mData->AccessWrites = 0;
mData-> ReadErrors = 0;
mData->WriteErrors = 0;
mData->MemoryFile = -1;
#ifdef ROBOT_OS_LINUX
char mem[32];
// Build a path to mem file
snprintf (mem, 32, PROC_PATH
"%d/mem", p.GetPID());
// Attempt to open the proc memory file
mData->MemoryFile = open (mem, O_RDWR);
#endif
}
//----------------------------------------------------------------------------//
// Functions Memory //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
bool Memory::IsValid (void) const
{
#ifdef ROBOT_OS_LINUX
// Verify that process is valid
return mData->Proc.IsValid();
#endif
#ifdef ROBOT_OS_MAC
// Verify that process is valid
// and a mach task is available
return mData->Proc.IsValid () &&
mData->Proc.GetHandle() != 0;
#endif
#ifdef ROBOT_OS_WIN
// Verify that process is valid
return mData->Proc.IsValid();
#endif
}
////////////////////////////////////////////////////////////////////////////////
Process Memory::GetProcess (void) const
{
return mData->Proc;
}
////////////////////////////////////////////////////////////////////////////////
Memory::Stats Memory::GetStats (void) const
{
Memory::Stats s;
s.SystemReads = mData->SystemReads;
s.CachedReads = mData->CachedReads;
s.SystemWrites = mData->SystemWrites;
s.AccessWrites = mData->AccessWrites;
s. ReadErrors = mData-> ReadErrors;
s.WriteErrors = mData->WriteErrors;
return s;
}
////////////////////////////////////////////////////////////////////////////////
Memory::Stats Memory::GetStats (bool reset)
{
Memory::Stats s;
s.SystemReads = mData->SystemReads;
s.CachedReads = mData->CachedReads;
s.SystemWrites = mData->SystemWrites;
s.AccessWrites = mData->AccessWrites;
s. ReadErrors = mData-> ReadErrors;
s.WriteErrors = mData->WriteErrors;
if (reset)
{
mData->SystemReads = 0;
mData->CachedReads = 0;
mData->SystemWrites = 0;
mData->AccessWrites = 0;
mData-> ReadErrors = 0;
mData->WriteErrors = 0;
}
return s;
}
////////////////////////////////////////////////////////////////////////////////
Memory::Region Memory::GetRegion (uintptr address) const
{
Region region = { 0 };
region.Valid = false;
// Check whether address is valid
if (address < GetMinAddress() ||
address >= GetMaxAddress() ||
!IsValid()) return region;
// Align address to page boundary
address &= ~(GetPageSize() - 1);
#ifdef ROBOT_OS_LINUX
RegionList list =
// Get the region at the address
GetRegions (address, address + 1);
// If list isn't empty, return front
return list.empty() ? region : list[0];
#endif
#ifdef ROBOT_OS_MAC
mach_port_t port = MACH_PORT_NULL;
mach_msg_type_number_t count =
VM_REGION_BASIC_INFO_COUNT_64;
uint64 base = address, size;
vm_region_basic_info_64 info;
vm_region_flavor_t flavor =
VM_REGION_BASIC_INFO_64;
// Query region info using specified address
if (mach_vm_region (ProcHandle, &base, &size,
flavor, (vm_region_info_t) &info, &count,
&port)) return region;
// Determine the start and stop
uintptr start = (uintptr) base;
uintptr stop = (uintptr) base +
(uintptr) size;
// Avoid returning invalid memory regions
if (stop > GetMaxAddress()) return region;
region.Start = address;
// Check if address is within the result
if (start <= address && address < stop)
{
region.Bound = true;
region.Stop = stop;
region.Size = stop - address;
// Determine the access of the region
region.Access = (info.protection);
region.Executable = (info.protection & VM_PROT_EXECUTE) != 0;
region.Readable = (info.protection & VM_PROT_READ ) != 0;
region.Writable = (info.protection & VM_PROT_WRITE ) != 0;
// Is the region private or shared
region.Private = info.shared == 0;
}
else
{
// Region is unbound
region.Stop = start;
region.Size = start - address;
}
assert (region.Start % GetPageSize() == 0);
assert (region.Stop % GetPageSize() == 0);
assert (region.Size % GetPageSize() == 0);
assert (region.Size != 0);
region.Valid = true; return region;
#endif
#ifdef ROBOT_OS_WIN
MEMORY_BASIC_INFORMATION mbInfo;
// Determine region info using specified address
if (VirtualQueryEx (ProcHandle, (LPCVOID) address,
&mbInfo, sizeof (mbInfo)) != sizeof (mbInfo))
return region;
// Ignore invalid memory regions
if ((uintptr) mbInfo.BaseAddress +
(uintptr) mbInfo.RegionSize >
GetMaxAddress()) return region;
// Set the size and base address of region
region.Start = (uintptr) mbInfo.BaseAddress;
region.Stop = (uintptr) mbInfo.BaseAddress +
(uintptr) mbInfo.RegionSize;
region.Size = (uintptr) mbInfo.RegionSize;
assert (region.Start % GetPageSize() == 0);
assert (region.Stop % GetPageSize() == 0);
assert (region.Size % GetPageSize() == 0);
assert (region.Size != 0);
region.Access = PAGE_NOACCESS;
// Check if storage is assigned
if (mbInfo.State == MEM_COMMIT)
{
region.Bound = true;
// Define constants encompassing various access rights
static const DWORD R = PAGE_READONLY | PAGE_READWRITE |
PAGE_WRITECOPY | PAGE_EXECUTE | PAGE_EXECUTE_READ |
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
static const DWORD W = PAGE_READWRITE | PAGE_WRITECOPY |
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
static const DWORD X = PAGE_EXECUTE | PAGE_EXECUTE_READ |
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
region.Readable = (mbInfo.Protect & R) != 0;
region.Writable = (mbInfo.Protect & W) != 0;
region.Executable = (mbInfo.Protect & X) != 0;
region.Access = mbInfo.Protect;
region.Private = (mbInfo.Type & MEM_PRIVATE) != 0;
region.Guarded = (mbInfo.Protect & PAGE_GUARD ) != 0;
}
region.Valid = true; return region;
#endif
}
////////////////////////////////////////////////////////////////////////////////
Memory::RegionList Memory::GetRegions
(uintptr start, uintptr stop) const
{
RegionList result;
// Verify the memory validity
if (!IsValid()) return result;
// Ensure start is in range
if (start < GetMinAddress())
start = GetMinAddress();
// Ensure stop is in range
if (stop > GetMaxAddress())
stop = GetMaxAddress();
uintptr oldStop = stop;
// Align range to the page size
start &= ~(GetPageSize() - 1);
stop &= ~(GetPageSize() - 1);
if (oldStop != stop)
stop += GetPageSize();
// Make sure the range is correct
if (start >= stop) return result;
#ifdef ROBOT_OS_LINUX
char maps[32];
// Build path to proc PID maps
snprintf (maps, 32, PROC_PATH
"%d/maps", mData->Proc.GetPID());
// Attempt to open maps file
fstream file (maps, ios::in);
if (!file) return result;
Mapping mapping; string line;
std::vector<Mapping> mappings;
// Create the mappings list
while (getline (file, line))
{
size_t begin, end;
// Parse current line into single mapping entry
if (sscanf (line.data(), "%zx-%zx %4s", &begin,
&end, mapping.Access) != 3) return result;
// Ignore any invalid memory regions
if (end > GetMaxAddress()) continue;
// Avoid GCC warning
mapping.Start = begin;
mapping.Stop = end;
mappings.push_back (mapping);
}
// Sort the mappings based on their address
std::sort (mappings.begin(), mappings.end(),
[](const Mapping& a, const Mapping& b)
-> bool { return a.Start < b.Start; });
Region region = { 0 };
region.Valid = true;
region.Start = start;
// Iterate through every mapping in the list
for (uintptr i = 0; i < mappings.size(); ++i)
{
// Avoid dereferencing each iteration
const Mapping& current = mappings[i];
// Position at the start address
if (region.Start >= current.Stop)
continue;
// Check if region is unbounded
if (region.Start < current.Start)
{
region.Bound = false;
region.Stop = current.Start;
region.Size = current.Start - region.Start;
region.Readable = false;
region.Writable = false;
region.Executable = false;
region.Access = PROT_NONE;
region.Private = false;
result.push_back (region);
assert (region.Start % GetPageSize() == 0);
assert (region.Stop % GetPageSize() == 0);
assert (region.Size % GetPageSize() == 0);
assert (region.Size != 0);
// Increase the base address
region.Start = current.Start;
if (region.Start >= stop) break;
}
region.Bound = true;
region.Stop = current.Stop;
region.Size = current.Stop - region.Start;
region.Readable = current.Access[0] == 'r';
region.Writable = current.Access[1] == 'w';
region.Executable = current.Access[2] == 'x';
region.Private = current.Access[3] == 'p';
region.Access = PROT_NONE;
if (region.Executable) region.Access |= PROT_EXEC;
if (region.Readable ) region.Access |= PROT_READ;
if (region.Writable ) region.Access |= PROT_WRITE;
result.push_back (region);
assert (region.Start % GetPageSize() == 0);
assert (region.Stop % GetPageSize() == 0);
assert (region.Size % GetPageSize() == 0);
assert (region.Size != 0);
// Increase the base address
region.Start = current.Stop;
if (region.Start >= stop) break;
}
return result;
#endif
#if defined (ROBOT_OS_MAC) || \
defined (ROBOT_OS_WIN)
Region region;
// Read memory, region by region
for (uintptr addr = start; addr
< stop; addr = region.Stop)
{
// Get region info and verify validity
if (!(region = GetRegion (addr)).Valid)
break;
// Append current region
result.push_back (region);
}
return result;
#endif
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::SetAccess (const Region& region, uint32 access)
{
// Verify the parameters and status
if (!region.Valid || !region.Bound ||
IsValid() == false) return false;
#ifdef ROBOT_OS_LINUX
// NOTE: Not able to be implemented safely
return false;
#endif
#ifdef ROBOT_OS_MAC
// Modify access value of the specified region
if (mach_vm_protect (ProcHandle, region.Start,
region.Size, 0, access)) return false;
// Update stats value
++mData->AccessWrites;
return true;
#endif
#ifdef ROBOT_OS_WIN
DWORD old = 0;
// Modify access value of specified region
if (!VirtualProtectEx (ProcHandle, (LPVOID)
region.Start, region.Size, access, &old))
return false;
// Update stats value
++mData->AccessWrites;
return true;
#endif
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::SetAccess (const Region& region,
bool read, bool write, bool exec)
{
#ifdef ROBOT_OS_LINUX
uint32 access = PROT_NONE;
// Compute new protection value
if (exec ) access |= PROT_EXEC;
if (read ) access |= PROT_READ;
if (write) access |= PROT_WRITE;
return SetAccess (region, access);
#endif
#ifdef ROBOT_OS_MAC
uint32 access = VM_PROT_NONE;
// Compute the new protection value
if (exec ) access |= VM_PROT_EXECUTE;
if (read ) access |= VM_PROT_READ;
if (write) access |= VM_PROT_WRITE;
return SetAccess (region, access);
#endif
#ifdef ROBOT_OS_WIN
uint32 access;
if (exec)
{
if (write)
access = PAGE_EXECUTE_READWRITE;
else
{
if (read)
access = PAGE_EXECUTE_READ;
else
access = PAGE_EXECUTE;
}
}
else
{
if (write)
access = PAGE_READWRITE;
else
{
if (read)
access = PAGE_READONLY;
else
access = PAGE_NOACCESS;
}
}
return SetAccess (region, access);
#endif
}
////////////////////////////////////////////////////////////////////////////////
AddressList Memory::Find (const char* pattern, uintptr start,
uintptr stop, uintptr limit, const char* accessFlags)
{
AddressList result;
// Check the parameters and the status
if (pattern == nullptr || !IsValid())
return result;
char aw = ' '; char ax = ' ';
char ap = ' '; char ag = ' ';
// Check if flags are valid
if (accessFlags != nullptr)
{
SET_A_FLAG (0, 'W', aw);
SET_A_FLAG (1, 'X', ax);
SET_A_FLAG (2, 'P', ap);
SET_A_FLAG (3, 'G', ag);
skip:
assert (aw == ' ' || aw == '-' || aw == 'w');
assert (ax == ' ' || ax == '-' || ax == 'x');
assert (ap == ' ' || ap == '-' || ap == 'p');
assert (ag == ' ' || ag == '-' || ag == 'g');
}
bool wild = false;
bool high = true;
PatternByte byte;
vector<PatternByte> compiled;
// Compile the pattern into an array format
for (uintptr i = 0; pattern[i] != '\0'; ++i)
{
switch (pattern[i])
{
case '0': HEX_TO_INT ( 0, 0);
case '1': HEX_TO_INT ( 16, 1);
case '2': HEX_TO_INT ( 32, 2);
case '3': HEX_TO_INT ( 48, 3);
case '4': HEX_TO_INT ( 64, 4);
case '5': HEX_TO_INT ( 80, 5);
case '6': HEX_TO_INT ( 96, 6);
case '7': HEX_TO_INT (112, 7);
case '8': HEX_TO_INT (128, 8);
case '9': HEX_TO_INT (144, 9);
case 'A': HEX_TO_INT (160, 10);
case 'a': HEX_TO_INT (160, 10);
case 'B': HEX_TO_INT (176, 11);
case 'b': HEX_TO_INT (176, 11);
case 'C': HEX_TO_INT (192, 12);
case 'c': HEX_TO_INT (192, 12);
case 'D': HEX_TO_INT (208, 13);
case 'd': HEX_TO_INT (208, 13);
case 'E': HEX_TO_INT (224, 14);
case 'e': HEX_TO_INT (224, 14);
case 'F': HEX_TO_INT (240, 15);
case 'f': HEX_TO_INT (240, 15);
case ' ':
if (!high) return result;
break;
case '?':
if (!high) return result;
byte.Wild = wild = true;
compiled.push_back (byte);
break;
default:
return result;
}
}
// Do the last compiler checks
if (!high || compiled.empty())
return result;
RegionList list =
// Get a list of regions
GetRegions (start, stop);
// Check for at least one region
if (list.empty()) return result;
uintptr size = 1048576;
// Initially allocate single MB
uint8* data = new uint8[size];
// Loop through memory region list
for (uintptr a = start, i = 0; i <
list.size(); a = list[i++].Stop)
{
// Avoid dereferencing each time
const Region& current = list[i];
#ifndef NDEBUG
// Paranoid bounds check
if (current.Stop >= stop)
assert (i+1 == list.size());
#endif
// Skip if region is unreadable
if (!current.Readable) continue;
// Don't check if defaults
if (accessFlags != nullptr)
{
// Filter regions not matching the access flags
if (aw == '-' && current.Writable ) continue;
if (aw == 'w' && !current.Writable ) continue;
if (ax == '-' && current.Executable) continue;
if (ax == 'x' && !current.Executable) continue;
if (ap == '-' && current.Private ) continue;
if (ap == 'p' && !current.Private ) continue;
if (ag == '-' && current.Guarded ) continue;
if (ag == 'g' && !current.Guarded ) continue;
}
// Compute the current value of length parameter
uintptr len = std::min (current.Stop, stop) - a;
if (size < len)
{
// Multiply by two until
// the length is reached
while ((size *= 2) < len);
delete[] data;
// Reallocate the buffer
data = new uint8[size];
}
// Attempt to read the memory region
if (ReadData (a, data, len) != len)
continue;
// Compute the range
uint8* begin = data;
uint8* end = data + len;
while (true)
{
// Search for the pattern in memory
uint8* res = std::search (begin, end,
compiled.begin(), compiled.end(),
wild ? SearchWild : SearchNorm);
// We're done searching
if (res >= end) break;
// Rebase the result from search
result.push_back (res - data + a);
if (limit != 0 &&
// Limit has been hit
limit <= result.size())
{
delete[] data;
return result;
}
// Progress the start pointer
begin = res + compiled.size();
// We're done searching
if (begin >= end) break;
}
}
delete[] data;
return result;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::CreateCache (uintptr blockLength,
uintptr blockBuffer, uintptr initialSize,
uintptr enlargeSize, uintptr maximumSize)
{
// Check for any zeroes
if (blockLength == 0 ||
blockBuffer == 0 ||
initialSize == 0)
return false;
// Retrieve the system page size
uintptr pageSize = GetPageSize();
if (pageSize == 0) return false;
// Ensure alignment to page size
if (blockLength % pageSize != 0 ||
blockBuffer % pageSize != 0 ||
initialSize % pageSize != 0 ||
enlargeSize % pageSize != 0 ||
maximumSize % pageSize != 0)
return false;
// Avoid any unoptimized alignment
if (blockLength & (blockLength-1))
return false;
// Ensure block can store data
if (blockLength < blockBuffer)
return false;
// Validate the initial size
if (initialSize < blockLength +
blockBuffer) return false;
// Validate enlarge size
if (enlargeSize != 0 &&
enlargeSize < blockLength +
blockBuffer) return false;
// Validate maximum size
if (maximumSize != 0 &&
maximumSize < initialSize)
return false;
// Set the memory cache properties
mData->BlockLength = blockLength;
mData->BlockBuffer = blockBuffer;
mData-> CacheSize = initialSize;
mData->EnlargeSize = enlargeSize;
mData->MaximumSize = maximumSize;
mData->Next = 0;
// Clear cached pages
mData->Pages.clear();
// Clean up the memory cache
if (mData->Cache != nullptr)
delete[] mData->Cache;
mData->Cache =
// Create memory cache
new uint8[initialSize];
return true;
}
////////////////////////////////////////////////////////////////////////////////
void Memory::ClearCache (void)
{
mData->Next = 0;
// Clear cached pages
mData->Pages.clear();
}
////////////////////////////////////////////////////////////////////////////////
void Memory::DeleteCache (void)
{
// Reset memory cache
mData->BlockLength = 0;
mData->BlockBuffer = 0;
mData-> CacheSize = 0;
mData->EnlargeSize = 0;
mData->MaximumSize = 0;
mData->Next = 0;
// Clear cached pages
mData->Pages.clear();
// Clean up the memory cache
if (mData->Cache != nullptr)
{
delete[] mData->Cache;
mData->Cache = nullptr;
}
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::IsCaching (void) const
{
// Return the state of the cache
return mData->Cache != nullptr;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::GetCacheSize (void) const
{
// Return the cache size
return mData->CacheSize;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::GetPtrSize (void) const
{
return mData->Proc.Is64Bit() ? 8 : 4;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::GetMinAddress (void) const
{
InitializeVM();
return gMinVM;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::GetMaxAddress (void) const
{
InitializeVM();
// Depends on architecture
return mData->Proc.Is64Bit() ?
gMaxVM_64 : gMaxVM_32;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::GetPageSize (void) const
{
InitializeVM();
return gPageSize;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::ReadData (uintptr address,
void* result, uintptr length, Flags f)
{
// Check the parameter values and status
if (!result || length == 0 || !IsValid())
return 0;
// Check if the address is within bounds
if (GetMaxAddress() < address + length ||
GetMinAddress() > address) return 0;
// Check if caching can be used
if (length > mData->BlockBuffer)
return MemRead (address, result, length, f);
const uintptr pageSize =
// Calculate size of single cached page
mData->BlockLength + mData->BlockBuffer;
const uintptr aligned =
// Compute alignment to block length
address & ~(mData->BlockLength - 1);
// Check if new address is within bounds
if (GetMaxAddress() < aligned + pageSize ||
GetMinAddress() > aligned) return 0;
// Check if the memory has already been cached before
if (mData->Pages.find (aligned) == mData->Pages.end())
{
// Increase size of the cache if out of space
if (mData->CacheSize - mData->Next < pageSize)
{
if (mData->EnlargeSize == 0)
// We can't enlarge the cache any further
return MemRead (address, result, length, f);
// Compute the new size of the cache
uintptr newSize = mData->CacheSize +
mData->EnlargeSize;
if (mData->MaximumSize != 0 &&
mData->MaximumSize < newSize)
// We have reached the maximum cache size
return MemRead (address, result, length, f);
// Allocate a cache of the new size
uint8* newData = new uint8[newSize];
// Copy existing cache data and free memory
memcpy (newData, mData->Cache, mData->Next);
delete[] mData->Cache;
// Set new size and cache
mData->CacheSize = newSize;
mData->Cache = newData;
}
// Read data into next available cache position
if (MemRead (aligned, mData->Cache + mData->Next,
pageSize, f) != pageSize)
{
// Perform a system call in case of an error
return MemRead (address, result, length, f);
}
// Store the location of this page
mData->Pages[aligned] = mData->Next;
// Update next free page
mData->Next += pageSize;
}
// Update statistics
++mData->CachedReads;
uintptr offset =
// Find the memory offset in cached memory
mData->Pages[aligned] + address - aligned;
// Read the cached memory and copy the result
memcpy (result, mData->Cache + offset, length);
return length;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::WriteData (uintptr address,
const void* data, uintptr length, Flags f)
{
// Check the parameter values and status
if (!data || length == 0 || !IsValid())
return 0;
// Check if the address is within bounds
if (GetMaxAddress() < address + length ||
GetMinAddress() > address) return 0;
// Nothing much to do, perform system call
return MemWrite (address, data, length, f);
}
//----------------------------------------------------------------------------//
// Internal Memory //
//----------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::MemRead (uintptr address,
void* result, uintptr length, Flags f)
{
assert (address != 0);
assert (result != 0);
assert (length != 0);
uintptr bytes = 0;
// Use default system call
if (f == Memory::Default)
{
SysRead (address, result, length, &bytes);
return bytes;
}
// Compute the stop address value
uintptr stop = address + length;
RegionList list =
// Get the list of regions
GetRegions (address, stop);
// Read the memory region by region
for (uintptr a = address, i = 0; i <
list.size(); a = list[i++].Stop)
{
// Avoid dereferencing each time
const Region& current = list[i];
#ifndef NDEBUG
// Paranoid bounds check
if (current.Stop >= stop)
assert (i+1 == list.size());
#endif
// Memory region access status
bool access = current.Readable;
// Compute result offset
uint8* offset = (uint8*)
result + a - address;
// Compute the current value of length parameter
uintptr len = std::min (current.Stop, stop) - a;
// Check whether the region can be made readable
if (!current.Readable && f == Memory::AutoAccess)
access = SetAccess (current, true, false, false);
// Attempt to read memory region or fill with zero
if (!access || !SysRead (a, offset, len, nullptr))
memset (offset, 0, len);
// Check whether region access should be restored
if (!current.Readable && f == Memory::AutoAccess)
if (access) SetAccess (current, current.Access);
bytes += len; // Update total bytes currently read
}
return bytes;
}
////////////////////////////////////////////////////////////////////////////////
uintptr Memory::MemWrite (uintptr address,
const void* data, uintptr length, Flags f)
{
assert (address != 0);
assert ( data != 0);
assert (length != 0);
uintptr bytes = 0;
// Use default system call
if (f == Memory::Default)
{
SysWrite (address, data, length, &bytes);
return bytes;
}
// Compute the stop address value
uintptr stop = address + length;
RegionList list =
// Get the list of regions
GetRegions (address, stop);
// Read the memory region by region
for (uintptr a = address, i = 0; i <
list.size(); a = list[i++].Stop)
{
// Avoid dereferencing each time
const Region& current = list[i];
#ifndef NDEBUG
// Paranoid bounds check
if (current.Stop >= stop)
assert (i+1 == list.size());
#endif
// Memory region access status
bool access = current.Writable;
// Compute result offset
uint8* offset = (uint8*)
data + a - address;
// Compute the current value of length parameter
uintptr len = std::min (current.Stop, stop) - a;
// Check whether the region can be made writable
if (!current.Writable && f == Memory::AutoAccess)
access = SetAccess (current, true, true, current.Executable);
// Attempt to write to the current memory region
if (access) SysWrite (a, offset, len, nullptr);
// Check whether region access should be restored
if (!current.Writable && f == Memory::AutoAccess)
if (access) SetAccess (current, current.Access);
bytes += len; // Update total bytes written
}
return bytes;
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::SysRead (uintptr address, void*
result, uintptr length, uintptr* bytes)
{
assert (address != 0);
assert (result != 0);
assert (length != 0);
// Update statistics
++mData->SystemReads;
#ifdef ROBOT_OS_LINUX
// NOTE: We assume that the Linux Kernel
// allows us to read and write to the mem
// file. This is not the case across some
// distributions which means we will, at
// some point, have to implement a ptrace
// version. Perhaps if we add a debugger.
// Check file and attempt seek
if (mData->MemoryFile == -1 ||
lseek (mData->MemoryFile,
address, SEEK_SET) == -1)
{
// Attempt to reset the bytes read
if (bytes != nullptr) *bytes = 0;
++mData->ReadErrors; return false;
}
ssize_t count = read
// Attempt to read from the mem file
(mData->MemoryFile, result, length);
if (count == -1)
{
// Attempt to reset the bytes read
if (bytes != nullptr) *bytes = 0;
++mData->ReadErrors; return false;
}
// Attempt to set bytes read result
if (bytes != nullptr) *bytes = count;
return true;
#endif
#ifdef ROBOT_OS_MAC
mach_vm_size_t bytesRead = 0;
// Read memory at the specified address
if (mach_vm_read_overwrite (ProcHandle,
address, length, (mach_vm_address_t)
result, &bytesRead))
{
++mData->ReadErrors;
if (bytes != nullptr)
*bytes = bytesRead;
return false;
}
else
{
if (bytes != nullptr)
*bytes = bytesRead;
return true;
}
#endif
#ifdef ROBOT_OS_WIN
bool status = true; SIZE_T bytesRead = 0;
// Read memory at the specified address
if (!ReadProcessMemory (ProcHandle, (LPCVOID)
address, result, length, &bytesRead))
{ ++mData->ReadErrors; status = false; }
// Attempt to set the bytes read result
if (bytes != nullptr) *bytes = bytesRead;
return status;
#endif
}
////////////////////////////////////////////////////////////////////////////////
bool Memory::SysWrite (uintptr address, const
void* data, uintptr length, uintptr* bytes)
{
assert (address != 0);
assert ( data != 0);
assert (length != 0);
// Update statistics
++mData->SystemWrites;
#ifdef ROBOT_OS_LINUX
// NOTE: We assume that the Linux Kernel
// allows us to read and write to the mem
// file. This is not the case across some
// distributions which means we will, at
// some point, have to implement a ptrace
// version. Perhaps if we add a debugger.
// Check file and attempt seek
if (mData->MemoryFile == -1 ||
lseek (mData->MemoryFile,
address, SEEK_SET) == -1)
{
// Attempt to reset bytes written
if (bytes != nullptr) *bytes = 0;
++mData->WriteErrors; return false;
}
ssize_t count = write
// Attempt to write to the mem file
(mData->MemoryFile, data, length);
if (count == -1)
{
// Attempt to reset bytes written
if (bytes != nullptr) *bytes = 0;
++mData->WriteErrors; return false;
}
// Attempt to set bytes written result
if (bytes != nullptr) *bytes = count;
return true;
#endif
#ifdef ROBOT_OS_MAC
// Write memory at a specified address
if (mach_vm_write (ProcHandle, address,
(uintptr) data, (natural_t) length))
{
++mData->WriteErrors;
if (bytes != nullptr)
*bytes = 0;
return false;
}
else
{
if (bytes != nullptr)
*bytes = length;
return true;
}
#endif
#ifdef ROBOT_OS_WIN
bool status = true; SIZE_T bytesWritten = 0;
// Write memory at the specified address
if (!WriteProcessMemory (ProcHandle, (LPVOID)
address, data, length, &bytesWritten))
{ ++mData->WriteErrors; status = false; }
// Attempt to set the bytes written result
if (bytes != nullptr) *bytes = bytesWritten;
return status;
#endif
}
////////////////////////////////////////////////////////////////////////////////
void Memory::InitializeVM (void)
{
// Initialize system info once
static bool initialized = false;
if (initialized == false)
{
initialized = true;
#if defined (ROBOT_OS_MAC) || \
defined (ROBOT_OS_LINUX)
gMinVM = 0x000000001000;
gMaxVM_32 = 0x0000C0000000; // 3G
#ifdef ROBOT_ARCH_64
gMaxVM_64 = 0x7FFFFFFF0000;
#else
// This shouldn't be used
gMaxVM_64 = 0x0000C0000000;
#endif
#ifdef ROBOT_OS_LINUX
// Adjust minimum VM value
ifstream file (PROC_PATH
"sys/vm/mmap_min_addr");
if (file) file >> gMinVM;
#endif
gPageSize = sysconf (_SC_PAGESIZE);
#endif
#ifdef ROBOT_OS_WIN
SYSTEM_INFO info;
// Retrieve the system info
GetNativeSystemInfo (&info);
gMinVM = (uintptr)
info.lpMinimumApplicationAddress + 0;
gMaxVM_64 = (uintptr)
info.lpMaximumApplicationAddress + 1;
// Always use full low 32-Bit value
gMaxVM_32 = gMaxVM_64 & 0xFFFFFFFF;
gPageSize = (uintptr) info.dwPageSize;
#endif
assert ((gMinVM % gPageSize) == 0);
assert ((gMaxVM_32 % gPageSize) == 0);
assert ((gMaxVM_64 % gPageSize) == 0);
}
}
ROBOT_NS_END