memory_io 0.1.1 → 0.2.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 +5 -5
- data/README.md +3 -3
- data/lib/memory_io.rb +2 -0
- data/lib/memory_io/io.rb +9 -4
- data/lib/memory_io/process.rb +8 -3
- data/lib/memory_io/types/basic/number.rb +3 -1
- data/lib/memory_io/types/clang/c_str.rb +5 -2
- data/lib/memory_io/types/cpp/string.rb +3 -0
- data/lib/memory_io/types/record.rb +6 -0
- data/lib/memory_io/types/type.rb +7 -2
- data/lib/memory_io/types/types.rb +4 -2
- data/lib/memory_io/util.rb +5 -0
- data/lib/memory_io/version.rb +3 -1
- metadata +13 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3de5ed7bf7e1f8543bbb290ed472f008ed9d8f4c1af032a11831bd904378c11e
|
4
|
+
data.tar.gz: e99b29afb7cdcc7941bb6249bbe453cea2cb3a6cb6eab72a8965bfa5ef9802e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4d3d9e60c649dd8d4e6f3c3dfe3bb777d2a7ffd2065699e3f86fef27f9ddf079b90acc7d7a5be244301d45ba46d9c713b950bbaa7a113843631287fb1e9653f
|
7
|
+
data.tar.gz: 2246f04654d036e8df50440cf2c3d9ebe50933921f5720bf5f69308648ab34849d869b3969f581579dba8930ae1fbd0e5a1d6e27246900129cef39212e2876bf
|
data/README.md
CHANGED
@@ -17,9 +17,9 @@ However, gdb doesn't support writing Ruby scripts
|
|
17
17
|
(unless you use [gdb-ruby](https://github.com/david942j/gdb-ruby), which has dependency of **MemoryIO**).
|
18
18
|
So I create this repo and want to make the debug procedure much easier.
|
19
19
|
|
20
|
-
This
|
20
|
+
This repository has two main goals:
|
21
21
|
|
22
|
-
1. To
|
22
|
+
1. To communicate with memory easily.
|
23
23
|
2. To collect all common structures for debugging/learning.
|
24
24
|
|
25
25
|
## Why
|
@@ -51,7 +51,7 @@ process.read(0x601000, 1, as: 'basic/u64')
|
|
51
51
|
process.read(0x601000, 1, as: :u64)
|
52
52
|
```
|
53
53
|
|
54
|
-
|
54
|
+
Go to [the online document](http://www.rubydoc.info/github/david942j/memory_io/master/MemoryIO/Types) for more details
|
55
55
|
of each type.
|
56
56
|
|
57
57
|
### BASIC
|
data/lib/memory_io.rb
CHANGED
data/lib/memory_io/io.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'memory_io/types/types'
|
4
5
|
|
@@ -11,14 +12,14 @@ module MemoryIO
|
|
11
12
|
#
|
12
13
|
# @param [#pos, #pos=, #read, #write] stream
|
13
14
|
# The file-like object to be read/written.
|
14
|
-
# +file+ can be
|
15
|
+
# +file+ can be un-writable if you will not invoke any write-related method.
|
15
16
|
#
|
16
17
|
# If +stream.read(*)+ returns empty string or +nil+, it would be seen as reaching EOF.
|
17
18
|
def initialize(stream)
|
18
19
|
@stream = stream
|
19
20
|
end
|
20
21
|
|
21
|
-
# Read and convert result into custom type/
|
22
|
+
# Read and convert result into custom type/structure.
|
22
23
|
#
|
23
24
|
# @param [Integer] num_elements
|
24
25
|
# Number of elements to be read.
|
@@ -43,7 +44,7 @@ module MemoryIO
|
|
43
44
|
#
|
44
45
|
# @return [String, Object, Array<Object>]
|
45
46
|
# There're multiple possible return types,
|
46
|
-
# which
|
47
|
+
# which depends on the value of parameter +num_elements+, +as+, and +force_array+.
|
47
48
|
#
|
48
49
|
# See examples for clear usage. The rule of return type is listed as following:
|
49
50
|
#
|
@@ -102,6 +103,7 @@ module MemoryIO
|
|
102
103
|
def read(num_elements, from: nil, as: nil, force_array: false)
|
103
104
|
stream.pos = from if from
|
104
105
|
return stream.read(num_elements) if as.nil?
|
106
|
+
|
105
107
|
conv = to_proc(as, :read)
|
106
108
|
# TODO: handle eof
|
107
109
|
ret = Array.new(num_elements) { conv.call(stream) }
|
@@ -123,7 +125,7 @@ module MemoryIO
|
|
123
125
|
#
|
124
126
|
# A +Proc+ is allowed, which should accept +stream+ and one object as arguments.
|
125
127
|
#
|
126
|
-
# If +objects+ is a
|
128
|
+
# If +objects+ is a descendant instance of {Types::Type} and +as+ is +nil,
|
127
129
|
# +objects.class+ will be used for +as+.
|
128
130
|
# Otherwise, when +as = nil+, this method will simply call +stream.write(objects)+.
|
129
131
|
#
|
@@ -163,6 +165,7 @@ module MemoryIO
|
|
163
165
|
stream.pos = from if from
|
164
166
|
as ||= objects.class if objects.class.ancestors.include?(MemoryIO::Types::Type)
|
165
167
|
return stream.write(objects) if as.nil?
|
168
|
+
|
166
169
|
conv = to_proc(as, :write)
|
167
170
|
Array(objects).map { |o| conv.call(stream, o) }
|
168
171
|
end
|
@@ -182,8 +185,10 @@ module MemoryIO
|
|
182
185
|
ret = as.respond_to?(rw) ? as.method(rw) : as
|
183
186
|
ret = ret.respond_to?(:call) ? ret : MemoryIO::Types.get_proc(ret, rw)
|
184
187
|
raise ArgumentError, <<-EOERR.strip unless ret.respond_to?(:call)
|
188
|
+
|
185
189
|
Invalid argument `as`: #{as.inspect}. It should be either a Proc or a supported type of MemoryIO::Types.
|
186
190
|
EOERR
|
191
|
+
|
187
192
|
ret
|
188
193
|
end
|
189
194
|
end
|
data/lib/memory_io/process.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MemoryIO
|
2
4
|
# Records information of a process.
|
3
5
|
class Process
|
@@ -26,6 +28,7 @@ module MemoryIO
|
|
26
28
|
@perm = MemoryIO::Util.file_permission(@mem)
|
27
29
|
# TODO: raise custom exception
|
28
30
|
raise Errno::ENOENT, @mem if perm.nil?
|
31
|
+
|
29
32
|
# FIXME: use logger
|
30
33
|
warn(<<-EOS.strip) unless perm.readable? || perm.writable?
|
31
34
|
You have no permission to read/write this process.
|
@@ -59,11 +62,13 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
|
59
62
|
def bases
|
60
63
|
file = "/proc/#{@pid}/maps"
|
61
64
|
stat = MemoryIO::Util.file_permission(file)
|
62
|
-
return {} unless stat
|
65
|
+
return {} unless stat&.readable?
|
66
|
+
|
63
67
|
maps = ::IO.binread(file).split("\n").map do |line|
|
64
68
|
# 7f76515cf000-7f76515da000 r-xp 00000000 fd:01 29360257 /lib/x86_64-linux-gnu/libnss_files-2.24.so
|
65
69
|
addr, _perm, _offset, _dev, _inode, pathname = line.strip.split(' ', 6)
|
66
70
|
next nil if pathname.nil?
|
71
|
+
|
67
72
|
addr = addr.to_i(16)
|
68
73
|
pathname = pathname[1..-2] if pathname =~ /^\[.+\]$/
|
69
74
|
pathname = ::File.basename(pathname)
|
@@ -104,7 +109,7 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
|
104
109
|
# #=> "\x7fELF"
|
105
110
|
# @see IO#read
|
106
111
|
def read(addr, num_elements, **options)
|
107
|
-
mem_io(:read) { |io| io.read(num_elements, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
|
112
|
+
mem_io(:read) { |io| io.read(num_elements, from: MemoryIO::Util.safe_eval(addr, **bases), **options) }
|
108
113
|
end
|
109
114
|
|
110
115
|
# Write objects at +addr+.
|
@@ -128,7 +133,7 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
|
128
133
|
# #=> 'BBBBCCCCAAAAAAAA'
|
129
134
|
# @see IO#write
|
130
135
|
def write(addr, objects, **options)
|
131
|
-
mem_io(:write) { |io| io.write(objects, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
|
136
|
+
mem_io(:write) { |io| io.write(objects, from: MemoryIO::Util.safe_eval(addr, **bases), **options) }
|
132
137
|
end
|
133
138
|
|
134
139
|
private
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'memory_io/types/type'
|
2
4
|
|
3
5
|
module MemoryIO
|
@@ -6,7 +8,7 @@ module MemoryIO
|
|
6
8
|
module Basic
|
7
9
|
# Register numbers to {Types}.
|
8
10
|
#
|
9
|
-
# All types
|
11
|
+
# All types registered by this class are assumed as *little endian*.
|
10
12
|
#
|
11
13
|
# This class registered (un)signed {8, 16, 32, 64)-bit integers and IEEE-754 floating numbers.
|
12
14
|
class Number
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'memory_io/types/type'
|
4
5
|
|
@@ -10,15 +11,17 @@ module MemoryIO
|
|
10
11
|
module Clang
|
11
12
|
# A null-terminated string.
|
12
13
|
class CStr < Types::Type
|
14
|
+
|
13
15
|
# @api private
|
14
16
|
#
|
15
17
|
# @return [String]
|
16
18
|
# String without null byte.
|
17
19
|
def self.read(stream)
|
18
|
-
ret = ''
|
20
|
+
ret = +''
|
19
21
|
loop do
|
20
22
|
c = stream.read(1)
|
21
23
|
break if c.nil? || c == '' || c == "\x00"
|
24
|
+
|
22
25
|
ret << c
|
23
26
|
end
|
24
27
|
ret
|
@@ -30,7 +33,7 @@ module MemoryIO
|
|
30
33
|
# A null byte would be appended if +val+ not ends with null byte.
|
31
34
|
def self.write(stream, val)
|
32
35
|
val = val.to_s
|
33
|
-
val
|
36
|
+
val += "\x00" unless val.end_with?("\x00")
|
34
37
|
stream.write(val)
|
35
38
|
end
|
36
39
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'memory_io/types/type'
|
2
4
|
|
3
5
|
module MemoryIO
|
@@ -16,6 +18,7 @@ module MemoryIO
|
|
16
18
|
# }
|
17
19
|
# };
|
18
20
|
class String < MemoryIO::Types::Type
|
21
|
+
|
19
22
|
# std::string uses inlined-buffer if string length isn't larger than {LOCAL_CAPACITY}.
|
20
23
|
LOCAL_CAPACITY = 15
|
21
24
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MemoryIO
|
2
4
|
module Types
|
3
5
|
# @api private
|
@@ -39,6 +41,7 @@ module MemoryIO
|
|
39
41
|
def doc
|
40
42
|
return @force_doc if @force_doc
|
41
43
|
return '' unless @caller
|
44
|
+
|
42
45
|
parse_file_doc(@caller.absolute_path, @caller.lineno)
|
43
46
|
end
|
44
47
|
|
@@ -47,13 +50,16 @@ module MemoryIO
|
|
47
50
|
# @return [String]
|
48
51
|
def parse_file_doc(file, lineno)
|
49
52
|
return '' unless ::File.file?(file)
|
53
|
+
|
50
54
|
strings = []
|
51
55
|
lines = ::IO.binread(file).split("\n")
|
52
56
|
(lineno - 1).downto(1) do |no|
|
53
57
|
str = lines[no - 1]
|
54
58
|
break if str.nil?
|
59
|
+
|
55
60
|
str.strip!
|
56
61
|
break unless str.start_with?('#')
|
62
|
+
|
57
63
|
strings.unshift(str[2..-1] || '')
|
58
64
|
end
|
59
65
|
trim_docstring(strings)
|
data/lib/memory_io/types/type.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ostruct'
|
2
4
|
|
3
5
|
require 'memory_io/types/record'
|
@@ -5,7 +7,7 @@ require 'memory_io/util'
|
|
5
7
|
|
6
8
|
module MemoryIO
|
7
9
|
module Types
|
8
|
-
# The base class, all
|
10
|
+
# The base class, all descendants of this class would be consider as a valid 'type'.
|
9
11
|
class Type
|
10
12
|
# The size of +size_t+. i.e. +sizeof(size_t)+.
|
11
13
|
SIZE_T = 8
|
@@ -92,7 +94,7 @@ module MemoryIO
|
|
92
94
|
# Register a new type.
|
93
95
|
#
|
94
96
|
# @param [#read, #write] object
|
95
|
-
# Normally, +object+ is a
|
97
|
+
# Normally, +object+ is a descendant class of {Type}.
|
96
98
|
#
|
97
99
|
# @option [Symbol, Array<Symbol>] alias
|
98
100
|
# Custom symbol name(s) that can be used in {.find}.
|
@@ -128,8 +130,10 @@ Register '#{object.inspect}' fails because another object with same name has bee
|
|
128
130
|
Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
|
129
131
|
EOS
|
130
132
|
raise reg_fail if aliases.any? && aliases.all? { |ali| @map[ali] }
|
133
|
+
|
131
134
|
keys = get_keys(object).concat(aliases).uniq.reject { |k| @map[k] }
|
132
135
|
raise reg_fail if keys.empty?
|
136
|
+
|
133
137
|
rec = MemoryIO::Types::Record.new(object, keys, option)
|
134
138
|
keys.each { |k| @map[k] = rec }
|
135
139
|
end
|
@@ -156,6 +160,7 @@ Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
|
|
156
160
|
# @return [Array<Symbol>]
|
157
161
|
def get_keys(klass)
|
158
162
|
return [] unless klass.instance_of?(Class)
|
163
|
+
|
159
164
|
snake = MemoryIO::Util.underscore(klass.name)
|
160
165
|
snake.gsub!(%r[^memory_io/types/], '')
|
161
166
|
ret = [snake]
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'memory_io/types/type'
|
2
4
|
require 'memory_io/util'
|
3
5
|
|
4
|
-
Dir
|
6
|
+
Dir.glob(File.join(__dir__, '**', '*.rb')).sort.each { |f| require f unless f == __FILE__ }
|
5
7
|
|
6
8
|
module MemoryIO
|
7
9
|
# Module that includes multiple types.
|
@@ -53,7 +55,7 @@ module MemoryIO
|
|
53
55
|
# #=> #<Method: MemoryIO::Types::Number#read>
|
54
56
|
def get_proc(name, rw)
|
55
57
|
klass = find(name)
|
56
|
-
klass
|
58
|
+
klass&.method(rw)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
end
|
data/lib/memory_io/util.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ostruct'
|
2
4
|
require 'dentaku'
|
3
5
|
|
@@ -24,6 +26,7 @@ module MemoryIO
|
|
24
26
|
# #=> 'my_module/my_class'
|
25
27
|
def underscore(str)
|
26
28
|
return '' if str.empty?
|
29
|
+
|
27
30
|
str = str.gsub('::', '/')
|
28
31
|
str.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
29
32
|
str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
@@ -41,6 +44,7 @@ module MemoryIO
|
|
41
44
|
# +nil+ for file not exists or is inaccessible.
|
42
45
|
def file_permission(file)
|
43
46
|
return nil unless File.file?(file)
|
47
|
+
|
44
48
|
stat = File.stat(file)
|
45
49
|
# we do a trick here because /proc/[pid]/mem might be marked as readable but fails at sysopen.
|
46
50
|
os = OpenStruct.new(readable?: stat.readable_real?, writable?: stat.writable_real?)
|
@@ -72,6 +76,7 @@ module MemoryIO
|
|
72
76
|
# #=> 56960 # 0xde80
|
73
77
|
def safe_eval(str, **vars)
|
74
78
|
return str if str.is_a?(Integer)
|
79
|
+
|
75
80
|
# dentaku 2 doesn't support hex
|
76
81
|
str = str.gsub(/0x[0-9a-zA-Z]+/) { |c| c.to_i(16) }
|
77
82
|
Dentaku::Calculator.new.store(vars).evaluate(str)
|
data/lib/memory_io/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memory_io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dentaku
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '13.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,28 +58,28 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0.
|
61
|
+
version: '0.59'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0.
|
68
|
+
version: '0.59'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: simplecov
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
75
|
+
version: '0.17'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: '0.17'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: yard
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,7 +94,9 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0.9'
|
97
|
-
description: '
|
97
|
+
description: 'Read/Write complicated structures in memory easily.
|
98
|
+
|
99
|
+
'
|
98
100
|
email:
|
99
101
|
- david942j@gmail.com
|
100
102
|
executables: []
|
@@ -125,15 +127,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
127
|
requirements:
|
126
128
|
- - ">="
|
127
129
|
- !ruby/object:Gem::Version
|
128
|
-
version: 2.
|
130
|
+
version: '2.3'
|
129
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
132
|
requirements:
|
131
133
|
- - ">="
|
132
134
|
- !ruby/object:Gem::Version
|
133
135
|
version: '0'
|
134
136
|
requirements: []
|
135
|
-
|
136
|
-
rubygems_version: 2.6.14
|
137
|
+
rubygems_version: 3.0.3
|
137
138
|
signing_key:
|
138
139
|
specification_version: 4
|
139
140
|
summary: memory_io
|