memory_io 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|