From bcc41c081a43a68042c4757a6569cb690098d9b8 Mon Sep 17 00:00:00 2001
From: Alexander Gavrilov
Date: Fri, 17 Aug 2012 14:32:04 +0400
Subject: [PATCH] Add a utility function for patching read-only memory.
---
LUA_API.rst | 6 +++++
Lua API.html | 5 ++++
library/Core.cpp | 50 +++++++++++++++++++++++++++++++++++++
library/LuaApi.cpp | 12 +++++++++
library/include/MemAccess.h | 3 +++
5 files changed, 76 insertions(+)
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;