Null-terminated string swapper

A forum dedicated to use and support LUA for Cheat Engine.


Post Reply
User avatar
mece
Table Maker
Table Maker
Apprentice Hacker
Apprentice Hacker
Posts: 63
Joined: Sat Jul 23, 2022 9:21 am
Answers: 0
x 90
Contact:

Null-terminated string swapper

Post by mece »

Problem:
Some games use null-terminated strings as identity for game objects. E.g. replacing the string with another you can replace item prefix with rarest one. The problem is when new string is longer than original the change crushes the game because of buffer overflow.

Possible solution:
Please create a CE plugin that will allocate memory for new string value of memory record. This should be optional. Like this:
Image

Partial solution I used in titan quest table for inventory editing.


User avatar
mece
Table Maker
Table Maker
Apprentice Hacker
Apprentice Hacker
Posts: 63
Joined: Sat Jul 23, 2022 9:21 am
Answers: 0
x 90
Contact:

Re: Null-terminated string swapper

Post by mece »

Here is the solution:
Open the image in new browser tab for better view.
Image

The main idea is to redefine the memory record editing procedure. This includes the modified UI.
[ic]getAddressList().OnValueChange = OnValueChangeEx[/ic]

Important note: this prototype is built with several assumptions:

  • the string length is stored as 4 byte integer next to the string value pointer

  • there is no need to release the memory allocated for string values (can crash the game)

Code: Select all

[ENABLE]
{$lua}
if syntaxcheck then return end
GetLuaEngine().MenuItem5.doClick() -- crear lua output
OpenProcess(getCheatEngineProcessID())
--------------------------------------------------------------------------------
function getPointerSize()
  return 4 + 4*(targetIs64Bit() and 1 or 0)
end
--------------------------------------------------------------------------------
function DeAllocateMemoryEx(symbol)
  if getAddressSafe(symbol) then
    deAlloc(symbol)
    unregisterSymbol(symbol)
  end
end
--------------------------------------------------------------------------------
function AllocateMemoryEx(symbol,size)
  local mem = allocateMemory(size)
  registerSymbol(symbol,mem)
  return mem
end
--------------------------------------------------------------------------------
strings = {}
function WriteStringEx(value,len,widechar)
  local num = #strings+1
  local memSize = (len+1)*(widechar and 2 or 1)
  local terminator = widechar and {0x0, 0x0} or {0x0}
  local addr = allocateMemory(memSize)
  if addr == nil or addr == 0 then
    print("allocateMemory() error!")
    return nil
  end
  strings[num] = addr
  writeString(addr,value,widechar)
  writeBytes(addr+len,terminator)
  registerSymbol("strings_" .. tostring(num), addr)
  return addr
end
--------------------------------------------------------------------------------
function AllocateMemoryWriteString(value)
  local len = string.len(value)
  local strAddr = WriteStringEx(value,len)

  local strMetaAddr = AllocateMemoryEx("StrMeta",16)
  local strPtrAddr = strMetaAddr
  local strLenAddr = strMetaAddr + getPointerSize()
  writePointer(strPtrAddr,strAddr)
  writeInteger(strLenAddr,len)
end
--------------------------------------------------------------------------------
function getSubAddress(mr, newOffsetCount)
  local addr = getAddressSafe(mr.Address)
  if newOffsetCount >= mr.OffsetCount then return mr.CurrentAddress end
  if newOffsetCount > 0 then
    for i = mr.OffsetCount-1, mr.OffsetCount-newOffsetCount, -1 do
      local ptr = readPointer(addr)
      local ofs = tonumber(mr.OffsetText[i],16)
      if ptr == nil or ptr == 0 or ofs == nil then return 0 end
      addr = ptr + ofs
    end
  end
  return addr
end
--------------------------------------------------------------------------------
function UDFChangeValue_Close(sender)
  UDFChangeValue.ModalResult = sender.ModalResult
end
--------------------------------------------------------------------------------
function UDFChangeValue_FormShow(sender)
  UDFChangeValue.CEMemoStringValue.setFocus()
end
--------------------------------------------------------------------------------
function OnValueChangeEx( addresslist, mr )
  if mr.Type == vtString and mr.OffsetCount > 0 then
    local memo = UDFChangeValue.CEMemoStringValue
    memo.Lines.Text = mr.Value
    memo.selectAll()
    if UDFChangeValue.showModal() == mrOK then
      local newValue = memo.Lines.Text
      local newLen = string.len(newValue)
      local oldLen = string.len(mr.Value)
      if newLen ~= oldLen then
        local strPtrAddr = getSubAddress(mr, mr.OffsetCount-1)
        local strLenAddr = strPtrAddr + getPointerSize()
        if newLen > oldLen and UDFChangeValue.CECheckboxStringValue.Checked then
          local size = (newLen+1)*(mr.String.Unicode and 2 or 1)
          writePointer(strPtrAddr, WriteStringEx(newValue, newLen, mr.String.Unicode))
        end
        writeInteger(strLenAddr,newLen)
      end
      mr.Value = newValue
    end
    return true
  end
  return false -- use default editing UI
end
--------------------------------------------------------------------------------
getAddressList().OnValueChange = OnValueChangeEx
AllocateMemoryWriteString("Hello World!")
{$asm}
[DISABLE]
{$lua}
--------------------------------------------------------------------------------
local function DeleteUserdefinedSymbols()
  local mv = getMemoryViewForm()
  if mv.frmSymbolhandler == nil then
    mv.Symbolhandler1.doClick()
    mv.frmSymbolhandler.Close()
  end
  local items = mv.frmSymbolhandler.ListView1.Items
  for i=0,items.Count-1 do
    unregisterSymbol(items.Item[0].Caption)
  end
end
--------------------------------------------------------------------------------
DeleteUserdefinedSymbols()
DeAllocateMemoryEx("StrMeta")
Attachments
Null-terminated string swapper.CT
(9.79 KiB) Downloaded 504 times

Post Reply