Read/Write structures in memory easily
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
spec
tasks
.codeclimate.yml
.gitignore
.rubocop.yml
.travis.yml
Gemfile
Gemfile.lock
LICENSE
README.md
README.tpl
Rakefile
memory_io.gemspec

README.md

Build Status Gem Version Maintainability Test Coverage Inline docs MIT License

MemoryIO

Read/Write complicated structures in memory easily.

Motivation

I usually need to dump a structure, say string in C++, from memory for debugging. This is not hard if using gdb. However, gdb doesn't support writing Ruby scripts (unless you use gdb-ruby, which has dependency of MemoryIO). So I create this repo and want to make the debug procedure much easier.

This repo has two main targets:

  1. To communiate with memory easily.
  2. To collect all common structures for debugging/learning.

Why

It's not hard to read/write a process's memory (simply open the file /proc/$PID/mem), but it still worth to wrap it.

This repo also targets to collect all common structures, such as how to parse a C++/Rust/Python object from memory. Therefore, Pull Requests of adding new structures are welcome :D

Supported Platform

  • Linux
  • (TODO) Windows
  • (TODO) MacOS

Implemented Structures

Following is the list of supported structures. Each type has a full-name and an alias. For example,

require 'memory_io'

process = MemoryIO.attach(`pidof victim`.to_i)
# read a 64-bit unsigned integer
process.read(0x601000, 1, as: 'basic/u64')
# is equivalent to
process.read(0x601000, 1, as: :u64)

Goto the online document for more details of each type.

BASIC

  • basic/u8: An unsigned 8-bit integer. Also known as: :u8
  • basic/u16: An unsigned 16-bit integer. Also known as: :u16
  • basic/u32: An unsigned 32-bit integer. Also known as: :u32
  • basic/u64: An unsigned 64-bit integer. Also known as: :u64
  • basic/s8: A signed 8-bit integer. Also known as: :s8
  • basic/s16: A signed 16-bit integer. Also known as: :s16
  • basic/s32: A signed 32-bit integer. Also known as: :s32
  • basic/s64: A signed 64-bit integer. Also known as: :s64
  • basic/float: IEEE-754 32-bit floating number. Also known as: :float
  • basic/double: IEEE-754 64-bit floating number. Also known as: :double

CLANG

  • clang/c_str: A null-terminated string. Also known as: :c_str

CPP

  • cpp/string: The std::string class in C++11. Also known as: :string

Installation

Available on RubyGems.org!

$ gem install memory_io

Usage

Read Process's Memory

require 'memory_io'

process = MemoryIO.attach(`pidof victim`.to_i)
puts process.read('heap', 4, as: :u64).map { |c| '0x%016x' % c }
# 0x0000000000000000
# 0x0000000000000021
# 0x00000000deadbeef
# 0x0000000000000000
#=> nil

process.read('heap+0x10', 4, as: :u8).map { |c| '0x%x' % c }
#=> ['0xef', '0xbe', '0xad', '0xde']

process.read('libc', 4)
#=> "\x7fELF"

Write Process's Memory

require 'memory_io'

process = MemoryIO.attach('self') # Hack! Write memory of this process directly!
string = 'A' * 16
pos = string.object_id * 2 + 16
process.read(pos, 16)
#=> 'AAAAAAAAAAAAAAAA'

process.write(pos, 'memory_changed!!')
string
#=> 'memory_changed!!'

Customize Read

require 'memory_io'
process = MemoryIO.attach(`pidof victim`.to_i)

# An example that read a chunk of pt-malloc.
read_chunk = lambda do |stream|
  _prev_size = stream.read(8)
  size = (stream.read(8).unpack('Q').first & -16) - 8
  [size, stream.read(size)]
end
process.read('heap', 1, as: read_chunk)
#=> [24, "\xef\xbe\xad\xde\x00\x00...\x00"]

Define Own Structure

require 'memory_io'
process = MemoryIO.attach(`pidof victim`.to_i)

class MyType < MemoryIO::Types::Type
  def self.read(stream)
    self.new(stream.read(1))
  end

  # Define this if you need to 'write' to memory
  def self.write(stream, my_type)
    stream.write(my_type.val)
  end

  attr_accessor :val
  def initialize(val)
    @val = val
  end
end

# Use snake-case symbol.
process.read('libc', 4, as: :my_type)
#=> [#<MyType @val="\x7F">,
# #<MyType @val="E">,
# #<MyType @val="L">,
# #<MyType @val="F">]

process.write('libc', MyType.new('MEOW'), as: :my_type)

# See if memory changed
process.read('libc', 4)
#=> 'MEOW'

Developing

To Add a New Structure

TBA