diff --git a/library/Core.cpp b/library/Core.cpp index 7766b3591..dc620e805 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1615,15 +1615,27 @@ void ClassNameCheck::getKnownClassNames(std::vector &names) names.push_back(*it); } -bool Process::patchMemory(void *target, const void* src, size_t count) +MemoryPatcher::MemoryPatcher(Process *p) : p(p) +{ + if (!p) + p = Core::getInstance().p; +} + +MemoryPatcher::~MemoryPatcher() +{ + close(); +} + +bool MemoryPatcher::verifyAccess(void *target, size_t count, bool write) { uint8_t *sptr = (uint8_t*)target; uint8_t *eptr = sptr + count; // Find the valid memory ranges - std::vector ranges; - getMemRanges(ranges); + if (ranges.empty()) + p->getMemRanges(ranges); + // Find the ranges that this area spans unsigned start = 0; while (start < ranges.size() && ranges[start].end <= sptr) start++; @@ -1645,24 +1657,49 @@ bool Process::patchMemory(void *target, const void* src, size_t count) if (!ranges[i].valid || !(ranges[i].read || ranges[i].execute) || ranges[i].shared) return false; - // Apply writable permissions & update - bool ok = true; + if (!write) + return true; - for (unsigned i = start; i < end && ok; i++) + // Apply writable permissions & update + for (unsigned i = start; i < end; i++) { - t_memrange perms = ranges[i]; + auto &perms = ranges[i]; + if (perms.write && perms.read) + continue; + + save.push_back(perms); perms.write = perms.read = true; - if (!setPermisions(perms, perms)) - ok = false; + if (!p->setPermisions(perms, perms)) + return false; } - if (ok) - memmove(target, src, count); + return true; +} + +bool MemoryPatcher::write(void *target, const void *src, size_t size) +{ + if (!makeWritable(target, size)) + return false; + + memmove(target, src, size); + return true; +} - for (unsigned i = start; i < end && ok; i++) - setPermisions(ranges[i], ranges[i]); +void MemoryPatcher::close() +{ + for (size_t i = 0; i < save.size(); i++) + p->setPermisions(save[i], save[i]); + + save.clear(); + ranges.clear(); +}; + + +bool Process::patchMemory(void *target, const void* src, size_t count) +{ + MemoryPatcher patcher(this); - return ok; + return patcher.write(target, src, count); } /******************************************************************************* diff --git a/library/VTableInterpose.cpp b/library/VTableInterpose.cpp index d8a07e830..3f9423b45 100644 --- a/library/VTableInterpose.cpp +++ b/library/VTableInterpose.cpp @@ -166,12 +166,12 @@ void *virtual_identity::get_vmethod_ptr(int idx) return vtable[idx]; } -bool virtual_identity::set_vmethod_ptr(int idx, void *ptr) +bool virtual_identity::set_vmethod_ptr(MemoryPatcher &patcher, int idx, void *ptr) { assert(idx >= 0); void **vtable = (void**)vtable_ptr; if (!vtable) return NULL; - return Core::getInstance().p->patchMemory(&vtable[idx], &ptr, sizeof(void*)); + return patcher.write(&vtable[idx], &ptr, sizeof(void*)); } /* @@ -344,7 +344,9 @@ void VMethodInterposeLinkBase::on_host_delete(virtual_identity *from) auto last = this; while (last->prev) last = last->prev; - from->set_vmethod_ptr(vmethod_idx, last->saved_chain); + MemoryPatcher patcher; + + from->set_vmethod_ptr(patcher, vmethod_idx, last->saved_chain); // Unlink the chains child_hosts.erase(from); @@ -379,13 +381,15 @@ bool VMethodInterposeLinkBase::apply(bool enable) assert(old_ptr != NULL && (!old_link || old_link->interpose_method == old_ptr)); // Apply the new method ptr + MemoryPatcher patcher; + set_chain(old_ptr); if (next_link) { next_link->set_chain(interpose_method); } - else if (!host->set_vmethod_ptr(vmethod_idx, interpose_method)) + else if (!host->set_vmethod_ptr(patcher, vmethod_idx, interpose_method)) { set_chain(NULL); return false; @@ -459,7 +463,7 @@ bool VMethodInterposeLinkBase::apply(bool enable) { auto nhost = *it; assert(nhost->interpose_list[vmethod_idx] == old_link); - nhost->set_vmethod_ptr(vmethod_idx, interpose_method); + nhost->set_vmethod_ptr(patcher, vmethod_idx, interpose_method); nhost->interpose_list[vmethod_idx] = this; } @@ -496,9 +500,11 @@ void VMethodInterposeLinkBase::remove() } else { + MemoryPatcher patcher; + // Remove from the list in the identity and vtable host->interpose_list[vmethod_idx] = prev; - host->set_vmethod_ptr(vmethod_idx, saved_chain); + host->set_vmethod_ptr(patcher, vmethod_idx, saved_chain); for (auto it = child_next.begin(); it != child_next.end(); ++it) { @@ -515,7 +521,7 @@ void VMethodInterposeLinkBase::remove() auto nhost = *it; assert(nhost->interpose_list[vmethod_idx] == this); nhost->interpose_list[vmethod_idx] = prev; - nhost->set_vmethod_ptr(vmethod_idx, saved_chain); + nhost->set_vmethod_ptr(patcher, vmethod_idx, saved_chain); if (prev) prev->child_hosts.insert(nhost); } diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 6c5ae5e62..32ffabc32 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -294,6 +294,7 @@ namespace DFHack #endif class DFHACK_EXPORT VMethodInterposeLinkBase; + class MemoryPatcher; class DFHACK_EXPORT virtual_identity : public struct_identity { static std::map known; @@ -313,7 +314,7 @@ namespace DFHack bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); } void *get_vmethod_ptr(int index); - bool set_vmethod_ptr(int index, void *ptr); + bool set_vmethod_ptr(MemoryPatcher &patcher, int index, void *ptr); public: virtual_identity(size_t size, TAllocateFn alloc, diff --git a/library/include/MemAccess.h b/library/include/MemAccess.h index 0bc027c5c..1b8e687b9 100644 --- a/library/include/MemAccess.h +++ b/library/include/MemAccess.h @@ -315,5 +315,22 @@ namespace DFHack // Get list of names given to ClassNameCheck constructors. static void getKnownClassNames(std::vector &names); }; + + class DFHACK_EXPORT MemoryPatcher + { + Process *p; + std::vector ranges, save; + public: + MemoryPatcher(Process *p = NULL); + ~MemoryPatcher(); + + bool verifyAccess(void *target, size_t size, bool write = false); + bool makeWritable(void *target, size_t size) { + return verifyAccess(target, size, true); + } + bool write(void *target, const void *src, size_t size); + + void close(); + }; } #endif