diff --git a/LUA_API.rst b/LUA_API.rst index 2bb2c949e..449f92b46 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -1235,6 +1235,12 @@ and are only documented here for completeness: Returns a sequence of tables describing virtual memory ranges of the process. +* ``dfhack.internal.patchMemory(dest,src,count)`` + + Like memmove below, but works even if dest is read-only memory, e.g. code. + If destination overlaps a completely invalid memory region, or another error + occurs, returns false. + * ``dfhack.internal.memmove(dest,src,count)`` Wraps the standard memmove function. Accepts both numbers and refs as pointers. diff --git a/Lua API.html b/Lua API.html index 2c9a6a8df..7dfc7df00 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1409,6 +1409,11 @@ global environment, persistent between calls to the script.

  • dfhack.internal.getMemRanges()

    Returns a sequence of tables describing virtual memory ranges of the process.

  • +
  • dfhack.internal.patchMemory(dest,src,count)

    +

    Like memmove below, but works even if dest is read-only memory, e.g. code. +If destination overlaps a completely invalid memory region, or another error +occurs, returns false.

    +
  • dfhack.internal.memmove(dest,src,count)

    Wraps the standard memmove function. Accepts both numbers and refs as pointers.

  • diff --git a/library/Core.cpp b/library/Core.cpp index 826576b77..f129d9523 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1548,6 +1548,56 @@ void ClassNameCheck::getKnownClassNames(std::vector &names) names.push_back(*it); } +bool Process::patchMemory(void *target, const void* src, size_t count) +{ + uint8_t *sptr = (uint8_t*)target; + uint8_t *eptr = sptr + count; + + // Find the valid memory ranges + std::vector ranges; + getMemRanges(ranges); + + unsigned start = 0; + while (start < ranges.size() && ranges[start].end <= sptr) + start++; + if (start >= ranges.size() || ranges[start].start > sptr) + return false; + + unsigned end = start+1; + while (end < ranges.size() && ranges[end].start < eptr) + { + if (ranges[end].start != ranges[end-1].end) + return false; + end++; + } + if (ranges[end-1].end < eptr) + return false; + + // Verify current permissions + for (unsigned i = start; i < end; i++) + if (!ranges[i].valid || !(ranges[i].read || ranges[i].execute) || ranges[i].shared) + return false; + + // Apply writable permissions & update + bool ok = true; + + for (unsigned i = start; i < end && ok; i++) + { + t_memrange perms = ranges[i]; + perms.write = perms.read = true; + if (!setPermisions(perms, perms)) + ok = false; + } + + if (ok) + memmove(target, src, count); + + for (unsigned i = start; i < end && ok; i++) + setPermisions(ranges[i], ranges[i]); + + return ok; +} + /******************************************************************************* M O D U L E S *******************************************************************************/ diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index b0a085eca..108dba88f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1124,6 +1124,17 @@ static int internal_getMemRanges(lua_State *L) return 1; } +static int internal_patchMemory(lua_State *L) +{ + void *dest = checkaddr(L, 1); + void *src = checkaddr(L, 2); + int size = luaL_checkint(L, 3); + if (size < 0) luaL_argerror(L, 1, "negative size"); + bool ok = Core::getInstance().p->patchMemory(dest, src, size); + lua_pushboolean(L, ok); + return 1; +} + static int internal_memmove(lua_State *L) { void *dest = checkaddr(L, 1); @@ -1214,6 +1225,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "setAddress", internal_setAddress }, { "getVTable", internal_getVTable }, { "getMemRanges", internal_getMemRanges }, + { "patchMemory", internal_patchMemory }, { "memmove", internal_memmove }, { "memcmp", internal_memcmp }, { "memscan", internal_memscan }, diff --git a/library/include/MemAccess.h b/library/include/MemAccess.h index c51df3c64..0e5f618e2 100644 --- a/library/include/MemAccess.h +++ b/library/include/MemAccess.h @@ -283,6 +283,9 @@ namespace DFHack /// modify permisions of memory range bool setPermisions(const t_memrange & range,const t_memrange &trgrange); + + /// write a possibly read-only memory area + bool patchMemory(void *target, const void* src, size_t count); private: VersionInfo * my_descriptor; PlatformSpecific *d;