memory_io 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +39 -0
- data/lib/memory_io/io.rb +126 -0
- data/lib/memory_io/process.rb +113 -0
- data/lib/memory_io/types/c_str.rb +34 -0
- data/lib/memory_io/types/number.rb +60 -0
- data/lib/memory_io/types/type.rb +37 -0
- data/lib/memory_io/types/types.rb +56 -0
- data/lib/memory_io/util.rb +101 -0
- data/lib/memory_io/version.rb +4 -0
- data/lib/memory_io.rb +21 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 23fd90a4ba0906c5264a4149f8992c1ca22ed251
|
4
|
+
data.tar.gz: d6414406dac7f68362f6d1aafb0d31966f8e21b0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc05ccbf5888b182138ff6112f8979e48875a8f3ab68730bf72e3cb5391e9205c32d7494e2d792196eda735eeac7e851e45fadcd54cdf51b9624d779b85e6492
|
7
|
+
data.tar.gz: b7ee99af0626e2486f8fab0da2b3c2bef47d877396a796118fc8ae8b4bfa456df609b0703d4da400b5a147e66a041ae557d69df2212b181f9d073353cf0e4bf6
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
[](https://travis-ci.org/david942j/memory_io)
|
2
|
+
[](https://badge.fury.io/rb/memory_io)
|
3
|
+
[](https://codeclimate.com/github/david942j/memory_io/maintainability)
|
4
|
+
[](https://codeclimate.com/github/david942j/memory_io/test_coverage)
|
5
|
+
[](https://inch-ci.org/github/david942j/memory_io)
|
6
|
+
[](http://choosealicense.com/licenses/mit/)
|
7
|
+
|
8
|
+
# Memory IO
|
9
|
+
|
10
|
+
Read/Write complicated structures in memory easily.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Available on RubyGems.org!
|
15
|
+
|
16
|
+
```bash
|
17
|
+
$ gem install memory_io
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Read Process's Memory
|
23
|
+
```
|
24
|
+
require 'memory_io'
|
25
|
+
|
26
|
+
process = MemoryIO.attach(`pidof victim`.to_i)
|
27
|
+
puts process.read('heap', 4, as: :u64).map { |c| '0x%016x' % c }
|
28
|
+
# 0x0000000000000000
|
29
|
+
# 0x0000000000000021
|
30
|
+
# 0x00000000deadbeef
|
31
|
+
# 0x0000000000000000
|
32
|
+
#=> nil
|
33
|
+
|
34
|
+
process.read('heap+0x10', 4, as: :u8).map { |c| '0x%x' % c }
|
35
|
+
#=> ['0xef', '0xbe', '0xad', '0xde']
|
36
|
+
|
37
|
+
process.read('libc', 4)
|
38
|
+
#=> "\x7fELF"
|
39
|
+
```
|
data/lib/memory_io/io.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'memory_io/types/types'
|
2
|
+
|
3
|
+
module MemoryIO
|
4
|
+
# Main class to use {MemoryIO}.
|
5
|
+
class IO
|
6
|
+
attr_reader :stream # @return [#pos=, #read, #write]
|
7
|
+
|
8
|
+
# Instantiate an {IO} object.
|
9
|
+
#
|
10
|
+
# @param [#pos=, #read, #write] stream
|
11
|
+
# The file-like object to be read/written.
|
12
|
+
# +file+ can be unwritable if you will not invoke any write-related method.
|
13
|
+
#
|
14
|
+
# If +stream.read(*)+ returns empty string or +nil+, it would be seen as reaching EOF.
|
15
|
+
def initialize(stream)
|
16
|
+
@stream = stream
|
17
|
+
end
|
18
|
+
|
19
|
+
# Read and convert result into custom type/stucture.
|
20
|
+
#
|
21
|
+
# @param [Integer] num_elements
|
22
|
+
# Number of elements to be read.
|
23
|
+
# This parameter must be positive and larger than zero.
|
24
|
+
#
|
25
|
+
# This parameter may effect the return type,
|
26
|
+
# see documents of return value.
|
27
|
+
# @param [Integer?] from
|
28
|
+
# Invoke +stream.pos = from+ before starting to read.
|
29
|
+
# +nil+ for not changing current position of stream.
|
30
|
+
# @param [nil, Symbol, MemoryIO::Types, Proc] as
|
31
|
+
# Decide the type/structure when reading.
|
32
|
+
# See {MemoryIO::Types} for all supported types.
|
33
|
+
#
|
34
|
+
# A +Proc+ is allowed, which should accept +stream+ as the first argument.
|
35
|
+
# The return value of the proc would be the return objects of this method.
|
36
|
+
#
|
37
|
+
# If +nil+ is given, this method returns a string and has same behavior as +::IO#read+.
|
38
|
+
# @param [Boolean] force_array
|
39
|
+
# When +num_elements+ equals to 1, the read +Object+ would be returned.
|
40
|
+
# Pass +true+ to this parameter to force this method returning an array.
|
41
|
+
#
|
42
|
+
# @return [String, Object, Array<Object>]
|
43
|
+
# There're multiple possible return types,
|
44
|
+
# which dependes on the value of parameter +num_elements+, +as+, and +force_array+.
|
45
|
+
#
|
46
|
+
# See examples for clear usage. The rule of return type is listed as following:
|
47
|
+
#
|
48
|
+
# * +as = nil+:
|
49
|
+
# A +String+ with length +num_elements+ is returned.
|
50
|
+
# * +as != nil+ and +num_elements = 1+ and +force_array = false+:
|
51
|
+
# An +Object+ is returned. The type of +Object+ depends on parameter +as+.
|
52
|
+
# * +as != nil+ and +num_elements = 1+ and +force_array = true+:
|
53
|
+
# An array with one element is returned.
|
54
|
+
# * +as != nil+ and +num_elements > 1+:
|
55
|
+
# An array with length +num_elements+ is returned.
|
56
|
+
#
|
57
|
+
# If EOF is occured, object(s) read will be returned.
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# stream = StringIO.new('A' * 8 + 'B' * 8)
|
61
|
+
# io = MemoryIO.new(stream)
|
62
|
+
# io.read(9)
|
63
|
+
# #=> "AAAAAAAAB"
|
64
|
+
# io.read(100)
|
65
|
+
# #=> "BBBBBBB"
|
66
|
+
#
|
67
|
+
# # read two unsigned 32-bit integers starts from posistion 4
|
68
|
+
# io.read(2, from: 4, as: :u32)
|
69
|
+
# #=> [1094795585, 1111638594] # [0x41414141, 0x42424242]
|
70
|
+
#
|
71
|
+
# io.read(1, as: :u16)
|
72
|
+
# #=> 16962 # 0x4242
|
73
|
+
# io.read(1, as: :u16, force_array: true)
|
74
|
+
# #=> [16962]
|
75
|
+
# @example
|
76
|
+
# stream = StringIO.new("\xef\xbe\xad\xde")
|
77
|
+
# io = MemoryIO.new(stream)
|
78
|
+
# io.read(1, as: :u32)
|
79
|
+
# #=> 3735928559
|
80
|
+
# io.rewind
|
81
|
+
# io.read(1, as: :s32)
|
82
|
+
# #=> -559038737
|
83
|
+
# @example
|
84
|
+
# stream = StringIO.new("123\x0045678\x00")
|
85
|
+
# io = MemoryIO.new(stream)
|
86
|
+
# io.read(2, as: :c_str)
|
87
|
+
# #=> ["123", "45678"]
|
88
|
+
# @example
|
89
|
+
# # pass lambda to `as`
|
90
|
+
# stream = StringIO.new("\x03123\x044567")
|
91
|
+
# io = MemoryIO.new(stream)
|
92
|
+
# io.read(2, as: lambda { |stream| stream.read(stream.read(1).ord) })
|
93
|
+
# #=> ["123", "4567"]
|
94
|
+
#
|
95
|
+
# @note
|
96
|
+
# This method's arguments and return value are different with +::IO#read+.
|
97
|
+
# Check documents and examples.
|
98
|
+
def read(num_elements, from: nil, as: nil, force_array: false)
|
99
|
+
stream.pos = from if from
|
100
|
+
return stream.read(num_elements) if as.nil?
|
101
|
+
conv = to_proc(as, :read)
|
102
|
+
# TODO: handle eof
|
103
|
+
ret = Array.new(num_elements) { conv.call(stream) }
|
104
|
+
ret = ret.first if num_elements == 1 && !force_array
|
105
|
+
ret
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set +stream+ to the beginning.
|
109
|
+
# i.e. invoke +stream.pos = 0+.
|
110
|
+
#
|
111
|
+
# @return [0]
|
112
|
+
def rewind
|
113
|
+
stream.pos = 0
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def to_proc(as, rw)
|
119
|
+
ret = as.respond_to?(:call) ? as : MemoryIO::Types.get_proc(as, rw)
|
120
|
+
raise ArgumentError, <<-EOERR.strip unless ret.respond_to?(:call)
|
121
|
+
Invalid argument `as`: #{as.inspect}. It should be either a Proc or a supported type of MemoryIO::Types.
|
122
|
+
EOERR
|
123
|
+
ret
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module MemoryIO
|
2
|
+
# Records information of a process.
|
3
|
+
class Process
|
4
|
+
# @api private
|
5
|
+
# @return [#readable?, #writable?]
|
6
|
+
attr_reader :perm
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
#
|
10
|
+
# Create process object from pid.
|
11
|
+
#
|
12
|
+
# @param [Integer] pid
|
13
|
+
# Process id.
|
14
|
+
#
|
15
|
+
# @note
|
16
|
+
# This class only supports procfs-based system. i.e. /proc is mounted and readable.
|
17
|
+
#
|
18
|
+
# @todo
|
19
|
+
# Support MacOS
|
20
|
+
# @todo
|
21
|
+
# Support Windows
|
22
|
+
def initialize(pid)
|
23
|
+
@pid = pid
|
24
|
+
@mem = "/proc/#{pid}/mem"
|
25
|
+
# check permission of '/proc/pid/mem'
|
26
|
+
@perm = MemoryIO::Util.file_permission(@mem)
|
27
|
+
# TODO: raise custom exception
|
28
|
+
raise Errno::ENOENT, @mem if perm.nil?
|
29
|
+
# FIXME: use logger
|
30
|
+
warn(<<-EOS.strip) unless perm.readable? || perm.writable?
|
31
|
+
You have no permission to read/write this process.
|
32
|
+
|
33
|
+
Check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
|
34
|
+
again as the root user.
|
35
|
+
|
36
|
+
To enable attach another process, do:
|
37
|
+
|
38
|
+
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
39
|
+
EOS
|
40
|
+
end
|
41
|
+
|
42
|
+
# Parse +/proc/[pid]/maps+ to get all bases.
|
43
|
+
#
|
44
|
+
# @return [{Symbol => Integer}]
|
45
|
+
# Hash of bases.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# process = Process.new(`pidof victim`.to_i)
|
49
|
+
# puts process.bases.map { |key, val| format('%s: 0x%016x', key, val) }
|
50
|
+
# # vsyscall: 0xffffffffff600000
|
51
|
+
# # vdso: 0x00007ffd5b565000
|
52
|
+
# # vvar: 0x00007ffd5b563000
|
53
|
+
# # stack: 0x00007ffd5ad21000
|
54
|
+
# # ld: 0x00007f339a69b000
|
55
|
+
# # libc: 0x00007f33996f1000
|
56
|
+
# # heap: 0x00005571994a1000
|
57
|
+
# # victim: 0x0000557198bcb000
|
58
|
+
# #=> nil
|
59
|
+
def bases
|
60
|
+
file = "/proc/#{@pid}/maps"
|
61
|
+
stat = MemoryIO::Util.file_permission(file)
|
62
|
+
return {} unless stat && stat.readable?
|
63
|
+
maps = ::IO.binread(file).split("\n").map do |line|
|
64
|
+
# 7f76515cf000-7f76515da000 r-xp 00000000 fd:01 29360257 /lib/x86_64-linux-gnu/libnss_files-2.24.so
|
65
|
+
addr, _perm, _offset, _dev, _inode, pathname = line.strip.split(' ', 6)
|
66
|
+
next nil if pathname.nil?
|
67
|
+
addr = addr.to_i(16)
|
68
|
+
pathname = pathname[1..-2] if pathname =~ /^\[.+\]$/
|
69
|
+
pathname = ::File.basename(pathname)
|
70
|
+
[MemoryIO::Util.trim_libname(pathname).to_sym, addr]
|
71
|
+
end
|
72
|
+
maps.compact.reverse.to_h
|
73
|
+
end
|
74
|
+
|
75
|
+
# Read from process's memory.
|
76
|
+
#
|
77
|
+
# This method has *almost* same arguements and return types as {IO#read}.
|
78
|
+
# The only difference is this method needs parameter +addr+ (which
|
79
|
+
# will be passed to paramter +from+ in {IO#reada}).
|
80
|
+
#
|
81
|
+
# @param [Integer, String] addr
|
82
|
+
# The address start to read.
|
83
|
+
# When String is given, it will be safe-evaluated.
|
84
|
+
# You can use variables such as +'heap'/'stack'/'libc'+ in this parameter.
|
85
|
+
# See examples.
|
86
|
+
# @param [Integer] num_elements
|
87
|
+
# Number of elements to read. See {IO#read}.
|
88
|
+
#
|
89
|
+
# @return [String, Object, Array<Object>]
|
90
|
+
# See {IO#read}.
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# process = MemoryIO.attach(`pidof victim`.to_i)
|
94
|
+
# puts process.read('heap', 4, as: :u64).map { |c| '0x%016x' % c }
|
95
|
+
# # 0x0000000000000000
|
96
|
+
# # 0x0000000000000021
|
97
|
+
# # 0x00000000deadbeef
|
98
|
+
# # 0x0000000000000000
|
99
|
+
# #=> nil
|
100
|
+
# process.read('heap+0x10', 4, as: :u8).map { |c| '0x%x' % c }
|
101
|
+
# #=> ['0xef', '0xbe', '0xad', '0xde']
|
102
|
+
#
|
103
|
+
# process.read('libc', 4)
|
104
|
+
# #=> "\x7fELF"
|
105
|
+
# @see IO#read
|
106
|
+
def read(addr, num_elements, **options)
|
107
|
+
addr = MemoryIO::Util.safe_eval(addr, bases)
|
108
|
+
File.open(@mem, 'rb') do |f|
|
109
|
+
MemoryIO::IO.new(f).read(num_elements, from: addr, **options)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require 'memory_io/types/type'
|
4
|
+
|
5
|
+
module MemoryIO
|
6
|
+
module Types
|
7
|
+
# Read a null-terminated string.
|
8
|
+
class CStr < Types::Type
|
9
|
+
# @api private
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
# String without null byte.
|
13
|
+
def self.read(stream)
|
14
|
+
ret = ''
|
15
|
+
loop do
|
16
|
+
c = stream.read(1)
|
17
|
+
break if c.nil? || c == '' || c == "\x00"
|
18
|
+
ret << c
|
19
|
+
end
|
20
|
+
ret
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
# @param [String] val
|
26
|
+
# A null byte would be appended if +val+ not ends with null byte.
|
27
|
+
def self.write(stream, val)
|
28
|
+
val = val.to_s
|
29
|
+
val << "\x00" unless val.end_with?("\x00")
|
30
|
+
stream.write(val)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'memory_io/types/type'
|
2
|
+
|
3
|
+
module MemoryIO
|
4
|
+
module Types
|
5
|
+
# @api private
|
6
|
+
# Register numbers to {Types}.
|
7
|
+
#
|
8
|
+
# All types registerd by this class are assumed as *little endian*.
|
9
|
+
class Number
|
10
|
+
# @param [Integer] bytes
|
11
|
+
# Bytes.
|
12
|
+
# @param [Boolean] signed
|
13
|
+
# Signed or unsigned.
|
14
|
+
# @param [String] pack_str
|
15
|
+
# The indicator to be passed to +Array#pack+ and +String#unpack+.
|
16
|
+
def initialize(bytes, signed, pack_str)
|
17
|
+
@bytes = bytes
|
18
|
+
@signed = signed
|
19
|
+
@pack_str = pack_str
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Integer]
|
23
|
+
def read(stream)
|
24
|
+
unpack(stream.read(@bytes))
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [Integer] val
|
28
|
+
def write(stream, val)
|
29
|
+
stream.write(pack(val))
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def unpack(str)
|
35
|
+
val = str.unpack(@pack_str).first
|
36
|
+
val -= (2**@bytes) if @signed && val >= (2**(@bytes - 1))
|
37
|
+
val
|
38
|
+
end
|
39
|
+
|
40
|
+
def pack(val)
|
41
|
+
[val].pack(@pack_str)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Register (un)signed n-bites integers.
|
45
|
+
{
|
46
|
+
8 => 'C',
|
47
|
+
16 => 'S',
|
48
|
+
32 => 'I',
|
49
|
+
64 => 'Q'
|
50
|
+
}.each do |t, c|
|
51
|
+
Type.register("s#{t}".to_sym, Number.new(t, true, c))
|
52
|
+
Type.register("u#{t}".to_sym, Number.new(t, false, c))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Register floating numbers.
|
56
|
+
Type.register(:float, Number.new(4, false, 'F'))
|
57
|
+
Type.register(:double, Number.new(8, false, 'D'))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MemoryIO
|
2
|
+
module Types
|
3
|
+
# The base class, all descendents of this class would be consider as a valid 'type'.
|
4
|
+
class Type
|
5
|
+
class << self
|
6
|
+
# Find the subclass of {Type} by symbol.
|
7
|
+
#
|
8
|
+
# @param [Symbol] symbol
|
9
|
+
# Symbol to be find.
|
10
|
+
#
|
11
|
+
# @return [#read, #write]
|
12
|
+
# The object that registered in {.register}.
|
13
|
+
def find(symbol)
|
14
|
+
@map[symbol]
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
# @param [Symbol] symbol
|
20
|
+
# Symbol name that used for searching.
|
21
|
+
# @param [#read, #write] klass
|
22
|
+
# Normally, +klass+ is a descendent of {Type}.
|
23
|
+
#
|
24
|
+
# @return [void]
|
25
|
+
def register(symbol, klass)
|
26
|
+
@map ||= {}
|
27
|
+
@map[symbol] = klass
|
28
|
+
end
|
29
|
+
|
30
|
+
# TO record descendants.
|
31
|
+
def inherited(klass)
|
32
|
+
register(MemoryIO::Util.underscore(klass.name).to_sym, klass)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'memory_io/types/type'
|
2
|
+
require 'memory_io/util'
|
3
|
+
|
4
|
+
require 'memory_io/types/c_str'
|
5
|
+
require 'memory_io/types/number'
|
6
|
+
|
7
|
+
module MemoryIO
|
8
|
+
# Module that includes multiple types.
|
9
|
+
#
|
10
|
+
# Supported types are all descendants of {Types::Type}.
|
11
|
+
module Types
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
# Returns the class whose name matches +name+.
|
17
|
+
#
|
18
|
+
# This method would search all descendants of {Types::Type}.
|
19
|
+
#
|
20
|
+
# @return [Symbol] name
|
21
|
+
# Class name to be searched.
|
22
|
+
#
|
23
|
+
# @return [Class?]
|
24
|
+
# The class.
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# Types.find(:u64)
|
28
|
+
# #=> MemoryIO::Types::U64
|
29
|
+
#
|
30
|
+
# Types.find(:c_str)
|
31
|
+
# #=> MemoryIO::Types::CStr
|
32
|
+
def find(name)
|
33
|
+
Types::Type.find(Util.underscore(name.to_s).to_sym)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns a callable object according to +name+.
|
37
|
+
#
|
38
|
+
# @param [Symbol] name
|
39
|
+
# The name of type.
|
40
|
+
# @param [:read, :write] rw
|
41
|
+
# Read or write?
|
42
|
+
#
|
43
|
+
# @return [Proc?]
|
44
|
+
# The proc that accepts +stream+ as the first parameter.
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# Types.get_proc(:c_str, :write)
|
48
|
+
# #=> #<Method: MemoryIO::Types::CStr.write>
|
49
|
+
# Types.get_proc(:s32, :read)
|
50
|
+
# #=> #<Method: MemoryIO::Types::Number#read>
|
51
|
+
def get_proc(name, rw)
|
52
|
+
klass = find(name)
|
53
|
+
klass && klass.method(rw)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'dentaku'
|
3
|
+
|
4
|
+
module MemoryIO
|
5
|
+
# Defines utility methods.
|
6
|
+
module Util
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# Convert input into snake-case.
|
10
|
+
#
|
11
|
+
# This method also removes strings before +'::'+ in +str+.
|
12
|
+
#
|
13
|
+
# @param [String] str
|
14
|
+
# String to be converted.
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
# Converted string.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# Util.underscore('MemoryIO')
|
21
|
+
# #=> 'memory_io'
|
22
|
+
#
|
23
|
+
# Util.underscore('MyModule::MyClass')
|
24
|
+
# #=> 'my_class'
|
25
|
+
def underscore(str)
|
26
|
+
return '' if str.empty?
|
27
|
+
str = str.split('::').last
|
28
|
+
str.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
29
|
+
str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
30
|
+
str.downcase!
|
31
|
+
str
|
32
|
+
end
|
33
|
+
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
# @param [String] file
|
37
|
+
# File name.
|
38
|
+
#
|
39
|
+
# @return [#readable?, #writable?, nil]
|
40
|
+
# Struct with two boolean method.
|
41
|
+
# +nil+ for file not exists or is inaccessible.
|
42
|
+
def file_permission(file)
|
43
|
+
return nil unless File.file?(file)
|
44
|
+
stat = File.stat(file)
|
45
|
+
# we do a trick here because /proc/[pid]/mem might be marked as readable but fails at sysopen.
|
46
|
+
os = OpenStruct.new(readable?: stat.readable_real?, writable?: stat.writable_real?)
|
47
|
+
begin
|
48
|
+
os.readable? && File.open(file, 'rb').close
|
49
|
+
rescue Errno::EACCES
|
50
|
+
os[:readable?] = false
|
51
|
+
end
|
52
|
+
begin
|
53
|
+
os.writable? && File.open(file, 'wb').close
|
54
|
+
rescue Errno::EACCES
|
55
|
+
os[:writable?] = false
|
56
|
+
end
|
57
|
+
os
|
58
|
+
end
|
59
|
+
|
60
|
+
# Evaluate string safely.
|
61
|
+
#
|
62
|
+
# @param [String] str
|
63
|
+
# String to be evaluated.
|
64
|
+
# @param [{Symbol => Integer}] vars
|
65
|
+
# Predefined variables
|
66
|
+
#
|
67
|
+
# @return [Integer]
|
68
|
+
# Result.
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# Util.safe_eval('heap + 0x10 * pp', heap: 0xde00, pp: 8)
|
72
|
+
# #=> 56960 # 0xde80
|
73
|
+
def safe_eval(str, **vars)
|
74
|
+
return str if str.is_a?(Integer)
|
75
|
+
# dentaku 2 doesn't support hex
|
76
|
+
str = str.gsub(/0x[0-9a-zA-Z]+/) { |c| c.to_i(16) }
|
77
|
+
Dentaku::Calculator.new.store(vars).evaluate(str)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Remove extension name (.so) and version in library name.
|
81
|
+
#
|
82
|
+
# @param [String] name
|
83
|
+
# Original library filename.
|
84
|
+
#
|
85
|
+
# @return [String]
|
86
|
+
# Name without version and '.so'.
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
# Util.trim_libname('libc-2.24.so')
|
90
|
+
# #=> 'libc'
|
91
|
+
# Util.trim_libname('libcrypto.so.1.0.0')
|
92
|
+
# #=> 'libcrypto'
|
93
|
+
# Util.trim_libname('not_a_so')
|
94
|
+
# #=> 'not_a_so'
|
95
|
+
def trim_libname(name)
|
96
|
+
type1 = '(-[\d.]+)?\.so$'
|
97
|
+
type2 = '\.so.\d+[\d.]+$'
|
98
|
+
name.sub(/#{type1}|#{type2}/, '')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/memory_io.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# MemoryIO - Read/Write structures in memory.
|
2
|
+
#
|
3
|
+
# @author david942j
|
4
|
+
module MemoryIO
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Get a process by process id.
|
8
|
+
#
|
9
|
+
# @param [Integer] pid
|
10
|
+
# Process Id in linux.
|
11
|
+
#
|
12
|
+
# @return [MemoryIO::Process]
|
13
|
+
# A process object for further usage.
|
14
|
+
def attach(pid)
|
15
|
+
MemoryIO::Process.new(pid)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'memory_io/io'
|
20
|
+
require 'memory_io/process'
|
21
|
+
require 'memory_io/version'
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: memory_io
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- david942j
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-01-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dentaku
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.11
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.0.11
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: codeclimate-test-reporter
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.6'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.6'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '12.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '12.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rspec
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '3.5'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '3.5'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rubocop
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.52'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.52'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: simplecov
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 0.13.0
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.13.0
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: yard
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.9'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0.9'
|
117
|
+
description: ''
|
118
|
+
email:
|
119
|
+
- david942j@gmail.com
|
120
|
+
executables: []
|
121
|
+
extensions: []
|
122
|
+
extra_rdoc_files: []
|
123
|
+
files:
|
124
|
+
- README.md
|
125
|
+
- lib/memory_io.rb
|
126
|
+
- lib/memory_io/io.rb
|
127
|
+
- lib/memory_io/process.rb
|
128
|
+
- lib/memory_io/types/c_str.rb
|
129
|
+
- lib/memory_io/types/number.rb
|
130
|
+
- lib/memory_io/types/type.rb
|
131
|
+
- lib/memory_io/types/types.rb
|
132
|
+
- lib/memory_io/util.rb
|
133
|
+
- lib/memory_io/version.rb
|
134
|
+
homepage: https://github.com/david942j/memory_io
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 2.1.0
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 2.6.14
|
155
|
+
signing_key:
|
156
|
+
specification_version: 4
|
157
|
+
summary: memory_io
|
158
|
+
test_files: []
|