summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/VM.cpp
diff options
context:
space:
mode:
authorkat <kat@vector35.com>2024-10-23 21:56:29 -0400
committerkat <kat@vector35.com>2024-10-23 21:58:47 -0400
commit3006c68e108a18f9b8782bc937dc9ec7e2cb3b54 (patch)
treef589bb38909349142c09b6215244205aef0a57ac /view/sharedcache/core/VM.cpp
parent80fcccec8f686e0e5c89626b60af121a97baf856 (diff)
Initial commit of the alpha dyld_shared_cache view API Plugin.
This is an early release of our DSC processing plugin. We're still hard at work improving this feature. You should be able to just drop in a dyld_shared_cache and use the 'Shared Cache Triage' view to load and analyze images.
Diffstat (limited to 'view/sharedcache/core/VM.cpp')
-rw-r--r--view/sharedcache/core/VM.cpp793
1 files changed, 793 insertions, 0 deletions
diff --git a/view/sharedcache/core/VM.cpp b/view/sharedcache/core/VM.cpp
new file mode 100644
index 00000000..7fe01471
--- /dev/null
+++ b/view/sharedcache/core/VM.cpp
@@ -0,0 +1,793 @@
+//
+// Created by kat on 5/23/23.
+//
+
+/*
+ This is the cross-plat file buffering logic used for SharedCache processing.
+ This is used for reading large amounts of large files in a performant manner.
+
+ Here be no dragons, but this code is very complex, beware.
+
+ We in _all_ cases memory map the files, as we hardly ever need more than a few pages per file for most intensive operations.
+
+ Memory Map Implementation:
+ Of interest is that on several platforms we have to account for very low file pointer limits, and when mapping
+ 40+ files, these are trivially reachable.
+
+ We handle this with a "SelfAllocatingWeakPtr":
+ - Calling .lock() ALWAYS delivers a shared_ptr guaranteed to stay valid. This may block waiting for a free pointer
+ - As soon as that lock is released, that file pointer MAY be freed if another thread wants to open a new one, and we are at our limit.
+ - Calling .lock() again on this same theoretical object will then wait for another file pointer to be freeable.
+
+ VM Implementation:
+
+
+ Since the caches we're operating on are by nature page aligned, we are able to use nice optimizations under the hood to translate
+ "VM Addresses" to their actual in-memory counterparts.
+
+ We do this with a page table, which is a map of page -> file offset.
+
+ We also implement a "VMReader" here, which is a drop-in replacement for BinaryReader that operates on the VM.
+ see "ObjC.cpp" for where this is used.
+
+*/
+
+
+#include "VM.h"
+#include <utility>
+#include <memory>
+#include <cstring>
+#include <stdio.h>
+#include <binaryninjaapi.h>
+
+#ifdef _MSC_VER
+ #include <windows.h>
+#else
+ #include <sys/mman.h>
+ #include <fcntl.h>
+ #include <stdlib.h>
+ #include <sys/resource.h>
+#endif
+
+void VMShutdown()
+{
+ std::unique_lock<std::mutex> lock2(fileAccessorsMutex);
+ std::unique_lock<std::mutex> lock(fileAccessorDequeMutex);
+
+ // This will trigger the deallocation logic for these.
+ // It is background threaded to avoid a deadlock on exit.
+ fileAccessorReferenceHolder.clear();
+ fileAccessors.clear();
+}
+
+void MMAP::Map()
+{
+ if (mapped)
+ return;
+#ifdef _MSC_VER
+ LARGE_INTEGER fileSize;
+ if (!GetFileSizeEx(hFile, &fileSize))
+ {
+ // Handle error
+ CloseHandle(hFile);
+ return;
+ }
+ len = static_cast<size_t>(fileSize.QuadPart);
+
+ HANDLE hMapping = CreateFileMapping(
+ hFile, // file handle
+ NULL, // security attributes
+ PAGE_WRITECOPY, // protection
+ 0, // maximum size (high-order DWORD)
+ 0, // maximum size (low-order DWORD)
+ NULL); // name of the mapping object
+
+ if (hMapping == NULL)
+ {
+ // Handle error
+ CloseHandle(hFile);
+ return;
+ }
+
+ _mmap = MapViewOfFile(
+ hMapping, // handle to the file mapping object
+ FILE_MAP_COPY, // desired access
+ 0, // file offset (high-order DWORD)
+ 0, // file offset (low-order DWORD)
+ 0); // number of bytes to map (0 = entire file)
+
+ if (_mmap == nullptr)
+ {
+ // Handle error
+ CloseHandle(hMapping);
+ CloseHandle(hFile);
+ return;
+ }
+
+ mapped = true;
+
+ CloseHandle(hMapping);
+ CloseHandle(hFile);
+
+#else
+ fseek(fd, 0L, SEEK_END);
+ len = ftell(fd);
+ fseek(fd, 0L, SEEK_SET);
+
+ _mmap = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(fd), 0u);
+ if (_mmap == MAP_FAILED)
+ {
+ // Handle error
+ return;
+ }
+
+ mapped = true;
+#endif
+}
+
+void MMAP::Unmap()
+{
+#ifdef _MSC_VER
+ if (_mmap)
+ {
+ UnmapViewOfFile(_mmap);
+ mapped = false;
+ }
+#else
+ if (mapped)
+ {
+ munmap(_mmap, len);
+ mapped = false;
+ }
+#endif
+}
+
+
+std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>> MMappedFileAccessor::Open(const uint64_t sessionID, const std::string &path, std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine)
+{
+ std::scoped_lock<std::mutex> lock(fileAccessorsMutex);
+ if (fileAccessors.count(path) == 0)
+ {
+ auto fileAcccessor = std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>>(new SelfAllocatingWeakPtr<MMappedFileAccessor>(
+ // Allocator logic for the SelfAllocatingWeakPtr
+ [path=path, sessionID=sessionID](){
+ std::unique_lock<std::mutex> _lock(fileAccessorDequeMutex);
+
+ // Iterate through held references and start removing them until we can get a file pointer
+ // FIXME: This could clear all currently used file pointers and still not get one. FIX!
+ // We should probably use a condition variable here to wait for a file pointer to be released!!!
+ for (auto& [_, fileAccessorDeque] : fileAccessorReferenceHolder)
+ {
+ if (fileAccessorSemaphore.try_acquire())
+ break;
+ fileAccessorDeque.pop_front();
+ }
+
+ mmapCount++;
+ _lock.unlock();
+ auto accessor = std::shared_ptr<MMappedFileAccessor>(new MMappedFileAccessor(path), [](MMappedFileAccessor* accessor){
+ // worker thread or we can deadlock on exit here.
+ BinaryNinja::WorkerEnqueue([accessor](){
+ fileAccessorSemaphore.release();
+ mmapCount--;
+ if (fileAccessors.count(accessor->m_path))
+ {
+ std::scoped_lock<std::mutex> lock(fileAccessorsMutex);
+ fileAccessors.erase(accessor->m_path);
+ }
+ delete accessor;
+ }, "MMappedFileAccessor Destructor");
+ });
+ _lock.lock();
+ // If some background thread has managed to try and open a file when the BV was already closed,
+ // we can still give them the file they want so they dont crash, but as soon as they let go it's gone.
+ if (!blockedSessionIDs.count(sessionID))
+ fileAccessorReferenceHolder[sessionID].push_back(accessor);
+ return accessor;
+ },
+ [postAllocationRoutine=postAllocationRoutine](std::shared_ptr<MMappedFileAccessor> accessor){
+ if (postAllocationRoutine)
+ postAllocationRoutine(accessor);
+ }));
+ fileAccessors.insert_or_assign(path, fileAcccessor);
+ }
+ return fileAccessors.at(path);
+}
+
+
+void MMappedFileAccessor::CloseAll(const uint64_t sessionID)
+{
+ blockedSessionIDs.insert(sessionID);
+ if (fileAccessorReferenceHolder.count(sessionID) == 0)
+ return;
+ fileAccessorReferenceHolder.erase(sessionID);
+}
+
+
+void MMappedFileAccessor::InitialVMSetup()
+{
+ // check for BN_SHAREDCACHE_FP_MAX
+ // if it exists, set maxFPLimit to that value
+ maxFPLimit = 0;
+ if (auto env = getenv("BN_SHAREDCACHE_FP_MAX"); env)
+ {
+ // FIXME behav on 0 here is unintuitive, '0123' will interpret as octal and be 83 according to manpage. meh.
+ maxFPLimit = strtoull(env, nullptr, 0);
+ if (maxFPLimit < 10)
+ {
+ BinaryNinja::LogWarn("BN_SHAREDCACHE_FP_MAX set to below 10. A value of at least 10 is recommended for performant analysis on SharedCache Binaries.");
+ }
+ if (maxFPLimit == 0)
+ {
+ BinaryNinja::LogError("BN_SHAREDCACHE_FP_MAX set to 0. Adjusting to 1");
+ maxFPLimit = 1;
+ }
+ }
+ else
+ {
+ if (maxFPLimit < 10) {
+#ifdef _MSC_VER
+ // It is not _super_ clear what the max file pointer limit is on windows,
+ // but to my understanding, we are using the windows API to map files,
+ // so we should have at least 2^24;
+ // kind of funny to me that windows would be the most effecient OS to
+ // parallelize sharedcache processing on in terms of FP usage concerns
+ maxFPLimit = 0x1000000;
+#else
+ // unix in comparison will likely have a very small limit, especially mac, necessitating all of this consideration
+ struct rlimit rlim;
+ getrlimit(RLIMIT_NOFILE, &rlim);
+ maxFPLimit = rlim.rlim_cur / 2;
+#endif
+ }
+ }
+ BinaryNinja::LogInfo("SharedCache processing initialized with a max file pointer limit of 0x%llx", maxFPLimit);
+ fileAccessorSemaphore.set_count(maxFPLimit);
+}
+
+
+MMappedFileAccessor::MMappedFileAccessor(const std::string& path) : m_path(path)
+{
+#ifdef _MSC_VER
+ m_mmap.hFile = CreateFile(
+ path.c_str(), // file name
+ GENERIC_READ, // desired access (read-only)
+ FILE_SHARE_READ, // share mode
+ NULL, // security attributes
+ OPEN_EXISTING, // creation disposition
+ FILE_ATTRIBUTE_NORMAL, // flags and attributes
+ NULL); // template file
+
+ if (m_mmap.hFile == INVALID_HANDLE_VALUE)
+ {
+ // BNLogInfo("Couldn't read file at %s", path.c_str());
+ throw MissingFileException();
+ }
+
+#else
+#ifdef ABORT_FAILURES
+ if (path.empty())
+ {
+ cerr << "Path is empty." << endl;
+ abort();
+ }
+#endif
+ m_mmap.fd = fopen(path.c_str(), "r");
+ if (m_mmap.fd == nullptr)
+ {
+ BNLogError("Serious VM Error: Couldn't read file at %s", path.c_str());
+
+#ifndef _MSC_VER
+ try {
+ throw BinaryNinja::ExceptionWithStackTrace("Unable to Read file");
+ }
+ catch (ExceptionWithStackTrace &ex)
+ {
+ BNLogError("%s", ex.m_stackTrace.c_str());
+ BNLogError("Error: %d (%s)", errno, strerror(errno));
+ }
+#endif
+ throw MissingFileException();
+ }
+#endif
+
+ m_mmap.Map();
+}
+
+MMappedFileAccessor::~MMappedFileAccessor()
+{
+ // BNLogInfo("Unmapping %s", m_path.c_str());
+ m_mmap.Unmap();
+
+#ifdef _MSC_VER
+ if (m_mmap.hFile != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(m_mmap.hFile);
+ }
+#else
+ if (m_mmap.fd != nullptr)
+ {
+ fclose(m_mmap.fd);
+ }
+#endif
+}
+
+void MMappedFileAccessor::WritePointer(size_t address, size_t pointer)
+{
+ ((size_t*)(&((uint8_t*)m_mmap._mmap)[address]))[0] = pointer;
+}
+
+std::string MMappedFileAccessor::ReadNullTermString(size_t address)
+{
+ if (address > m_mmap.len)
+ return "";
+ size_t max = m_mmap.len;
+ size_t i = address;
+ std::string str;
+ str.reserve(140);
+ while (i < max)
+ {
+ char c = ((char*)(&((uint8_t*)m_mmap._mmap)[i]))[0];
+ if (c == 0)
+ break;
+ str += c;
+ i++;
+ }
+ str.shrink_to_fit();
+ return str;
+}
+
+uint8_t MMappedFileAccessor::ReadUChar(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint8_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int8_t MMappedFileAccessor::ReadChar(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int8_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+uint16_t MMappedFileAccessor::ReadUShort(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint16_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int16_t MMappedFileAccessor::ReadShort(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int16_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+uint32_t MMappedFileAccessor::ReadUInt32(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint32_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int32_t MMappedFileAccessor::ReadInt32(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int32_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+uint64_t MMappedFileAccessor::ReadULong(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint64_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int64_t MMappedFileAccessor::ReadLong(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int64_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+BinaryNinja::DataBuffer* MMappedFileAccessor::ReadBuffer(size_t address, size_t length)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ if (address + length > m_mmap.len)
+ throw MappingReadException();
+ void* data = (void*)(&(((uint8_t*)m_mmap._mmap)[address]));
+ void* dataCopy = malloc(length);
+ memcpy(dataCopy, data, length);
+ return new BinaryNinja::DataBuffer(dataCopy, length);
+}
+
+void MMappedFileAccessor::Read(void* dest, size_t address, size_t length)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ if (address + length > m_mmap.len)
+ throw MappingReadException();
+ memcpy(dest, (void*)&(((uint8_t*)m_mmap._mmap)[address]), length);
+}
+
+
+VM::VM(size_t pageSize, bool safe) : m_pageSize(pageSize), m_safe(safe)
+{
+ unsigned bits, var = (m_pageSize - 1 < 0) ? -(m_pageSize - 1) : m_pageSize - 1;
+ for (bits = 0; var != 0; ++bits)
+ var >>= 1;
+ m_pageSizeBits = bits;
+}
+
+VM::~VM()
+{
+}
+
+
+void VM::MapPages(uint64_t sessionID, size_t vm_address, size_t fileoff, size_t size, std::string filePath, std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine)
+{
+ // The mappings provided for shared caches will always be page aligned.
+ // We can use this to our advantage and gain considerable performance via page tables.
+ // This could probably be sped up if c++ were avoided?
+ // We want to create a map of page -> file offset
+
+ if (vm_address % m_pageSize != 0 || size % m_pageSize != 0)
+ {
+ throw MappingPageAlignmentException();
+ }
+
+ size_t pagesRemainingCount = size / m_pageSize;
+ for (size_t i = 0; i < size; i += m_pageSize)
+ {
+ // Our pages will be delimited by shifting off the page size
+ // So, 0x12345000 will become 0x12345 (assuming m_pageSize is 0x1000)
+ auto page = (vm_address + (i)) >> m_pageSizeBits;
+ if (m_map.count(page) != 0)
+ {
+ if (m_safe)
+ {
+ BNLogWarn("Remapping page 0x%lx (i == 0x%lx) (a: 0x%zx, f: 0x%zx)", page, i, vm_address, fileoff);
+ throw MappingCollisionException();
+ }
+ }
+ m_map.insert_or_assign(page, PageMapping(filePath, MMappedFileAccessor::Open(sessionID, filePath, postAllocationRoutine), i + fileoff));
+ }
+}
+
+std::pair<PageMapping, size_t> VM::MappingAtAddress(size_t address)
+{
+ // Get the page (e.g. 0x12345678 will become 0x12345 on 0x1000 aligned caches)
+ auto page = address >> m_pageSizeBits;
+ if (auto f = m_map.find(page); f != m_map.end())
+ {
+ // The PageMapping object returned contains the page, and more importantly, the file pointer (there can be
+ // multiple in newer caches) This is relevant for reading out the data in the rest of this file. The second item
+ // in this pair is created by taking the fileOffset (which will be a page but with the trailing bits (e.g.
+ // 0x12345000)
+ // and will add the "extra" bits lopped off when determining the page. (e.g. 0x12345678 -> 0x678)
+ return {f->second, f->second.fileOffset + (address & (m_pageSize - 1))};
+ }
+
+ throw MappingReadException();
+}
+
+
+bool VM::AddressIsMapped(uint64_t address)
+{
+ try
+ {
+ MappingAtAddress(address);
+ return true;
+ }
+ catch (...)
+ {}
+ return false;
+}
+
+
+uint64_t VMReader::ReadULEB128(size_t limit)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ auto fileCursor = mapping.second;
+ auto fileLimit = fileCursor + (limit - m_cursor);
+ auto fa = mapping.first.fileAccessor->lock();
+ auto* fileBuff = (uint8_t*)fa->Data();
+ do
+ {
+ if (fileCursor >= fileLimit)
+ return -1;
+ uint64_t slice = ((uint64_t*)&((fileBuff)[fileCursor]))[0] & 0x7f;
+ if (bit > 63)
+ return -1;
+ else
+ {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ } while (((uint64_t*)&(fileBuff[fileCursor++]))[0] & 0x80);
+ fa->Data(); // prevent deallocation of the fileAccessor as we're operating on the raw data buffer
+ return result;
+}
+
+
+int64_t VMReader::ReadSLEB128(size_t limit)
+{
+ uint8_t cur;
+ int64_t value = 0;
+ size_t shift = 0;
+
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ auto fileCursor = mapping.second;
+ auto fileLimit = fileCursor + (limit - m_cursor);
+ auto fa = mapping.first.fileAccessor->lock();
+ auto* fileBuff = (uint8_t*)fa->Data();
+
+ while (fileCursor < fileLimit)
+ {
+ cur = ((uint64_t*)&((fileBuff)[fileCursor]))[0];
+ fileCursor++;
+ value |= (cur & 0x7f) << shift;
+ shift += 7;
+ if ((cur & 0x80) == 0)
+ break;
+ }
+ value = (value << (64 - shift)) >> (64 - shift);
+ fa->Data(); // prevent deallocation of the fileAccessor as we're operating on the raw data buffer
+ return value;
+}
+
+std::string VM::ReadNullTermString(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadNullTermString(mapping.second);
+}
+
+uint8_t VM::ReadUChar(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadUChar(mapping.second);
+}
+
+int8_t VM::ReadChar(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadChar(mapping.second);
+}
+
+uint16_t VM::ReadUShort(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadUShort(mapping.second);
+}
+
+int16_t VM::ReadShort(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadShort(mapping.second);
+}
+
+uint32_t VM::ReadUInt32(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadUInt32(mapping.second);
+}
+
+int32_t VM::ReadInt32(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadInt32(mapping.second);
+}
+
+uint64_t VM::ReadULong(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadULong(mapping.second);
+}
+
+int64_t VM::ReadLong(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadLong(mapping.second);
+}
+
+BinaryNinja::DataBuffer* VM::ReadBuffer(size_t addr, size_t length)
+{
+ auto mapping = MappingAtAddress(addr);
+ return mapping.first.fileAccessor->lock()->ReadBuffer(mapping.second, length);
+}
+
+
+void VM::Read(void* dest, size_t addr, size_t length)
+{
+ auto mapping = MappingAtAddress(addr);
+ mapping.first.fileAccessor->lock()->Read(dest, mapping.second, length);
+}
+
+VMReader::VMReader(std::shared_ptr<VM> vm, size_t addressSize) : m_vm(vm), m_cursor(0), m_addressSize(addressSize) {}
+
+
+void VMReader::Seek(size_t address)
+{
+ m_cursor = address;
+}
+
+void VMReader::SeekRelative(size_t offset)
+{
+ m_cursor += offset;
+}
+
+std::string VMReader::ReadCString(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadNullTermString(mapping.second);
+}
+
+uint8_t VMReader::ReadUChar(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 1;
+ return mapping.first.fileAccessor->lock()->ReadUChar(mapping.second);
+}
+
+int8_t VMReader::ReadChar(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 1;
+ return mapping.first.fileAccessor->lock()->ReadChar(mapping.second);
+}
+
+uint16_t VMReader::ReadUShort(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 2;
+ return mapping.first.fileAccessor->lock()->ReadUShort(mapping.second);
+}
+
+int16_t VMReader::ReadShort(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 2;
+ return mapping.first.fileAccessor->lock()->ReadShort(mapping.second);
+}
+
+uint32_t VMReader::ReadUInt32(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 4;
+ return mapping.first.fileAccessor->lock()->ReadUInt32(mapping.second);
+}
+
+int32_t VMReader::ReadInt32(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 4;
+ return mapping.first.fileAccessor->lock()->ReadInt32(mapping.second);
+}
+
+uint64_t VMReader::ReadULong(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 8;
+ return mapping.first.fileAccessor->lock()->ReadULong(mapping.second);
+}
+
+int64_t VMReader::ReadLong(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 8;
+ return mapping.first.fileAccessor->lock()->ReadLong(mapping.second);
+}
+
+
+size_t VMReader::ReadPointer(size_t address)
+{
+ if (m_addressSize == 8)
+ return ReadULong(address);
+ else if (m_addressSize == 4)
+ return ReadUInt32(address);
+
+ // no idea what horrible arch we have, should probably die here.
+ return 0;
+}
+
+
+size_t VMReader::ReadPointer()
+{
+ if (m_addressSize == 8)
+ return Read64();
+ else if (m_addressSize == 4)
+ return Read32();
+
+ return 0;
+}
+
+BinaryNinja::DataBuffer* VMReader::ReadBuffer(size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += length;
+ return mapping.first.fileAccessor->lock()->ReadBuffer(mapping.second, length);
+}
+
+BinaryNinja::DataBuffer* VMReader::ReadBuffer(size_t addr, size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(addr);
+ m_cursor = addr + length;
+ return mapping.first.fileAccessor->lock()->ReadBuffer(mapping.second, length);
+}
+
+void VMReader::Read(void* dest, size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += length;
+ mapping.first.fileAccessor->lock()->Read(dest, mapping.second, length);
+}
+
+void VMReader::Read(void* dest, size_t addr, size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(addr);
+ m_cursor = addr + length;
+ mapping.first.fileAccessor->lock()->Read(dest, mapping.second, length);
+}
+
+
+uint8_t VMReader::Read8()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 1;
+ return mapping.first.fileAccessor->lock()->ReadUChar(mapping.second);
+}
+
+int8_t VMReader::ReadS8()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 1;
+ return mapping.first.fileAccessor->lock()->ReadChar(mapping.second);
+}
+
+uint16_t VMReader::Read16()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 2;
+ return mapping.first.fileAccessor->lock()->ReadUShort(mapping.second);
+}
+
+int16_t VMReader::ReadS16()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 2;
+ return mapping.first.fileAccessor->lock()->ReadShort(mapping.second);
+}
+
+uint32_t VMReader::Read32()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 4;
+ return mapping.first.fileAccessor->lock()->ReadUInt32(mapping.second);
+}
+
+int32_t VMReader::ReadS32()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 4;
+ return mapping.first.fileAccessor->lock()->ReadInt32(mapping.second);
+}
+
+uint64_t VMReader::Read64()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 8;
+ return mapping.first.fileAccessor->lock()->ReadULong(mapping.second);
+}
+
+int64_t VMReader::ReadS64()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 8;
+ return mapping.first.fileAccessor->lock()->ReadLong(mapping.second);
+}