From 4aa1999347f85d83dc27d7ef1e6e4cdd6e07561e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 28 Oct 2012 11:50:28 +0400 Subject: [PATCH] Add a lua api function for patching multiple individual bytes. --- Lua API.html | 9 ++++++ Lua API.rst | 12 +++++++ library/Core.cpp | 5 +-- library/LuaApi.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++ library/LuaTools.cpp | 5 ++- 5 files changed, 102 insertions(+), 5 deletions(-) diff --git a/Lua API.html b/Lua API.html index 9c18b6585..be52ea985 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1714,6 +1714,15 @@ global environment, persistent between calls to the script.

If destination overlaps a completely invalid memory region, or another error occurs, returns false.

+
  • dfhack.internal.patchBytes(write_table[, verify_table])

    +

    The first argument must be a lua table, which is interpreted as a mapping from +memory addresses to byte values that should be stored there. The second argument +may be a similar table of values that need to be checked before writing anything.

    +

    The function takes care to either apply all of write_table, or none of it. +An empty write_table with a nonempty verify_table can be used to reasonably +safely check if the memory contains certain values.

    +

    Returns true if successful, or nil, error_msg, address if not.

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

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

  • diff --git a/Lua API.rst b/Lua API.rst index 185672816..8dad3663b 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -1581,6 +1581,18 @@ and are only documented here for completeness: If destination overlaps a completely invalid memory region, or another error occurs, returns false. +* ``dfhack.internal.patchBytes(write_table[, verify_table])`` + + The first argument must be a lua table, which is interpreted as a mapping from + memory addresses to byte values that should be stored there. The second argument + may be a similar table of values that need to be checked before writing anything. + + The function takes care to either apply all of ``write_table``, or none of it. + An empty ``write_table`` with a nonempty ``verify_table`` can be used to reasonably + safely check if the memory contains certain values. + + Returns *true* if successful, or *nil, error_msg, address* if not. + * ``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 9986c3396..111774f26 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1657,14 +1657,11 @@ bool MemoryPatcher::verifyAccess(void *target, size_t count, bool write) if (!ranges[i].valid || !(ranges[i].read || ranges[i].execute) || ranges[i].shared) return false; - if (!write) - return true; - // Apply writable permissions & update for (unsigned i = start; i < end; i++) { auto &perms = ranges[i]; - if (perms.write && perms.read) + if ((perms.write || !write) && perms.read) continue; save.push_back(perms); diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 593ac3d8d..6ca1188d9 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1535,6 +1535,81 @@ static int internal_patchMemory(lua_State *L) return 1; } +static int internal_patchBytes(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); + + MemoryPatcher patcher; + + if (!lua_isnil(L, 2)) + { + luaL_checktype(L, 2, LUA_TTABLE); + + lua_pushnil(L); + + while (lua_next(L, 2)) + { + uint8_t *addr = (uint8_t*)checkaddr(L, -2, true); + int isnum; + uint8_t value = (uint8_t)lua_tounsignedx(L, -1, &isnum); + if (!isnum) + luaL_error(L, "invalid value in verify table"); + lua_pop(L, 1); + + if (!patcher.verifyAccess(addr, 1, false)) + { + lua_pushnil(L); + lua_pushstring(L, "invalid verify address"); + lua_pushvalue(L, -3); + return 3; + } + + if (*addr != value) + { + lua_pushnil(L); + lua_pushstring(L, "wrong verify value"); + lua_pushvalue(L, -3); + return 3; + } + } + } + + lua_pushnil(L); + + while (lua_next(L, 1)) + { + uint8_t *addr = (uint8_t*)checkaddr(L, -2, true); + int isnum; + uint8_t value = (uint8_t)lua_tounsignedx(L, -1, &isnum); + if (!isnum) + luaL_error(L, "invalid value in write table"); + lua_pop(L, 1); + + if (!patcher.verifyAccess(addr, 1, true)) + { + lua_pushnil(L); + lua_pushstring(L, "invalid write address"); + lua_pushvalue(L, -3); + return 3; + } + } + + lua_pushnil(L); + + while (lua_next(L, 1)) + { + uint8_t *addr = (uint8_t*)checkaddr(L, -2, true); + uint8_t value = (uint8_t)lua_tounsigned(L, -1); + lua_pop(L, 1); + + *addr = value; + } + + lua_pushboolean(L, true); + return 1; +} + static int internal_memmove(lua_State *L) { void *dest = checkaddr(L, 1); @@ -1626,6 +1701,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "getVTable", internal_getVTable }, { "getMemRanges", internal_getMemRanges }, { "patchMemory", internal_patchMemory }, + { "patchBytes", internal_patchBytes }, { "memmove", internal_memmove }, { "memcmp", internal_memcmp }, { "memscan", internal_memscan }, diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 6bf218ba0..c052b88aa 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -107,7 +107,8 @@ static void signal_typeid_error(color_ostream *out, lua_State *state, type_identity *type, const char *msg, int val_index, bool perr, bool signal) { - std::string error = stl_sprintf(msg, type->getFullName().c_str()); + std::string typestr = type ? type->getFullName() : "any pointer"; + std::string error = stl_sprintf(msg, typestr.c_str()); if (signal) { @@ -134,6 +135,8 @@ void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_ if (lua_isnil(state, val_index)) return NULL; + if (lua_islightuserdata(state, val_index) && !lua_touserdata(state, val_index)) + return NULL; void *rv = get_object_internal(state, type, val_index, exact_type, false);