Home

Awesome

memreader

Build status

memreader is a Lua module for reading the memory of Windows processes.

local memreader = require('memreader')

-- Find a window by title and open a handle to its process
local window = memreader.findwindow("Your Window Title")
local process = memreader.openprocess(window.pid)

-- Read the first 8 bytes from the start of the main module's memory
local data_rel = process:readrelative(0, 8)

-- Do the same thing, but using the absolute address this time
local address = process.base
local data_abs = process:read(address, 8)

-- Data is returned as a string of the specified length (not null-terminated)
assert(#data_rel == 8 and #data_abs == 8)
assert(data_rel == data_abs)

Installation

With Luarocks:

luarocks install memreader

Building

To build memreader, you'll need to install cmake, some version of Visual Studio, and have a Lua .lib file that can be found by cmake (preferably built with the same compiler you're using to build memreader).

Using cmake-gui

Using cmake

Open a command line in the memreader directory and do the following:

mkdir build
cd build
cmake ..
cmake --build . --config Release

If needed, you can specify a generator by doing cmake -G "Visual Studio 14 2015 Win64" .. instead of cmake ..

API Reference

memreader.debugprivilege([state = true])

Attempts to adjust the access token of the Lua process to set the SeDebugPrivilege privilege (needed to access the memory of processes owned by other accounts). Calling this may or may not be necessary depending on how Lua is spawned, your use case, etc.

On success, returns true; otherwise, returns nil, errmsg.

memreader.processes()

Returns an iterator for all the processes in the system, in pid, name pairs.

Example:

local process
for pid, name in memreader.processes() do
  if name == "target.exe" then
    process = memreader.openprocess(pid)
    break
  end
end

Relevant WinAPI docs: CreateToolhelp32Snapshot, Process32Next

memreader.findwindow(title)

Finds a window by title. If found, returns a memreader.window usertype; otherwise, returns nil, errmsg.

Note: If there are multiple windows with the same title, this will only return the first (in arbitrary order). findwindow is faster than iterating with processes, but is not as precise or thorough.

Relevant WinAPI docs: FindWindow

memreader.openprocess(pid)

Attempts to open a handle to the process with the given process ID using the flags PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ. On success, returns a memreader.process usertype; otherwise, returns nil, errmsg.

Relevant WinAPI docs: OpenProcess

memreader.process

A usertype for process handles.

Fields (read-only):

process:read(address, nbytes)

Reads the specified number of bytes starting at the given address (can be either a number or a memreader.address) and returns that memory as a string (not null-terminated). On failure, returns nil, errmsg.

process:readrelative(offset, nbytes)

Like process:read(), except that offset is added to the process' main module's base address to determine the address to start from.

-- The following are exactly equivalent
process:read(process.base + 0x40, 4)
process:readrelative(0x40, 4)

process:modules()

Returns an iterator for all the modules of the process, as memreader.module usertypes.

Example:

for module in process:modules() do
  print(module.name, module.base)
end

process:exitcode()

Returns the exit code of the process (if it has exited). If the process is still running, then it will instead return nil. On failure, returns nil, errmsg.

process:version()

Retrieves the file and product version info embedded in the process' main module and returns it as a table. On failure, returns nil, errmsg.

Structure of the returned table:

{ 
  file = { major=1, minor=0, build=3, revision=105 },
  product = { major=3, minor=0, build=0, revision=0 }
}

memreader.module

A usertype for process modules.

Fields (read-only):

memreader.address

A usertype for an address in memory (LPVOID). Can be manipulated by adding/subtracting it with numbers or other memreader.address instances.

Example:

local a = process.base
local b = a + 0x40
local c = b - a

memreader.window

A usertype for window handles.

Fields (read-only):