Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read/WriteProcessMemory in Ruby

I have been experimenting with reading and writing process memory in Ruby in hopes to move some old C++ programs to a more dynamic language. However I've not been having an easy time moving over. I've done some reading but I can't find much on my specific problem. I probably have some pretty fundamental mistakes below as I'm not too sure how pointer management works in Ruby-ffi.

Anyway, I currently have the ffi gem installed and have been using that to grab functions. This is what I have:

module Memory
  PROC_READ = 0x10
  PROC_WRITE = 0x20
  PROC_RW = PROC_READ | PROC_WRITE

  extend FFI::Library

  ffi_lib 'kernel32'

  # HANDLE WINAPI OpenProcess(DWORD, BOOL, DWORD)
  attach_function :open, :OpenProcess, [:uint, :bool, :uint], :pointer

  # BOOL WINAPI CloseHandle(HANDLE)
  attach_function :close, :CloseHandle, [:pointer], :bool

  # BOOL WINAPI ReadProcessMemory(HANDLE, LPCVOID, out LPVOID, SIZE_T, out SIZE_T)
  attach_function :read, :ReadProcessMemory, [:pointer, :pointer, :pointer, :int, :int], :bool

  # BOOL WINAPI WriteProcessMemory(HANDLE, LPCVOID, LPVOID, SIZE_T, out SIZE_T)
  attach_function :write, :WriteProcessMemory, [:pointer, :pointer, :pointer, :int, :int], :bool

  # DWORD WINAPI GetLastError(void)
  attach_function :error, :GetLastError, [], :uint
end

It seems that when I call Memory.open I get a correct handle. I'm not quite sure, but here's the output of a variable storing the result in-case I'm wrong.

#<FFI::Pointer address=0x00000000000150>

Here's the full code I have currently:

# 1048 is a fixed pid currently
handle = Memory::open(Memory::PROC_RW, false, 1048)
puts "GetLastError: #{Memory::error()}"

# Address to read from
loc = 0x057C75F8

out = 0
read = 0

# Supposed to be the address of out to store the value read
val = FFI::MemoryPointer.new :uint, out

# Supposed to be a pointer to loc which holds the address to read from
addr = FFI::MemoryPointer.new :pointer, loc

res = Memory::read(handle, addr, val, 4, read)

puts "GetLastError: #{Memory::error()}"
puts "ReadProcessMemory: #{res}"
puts read
puts out

Memory::close(handle)

This prints out the following:

GetLastError: 0
GetLastError: 0
ReadProcessMemory: false
0
0

I know I must be doing something fundamentally wrong with the pointer variables. If I change addr to an FFI::Pointer of type :uint and value loc then ReadProcessMemory returns true, but the out and read variables do not change.

I hope this has been clear enough. I can try to clarify if something seems like it's missing.

like image 252
ozdrgnaDiies Avatar asked Nov 25 '13 15:11

ozdrgnaDiies


1 Answers

I was finally able to figure out my problem with pointers after reading the example page on Github for the project:

https://github.com/ffi/ffi/wiki/Examples

Specifically under the "Output Parameters with MemoryPointer" section. After reading that I was able to change my code to the following:

require 'ffi'

module Memory
  PROC_READ = 0x10
  PROC_WRITE = 0x20
  PROC_RW = PROC_READ | PROC_WRITE

  extend FFI::Library

  ffi_lib 'kernel32'
  ffi_convention :stdcall

  attach_function :open, :OpenProcess, [:uint, :bool, :uint], :pointer
  attach_function :close, :CloseHandle, [:pointer], :bool
  attach_function :read, :ReadProcessMemory, [:pointer, :pointer, :pointer, :size_t, :pointer], :bool
  attach_function :write, :WriteProcessMemory, [:pointer, :pointer, :pointer, :size_t, :pointer], :bool
  attach_function :error, :GetLastError, [], :uint
end

# 1048 is a fixed pid currently
handle = Memory::open(Memory::PROC_RW, false, 1048)
puts "GetLastError: #{Memory::error()}"

# Output parameters for ReadProcessMemory
out = FFI::MemoryPointer.new :pointer
read = FFI::MemoryPointer.new :pointer

# Pointer holding the location to read from
addr = FFI::Pointer.new :pointer, 0x057C75F8

res = Memory::read(handle, addr, out, 4, read)

# get_pointer(0) grabs the pointer to the value
# address holds the value we actually want (at least in this case)
read = read.get_pointer(0).address
out = out.get_pointer(0).address

puts "GetLastError: #{Memory::error()}"
puts "ReadProcessMemory: #{res}"
puts "Bytes Read: #{read}"
puts "Value Read: #{out}"
Memory::close(handle)

The above code in my specific case correctly outputs the following:

GetLastError: 0
GetLastError: 0
ReadProcessMemory: true
Bytes Read: 4
Value Read: 10

I hope this will help someone in the future.

like image 153
ozdrgnaDiies Avatar answered Oct 22 '22 22:10

ozdrgnaDiies