memory_io 0.1.1 → 0.3.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/LICENSE +21 -0
- data/README.md +12 -14
- data/lib/memory_io/io.rb +10 -5
- data/lib/memory_io/process.rb +8 -8
- data/lib/memory_io/types/basic/number.rb +5 -3
- data/lib/memory_io/types/clang/c_str.rb +5 -2
- data/lib/memory_io/types/cpp/string.rb +9 -8
- data/lib/memory_io/types/record.rb +10 -4
- data/lib/memory_io/types/type.rb +8 -4
- data/lib/memory_io/types/types.rb +5 -3
- data/lib/memory_io/util.rb +39 -20
- data/lib/memory_io/version.rb +3 -1
- data/lib/memory_io.rb +2 -0
- metadata +33 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0f287dc7b3453557bbcda2f2ce09e03d779d0dbff69f8ef059069f03c76da427
|
|
4
|
+
data.tar.gz: a471ab55f6abd70783a220d0939cdd8f84231f59a533a6470a9f64119c184a41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 959a45d2dcab5ced28be35f6988a6c6fadbd1fd6510e21747727f16e87cabddf23e951ffba71d8deec4baa1be440ce5bd79b1fe54e1bad66a17deb9185c54db9
|
|
7
|
+
data.tar.gz: a1fe1e032d09a53ce4611e2738255ac1444b5a0283e959606a1da232db28c9d0d42a7277977b17d2bb8fcfa476cc58c53368ad64fa970d0b7b91a5536e59e79e
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 david942j
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
[](https://travis-ci.org/david942j/memory_io)
|
|
2
1
|
[](https://badge.fury.io/rb/memory_io)
|
|
3
|
-
[](https://github.com/david942j/memory_io/actions)
|
|
3
|
+
[](https://qlty.sh/gh/david942j/projects/memory_io)
|
|
4
|
+
[](https://qlty.sh/gh/david942j/projects/memory_io)
|
|
5
|
+
[](https://www.rubydoc.info/github/david942j/memory_io/)
|
|
6
6
|
[](http://choosealicense.com/licenses/mit/)
|
|
7
7
|
|
|
8
8
|
# MemoryIO
|
|
@@ -14,27 +14,25 @@ Read/Write complicated structures in memory easily.
|
|
|
14
14
|
I usually need to dump a structure, say `string` in C++, from memory for debugging.
|
|
15
15
|
This is not hard if using gdb.
|
|
16
16
|
However, gdb doesn't support writing Ruby scripts
|
|
17
|
-
(unless you use [gdb-ruby](https://github.com/david942j/gdb-ruby), which has
|
|
18
|
-
So I
|
|
17
|
+
(unless you use [gdb-ruby](https://github.com/david942j/gdb-ruby), which has **MemoryIO** as its dependency).
|
|
18
|
+
So I created this projected 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
|
|
26
26
|
|
|
27
27
|
It's not hard to read/write a process's memory (simply open the file `/proc/$PID/mem`),
|
|
28
|
-
but it still
|
|
28
|
+
but it's still worthy to make a utility.
|
|
29
29
|
|
|
30
|
-
This
|
|
30
|
+
This project also targets to collect all common structures, such as how to parse a C++/Rust/Python object from memory.
|
|
31
31
|
Therefore, **Pull Requests of adding new structures** are welcome :D
|
|
32
32
|
|
|
33
33
|
## Supported Platform
|
|
34
34
|
|
|
35
35
|
- Linux
|
|
36
|
-
- (TODO) Windows
|
|
37
|
-
- (TODO) MacOS
|
|
38
36
|
|
|
39
37
|
## Implemented Structures
|
|
40
38
|
|
|
@@ -51,7 +49,7 @@ process.read(0x601000, 1, as: 'basic/u64')
|
|
|
51
49
|
process.read(0x601000, 1, as: :u64)
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
Go to [the online document](http://www.rubydoc.info/github/david942j/memory_io/master/MemoryIO/Types) for more details
|
|
55
53
|
of each type.
|
|
56
54
|
|
|
57
55
|
### BASIC
|
|
@@ -122,7 +120,7 @@ string
|
|
|
122
120
|
require 'memory_io'
|
|
123
121
|
process = MemoryIO.attach(`pidof victim`.to_i)
|
|
124
122
|
|
|
125
|
-
# An example that
|
|
123
|
+
# An example that reads a chunk of pt-malloc.
|
|
126
124
|
read_chunk = lambda do |stream|
|
|
127
125
|
_prev_size = stream.read(8)
|
|
128
126
|
size = (stream.read(8).unpack('Q').first & -16) - 8
|
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
|
|
@@ -180,10 +183,12 @@ module MemoryIO
|
|
|
180
183
|
# @api private
|
|
181
184
|
def to_proc(as, rw)
|
|
182
185
|
ret = as.respond_to?(rw) ? as.method(rw) : as
|
|
183
|
-
ret =
|
|
186
|
+
ret = MemoryIO::Types.get_proc(ret, rw) unless ret.respond_to?(:call)
|
|
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
|
|
@@ -14,11 +16,6 @@ module MemoryIO
|
|
|
14
16
|
#
|
|
15
17
|
# @note
|
|
16
18
|
# 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
19
|
def initialize(pid)
|
|
23
20
|
@pid = pid
|
|
24
21
|
@mem = "/proc/#{pid}/mem"
|
|
@@ -26,6 +23,7 @@ module MemoryIO
|
|
|
26
23
|
@perm = MemoryIO::Util.file_permission(@mem)
|
|
27
24
|
# TODO: raise custom exception
|
|
28
25
|
raise Errno::ENOENT, @mem if perm.nil?
|
|
26
|
+
|
|
29
27
|
# FIXME: use logger
|
|
30
28
|
warn(<<-EOS.strip) unless perm.readable? || perm.writable?
|
|
31
29
|
You have no permission to read/write this process.
|
|
@@ -59,11 +57,13 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
|
|
59
57
|
def bases
|
|
60
58
|
file = "/proc/#{@pid}/maps"
|
|
61
59
|
stat = MemoryIO::Util.file_permission(file)
|
|
62
|
-
return {} unless stat
|
|
60
|
+
return {} unless stat&.readable?
|
|
61
|
+
|
|
63
62
|
maps = ::IO.binread(file).split("\n").map do |line|
|
|
64
63
|
# 7f76515cf000-7f76515da000 r-xp 00000000 fd:01 29360257 /lib/x86_64-linux-gnu/libnss_files-2.24.so
|
|
65
64
|
addr, _perm, _offset, _dev, _inode, pathname = line.strip.split(' ', 6)
|
|
66
65
|
next nil if pathname.nil?
|
|
66
|
+
|
|
67
67
|
addr = addr.to_i(16)
|
|
68
68
|
pathname = pathname[1..-2] if pathname =~ /^\[.+\]$/
|
|
69
69
|
pathname = ::File.basename(pathname)
|
|
@@ -104,7 +104,7 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
|
|
104
104
|
# #=> "\x7fELF"
|
|
105
105
|
# @see IO#read
|
|
106
106
|
def read(addr, num_elements, **options)
|
|
107
|
-
mem_io(:read) { |io| io.read(num_elements, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
|
|
107
|
+
mem_io(:read) { |io| io.read(num_elements, from: MemoryIO::Util.safe_eval(addr, **bases), **options) }
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
# Write objects at +addr+.
|
|
@@ -128,7 +128,7 @@ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
|
|
128
128
|
# #=> 'BBBBCCCCAAAAAAAA'
|
|
129
129
|
# @see IO#write
|
|
130
130
|
def write(addr, objects, **options)
|
|
131
|
-
mem_io(:write) { |io| io.write(objects, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
|
|
131
|
+
mem_io(:write) { |io| io.write(objects, from: MemoryIO::Util.safe_eval(addr, **bases), **options) }
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
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
|
|
@@ -35,8 +37,8 @@ module MemoryIO
|
|
|
35
37
|
private
|
|
36
38
|
|
|
37
39
|
def unpack(str)
|
|
38
|
-
val = str.
|
|
39
|
-
val -= (2**(@bytes * 8)) if @signed && val >= (2**(@bytes * 8 - 1))
|
|
40
|
+
val = str.unpack1(@pack_str)
|
|
41
|
+
val -= (2**(@bytes * 8)) if @signed && val >= (2**((@bytes * 8) - 1))
|
|
40
42
|
val
|
|
41
43
|
end
|
|
42
44
|
|
|
@@ -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,12 +18,11 @@ 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
|
|
|
22
|
-
attr_reader :data # @return [::String]
|
|
23
|
-
attr_reader :capacity # @return [Integer]
|
|
24
|
-
attr_reader :dataplus # @return [Integer]
|
|
25
|
+
attr_reader :data, :capacity, :dataplus # @return [::String] # @return [Integer] # @return [Integer]
|
|
25
26
|
|
|
26
27
|
# Instantiate a {CPP::String} object.
|
|
27
28
|
#
|
|
@@ -30,6 +31,7 @@ module MemoryIO
|
|
|
30
31
|
# @param [Integer] dataplus
|
|
31
32
|
# A pointer.
|
|
32
33
|
def initialize(data, capacity, dataplus)
|
|
34
|
+
super()
|
|
33
35
|
@data = data
|
|
34
36
|
@capacity = capacity
|
|
35
37
|
@dataplus = dataplus
|
|
@@ -54,15 +56,14 @@ module MemoryIO
|
|
|
54
56
|
# Custom inspect view.
|
|
55
57
|
#
|
|
56
58
|
# @return [String]
|
|
57
|
-
#
|
|
58
|
-
# @todo
|
|
59
|
-
# Let it be colorful in pry.
|
|
60
59
|
def inspect
|
|
60
|
+
# rubocop:disable Lint/FormatParameterMismatch
|
|
61
61
|
format("#<%s @data=%s, @capacity=%d, @dataplus=0x%0#{SIZE_T * 2}x>",
|
|
62
62
|
self.class.name,
|
|
63
63
|
data.inspect,
|
|
64
64
|
capacity,
|
|
65
65
|
dataplus)
|
|
66
|
+
# rubocop:enable Lint/FormatParameterMismatch
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
class << self
|
|
@@ -106,10 +107,10 @@ module MemoryIO
|
|
|
106
107
|
write_size_t(stream, obj.length)
|
|
107
108
|
pos = stream.pos
|
|
108
109
|
if obj.length > LOCAL_CAPACITY
|
|
109
|
-
keep_pos(stream, pos: obj.dataplus) { |s| s.write(obj.data
|
|
110
|
+
keep_pos(stream, pos: obj.dataplus) { |s| s.write("#{obj.data}\u0000") }
|
|
110
111
|
write_size_t(stream, obj.capacity)
|
|
111
112
|
else
|
|
112
|
-
stream.write(obj.data
|
|
113
|
+
stream.write("#{obj.data}\u0000")
|
|
113
114
|
end
|
|
114
115
|
stream.pos = pos + LOCAL_CAPACITY + 1
|
|
115
116
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module MemoryIO
|
|
2
4
|
module Types
|
|
3
5
|
# @api private
|
|
@@ -21,8 +23,8 @@ module MemoryIO
|
|
|
21
23
|
# @option [Thread::Backtrace::Location] caller
|
|
22
24
|
# This option should present if and only if +object+ is a subclass of {Types::Type}.
|
|
23
25
|
# @option [String] doc
|
|
24
|
-
#
|
|
25
|
-
# Automatically parse from caller location if this parameter
|
|
26
|
+
# Doc-string.
|
|
27
|
+
# Automatically parse from caller location if this parameter doesn't present.
|
|
26
28
|
def initialize(object, keys, option = {})
|
|
27
29
|
@obj = object
|
|
28
30
|
@keys = keys
|
|
@@ -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,21 +50,24 @@ 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?('#')
|
|
57
|
-
|
|
62
|
+
|
|
63
|
+
strings.unshift(str[2..] || '')
|
|
58
64
|
end
|
|
59
65
|
trim_docstring(strings)
|
|
60
66
|
end
|
|
61
67
|
|
|
62
68
|
def trim_docstring(strings)
|
|
63
69
|
strings = strings.drop_while { |s| s.start_with?('@') }.take_while { |s| !s.start_with?('@') }
|
|
64
|
-
strings.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse.join("\n")
|
|
70
|
+
"#{strings.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse.join("\n")}\n"
|
|
65
71
|
end
|
|
66
72
|
end
|
|
67
73
|
end
|
data/lib/memory_io/types/type.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'memory_io/types/record'
|
|
4
4
|
require 'memory_io/util'
|
|
5
5
|
|
|
6
6
|
module MemoryIO
|
|
7
7
|
module Types
|
|
8
|
-
# The base class, all
|
|
8
|
+
# The base class, all descendants of this class would be consider as a valid 'type'.
|
|
9
9
|
class Type
|
|
10
10
|
# The size of +size_t+. i.e. +sizeof(size_t)+.
|
|
11
11
|
SIZE_T = 8
|
|
@@ -92,7 +92,7 @@ module MemoryIO
|
|
|
92
92
|
# Register a new type.
|
|
93
93
|
#
|
|
94
94
|
# @param [#read, #write] object
|
|
95
|
-
# Normally, +object+ is a
|
|
95
|
+
# Normally, +object+ is a descendant class of {Type}.
|
|
96
96
|
#
|
|
97
97
|
# @option [Symbol, Array<Symbol>] alias
|
|
98
98
|
# Custom symbol name(s) that can be used in {.find}.
|
|
@@ -121,15 +121,17 @@ module MemoryIO
|
|
|
121
121
|
#
|
|
122
122
|
# @see .find
|
|
123
123
|
def register(object, option = {})
|
|
124
|
-
@map ||=
|
|
124
|
+
@map ||= {}
|
|
125
125
|
aliases = Array(option[:alias])
|
|
126
126
|
reg_fail = ArgumentError.new(<<-EOS.strip)
|
|
127
127
|
Register '#{object.inspect}' fails because another object with same name has been registered.
|
|
128
128
|
Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
|
|
129
129
|
EOS
|
|
130
130
|
raise reg_fail if aliases.any? && aliases.all? { |ali| @map[ali] }
|
|
131
|
+
|
|
131
132
|
keys = get_keys(object).concat(aliases).uniq.reject { |k| @map[k] }
|
|
132
133
|
raise reg_fail if keys.empty?
|
|
134
|
+
|
|
133
135
|
rec = MemoryIO::Types::Record.new(object, keys, option)
|
|
134
136
|
keys.each { |k| @map[k] = rec }
|
|
135
137
|
end
|
|
@@ -148,6 +150,7 @@ Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
|
|
|
148
150
|
#
|
|
149
151
|
# To record descendants.
|
|
150
152
|
def inherited(klass)
|
|
153
|
+
super
|
|
151
154
|
register(klass, caller: caller_locations(1, 1).first)
|
|
152
155
|
end
|
|
153
156
|
|
|
@@ -156,6 +159,7 @@ Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
|
|
|
156
159
|
# @return [Array<Symbol>]
|
|
157
160
|
def get_keys(klass)
|
|
158
161
|
return [] unless klass.instance_of?(Class)
|
|
162
|
+
|
|
159
163
|
snake = MemoryIO::Util.underscore(klass.name)
|
|
160
164
|
snake.gsub!(%r[^memory_io/types/], '')
|
|
161
165
|
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')).each { |f| require f unless f == __FILE__ }
|
|
5
7
|
|
|
6
8
|
module MemoryIO
|
|
7
9
|
# Module that includes multiple types.
|
|
@@ -31,7 +33,7 @@ module MemoryIO
|
|
|
31
33
|
# #=> #<MemoryIO::Types::Number:0x000055ecc017a310 @bytes=8, @pack_str="Q", @signed=false>
|
|
32
34
|
def find(name)
|
|
33
35
|
obj = Types::Type.find(name)
|
|
34
|
-
|
|
36
|
+
obj&.obj
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# @api private
|
|
@@ -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,9 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'dentaku'
|
|
3
4
|
|
|
4
5
|
module MemoryIO
|
|
5
6
|
# Defines utility methods.
|
|
6
7
|
module Util
|
|
8
|
+
# A simple class to be returned for getting file permissions.
|
|
9
|
+
class FilePermission
|
|
10
|
+
attr_reader :readable, :writable
|
|
11
|
+
# Alias the two methods to fit Ruby's method naming convention.
|
|
12
|
+
alias readable? readable
|
|
13
|
+
alias writable? writable
|
|
14
|
+
|
|
15
|
+
def initialize(file)
|
|
16
|
+
stat = File.stat(file)
|
|
17
|
+
@readable = stat.readable_real?
|
|
18
|
+
@writable = stat.writable_real?
|
|
19
|
+
# we do a trick here because /proc/[pid]/mem might be marked as writeable but fails at sysopen.
|
|
20
|
+
begin
|
|
21
|
+
@readable && File.open(file, 'rb').close
|
|
22
|
+
rescue Errno::EACCES
|
|
23
|
+
@readable = false
|
|
24
|
+
end
|
|
25
|
+
begin
|
|
26
|
+
@writable && File.open(file, 'wb').close
|
|
27
|
+
rescue Errno::EACCES
|
|
28
|
+
@writable = false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
7
33
|
module_function
|
|
8
34
|
|
|
9
35
|
# Convert input into snake-case.
|
|
@@ -24,6 +50,7 @@ module MemoryIO
|
|
|
24
50
|
# #=> 'my_module/my_class'
|
|
25
51
|
def underscore(str)
|
|
26
52
|
return '' if str.empty?
|
|
53
|
+
|
|
27
54
|
str = str.gsub('::', '/')
|
|
28
55
|
str.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
29
56
|
str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
|
@@ -36,25 +63,12 @@ module MemoryIO
|
|
|
36
63
|
# @param [String] file
|
|
37
64
|
# File name.
|
|
38
65
|
#
|
|
39
|
-
# @return [
|
|
40
|
-
#
|
|
41
|
-
# +nil+ for file not exists or is inaccessible.
|
|
66
|
+
# @return [MemoryIO::Util::FilePermission?]
|
|
67
|
+
# +nil+ is returned if file does not exist or is inaccessible.
|
|
42
68
|
def file_permission(file)
|
|
43
69
|
return nil unless File.file?(file)
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
70
|
+
|
|
71
|
+
FilePermission.new(file)
|
|
58
72
|
end
|
|
59
73
|
|
|
60
74
|
# Evaluate string safely.
|
|
@@ -72,6 +86,7 @@ module MemoryIO
|
|
|
72
86
|
# #=> 56960 # 0xde80
|
|
73
87
|
def safe_eval(str, **vars)
|
|
74
88
|
return str if str.is_a?(Integer)
|
|
89
|
+
|
|
75
90
|
# dentaku 2 doesn't support hex
|
|
76
91
|
str = str.gsub(/0x[0-9a-zA-Z]+/) { |c| c.to_i(16) }
|
|
77
92
|
Dentaku::Calculator.new.store(vars).evaluate(str)
|
|
@@ -92,7 +107,7 @@ module MemoryIO
|
|
|
92
107
|
# Util.unpack("@\xE2\x01\x00")
|
|
93
108
|
# #=> 123456
|
|
94
109
|
def unpack(str)
|
|
95
|
-
str.bytes.reverse.reduce(0) { |s, c| s * 256 + c }
|
|
110
|
+
str.bytes.reverse.reduce(0) { |s, c| (s * 256) + c }
|
|
96
111
|
end
|
|
97
112
|
|
|
98
113
|
# Pack an integer into +b+ bytes.
|
|
@@ -130,9 +145,13 @@ module MemoryIO
|
|
|
130
145
|
# #=> 'libcrypto'
|
|
131
146
|
# Util.trim_libname('not_a_so')
|
|
132
147
|
# #=> 'not_a_so'
|
|
148
|
+
# Util.trim_libname('ld-linux-x86-64.so.2')
|
|
149
|
+
# #=> 'ld'
|
|
133
150
|
def trim_libname(name)
|
|
151
|
+
return 'ld' if name.start_with?('ld-')
|
|
152
|
+
|
|
134
153
|
type1 = '(-[\d.]+)?\.so$'
|
|
135
|
-
type2 = '\.so
|
|
154
|
+
type2 = '\.so.[\.\d]+$'
|
|
136
155
|
name.sub(/#{type1}|#{type2}/, '')
|
|
137
156
|
end
|
|
138
157
|
end
|
data/lib/memory_io/version.rb
CHANGED
data/lib/memory_io.rb
CHANGED
metadata
CHANGED
|
@@ -1,43 +1,56 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: memory_io
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- david942j
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2025-11-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: dentaku
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
16
|
+
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
18
|
+
version: '3'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ostruct
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.6'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
22
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
36
|
requirements:
|
|
24
37
|
- - ">="
|
|
25
38
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
39
|
+
version: '0.6'
|
|
27
40
|
- !ruby/object:Gem::Dependency
|
|
28
41
|
name: rake
|
|
29
42
|
requirement: !ruby/object:Gem::Requirement
|
|
30
43
|
requirements:
|
|
31
44
|
- - "~>"
|
|
32
45
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
46
|
+
version: '13.0'
|
|
34
47
|
type: :development
|
|
35
48
|
prerelease: false
|
|
36
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
50
|
requirements:
|
|
38
51
|
- - "~>"
|
|
39
52
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
53
|
+
version: '13.0'
|
|
41
54
|
- !ruby/object:Gem::Dependency
|
|
42
55
|
name: rspec
|
|
43
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,28 +71,28 @@ dependencies:
|
|
|
58
71
|
requirements:
|
|
59
72
|
- - "~>"
|
|
60
73
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
74
|
+
version: '1'
|
|
62
75
|
type: :development
|
|
63
76
|
prerelease: false
|
|
64
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
78
|
requirements:
|
|
66
79
|
- - "~>"
|
|
67
80
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
81
|
+
version: '1'
|
|
69
82
|
- !ruby/object:Gem::Dependency
|
|
70
83
|
name: simplecov
|
|
71
84
|
requirement: !ruby/object:Gem::Requirement
|
|
72
85
|
requirements:
|
|
73
86
|
- - "~>"
|
|
74
87
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 0.
|
|
88
|
+
version: '0.22'
|
|
76
89
|
type: :development
|
|
77
90
|
prerelease: false
|
|
78
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
92
|
requirements:
|
|
80
93
|
- - "~>"
|
|
81
94
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 0.
|
|
95
|
+
version: '0.22'
|
|
83
96
|
- !ruby/object:Gem::Dependency
|
|
84
97
|
name: yard
|
|
85
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,13 +107,16 @@ dependencies:
|
|
|
94
107
|
- - "~>"
|
|
95
108
|
- !ruby/object:Gem::Version
|
|
96
109
|
version: '0.9'
|
|
97
|
-
description: '
|
|
110
|
+
description: 'Read/Write complicated structures in memory easily.
|
|
111
|
+
|
|
112
|
+
'
|
|
98
113
|
email:
|
|
99
114
|
- david942j@gmail.com
|
|
100
115
|
executables: []
|
|
101
116
|
extensions: []
|
|
102
117
|
extra_rdoc_files: []
|
|
103
118
|
files:
|
|
119
|
+
- LICENSE
|
|
104
120
|
- README.md
|
|
105
121
|
- lib/memory_io.rb
|
|
106
122
|
- lib/memory_io/io.rb
|
|
@@ -116,8 +132,8 @@ files:
|
|
|
116
132
|
homepage: https://github.com/david942j/memory_io
|
|
117
133
|
licenses:
|
|
118
134
|
- MIT
|
|
119
|
-
metadata:
|
|
120
|
-
|
|
135
|
+
metadata:
|
|
136
|
+
rubygems_mfa_required: 'true'
|
|
121
137
|
rdoc_options: []
|
|
122
138
|
require_paths:
|
|
123
139
|
- lib
|
|
@@ -125,16 +141,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
125
141
|
requirements:
|
|
126
142
|
- - ">="
|
|
127
143
|
- !ruby/object:Gem::Version
|
|
128
|
-
version: 2
|
|
144
|
+
version: '3.2'
|
|
129
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
146
|
requirements:
|
|
131
147
|
- - ">="
|
|
132
148
|
- !ruby/object:Gem::Version
|
|
133
149
|
version: '0'
|
|
134
150
|
requirements: []
|
|
135
|
-
|
|
136
|
-
rubygems_version: 2.6.14
|
|
137
|
-
signing_key:
|
|
151
|
+
rubygems_version: 3.6.2
|
|
138
152
|
specification_version: 4
|
|
139
153
|
summary: memory_io
|
|
140
154
|
test_files: []
|