memory_io 0.2.0 → 1.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 +4 -4
- data/LICENSE +21 -0
- data/README.md +38 -12
- data/lib/memory_io/context.rb +95 -0
- data/lib/memory_io/error.rb +38 -0
- data/lib/memory_io/io.rb +83 -11
- data/lib/memory_io/logger.rb +32 -0
- data/lib/memory_io/process.rb +76 -15
- data/lib/memory_io/stream.rb +54 -0
- data/lib/memory_io/types/basic/number.rb +105 -23
- data/lib/memory_io/types/clang/c_str.rb +17 -3
- data/lib/memory_io/types/cpp/string.rb +21 -14
- data/lib/memory_io/types/record.rb +9 -7
- data/lib/memory_io/types/type.rb +38 -18
- data/lib/memory_io/types/types.rb +6 -6
- data/lib/memory_io/util.rb +79 -29
- data/lib/memory_io/version.rb +1 -1
- data/lib/memory_io.rb +27 -3
- metadata +66 -15
data/lib/memory_io/util.rb
CHANGED
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'ostruct'
|
|
4
3
|
require 'dentaku'
|
|
5
4
|
|
|
6
5
|
module MemoryIO
|
|
7
6
|
# Defines utility methods.
|
|
8
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
|
+
|
|
9
33
|
module_function
|
|
10
34
|
|
|
11
35
|
# Convert input into snake-case.
|
|
@@ -39,26 +63,12 @@ module MemoryIO
|
|
|
39
63
|
# @param [String] file
|
|
40
64
|
# File name.
|
|
41
65
|
#
|
|
42
|
-
# @return [
|
|
43
|
-
#
|
|
44
|
-
# +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.
|
|
45
68
|
def file_permission(file)
|
|
46
69
|
return nil unless File.file?(file)
|
|
47
70
|
|
|
48
|
-
|
|
49
|
-
# we do a trick here because /proc/[pid]/mem might be marked as readable but fails at sysopen.
|
|
50
|
-
os = OpenStruct.new(readable?: stat.readable_real?, writable?: stat.writable_real?)
|
|
51
|
-
begin
|
|
52
|
-
os.readable? && File.open(file, 'rb').close
|
|
53
|
-
rescue Errno::EACCES
|
|
54
|
-
os[:readable?] = false
|
|
55
|
-
end
|
|
56
|
-
begin
|
|
57
|
-
os.writable? && File.open(file, 'wb').close
|
|
58
|
-
rescue Errno::EACCES
|
|
59
|
-
os[:writable?] = false
|
|
60
|
-
end
|
|
61
|
-
os
|
|
71
|
+
FilePermission.new(file)
|
|
62
72
|
end
|
|
63
73
|
|
|
64
74
|
# Evaluate string safely.
|
|
@@ -68,25 +78,53 @@ module MemoryIO
|
|
|
68
78
|
# @param [{Symbol => Integer}] vars
|
|
69
79
|
# Predefined variables
|
|
70
80
|
#
|
|
71
|
-
# @return [Integer]
|
|
72
|
-
# Result.
|
|
81
|
+
# @return [Integer?]
|
|
82
|
+
# Result, or +nil+ if +str+ is not a valid expression.
|
|
73
83
|
#
|
|
74
84
|
# @example
|
|
75
85
|
# Util.safe_eval('heap + 0x10 * pp', heap: 0xde00, pp: 8)
|
|
76
86
|
# #=> 56960 # 0xde80
|
|
87
|
+
#
|
|
88
|
+
# Util.safe_eval('0xzz')
|
|
89
|
+
# #=> nil
|
|
77
90
|
def safe_eval(str, **vars)
|
|
78
91
|
return str if str.is_a?(Integer)
|
|
79
92
|
|
|
80
|
-
# dentaku 2 doesn't support hex
|
|
81
|
-
str = str.gsub(/0x[0-9a-zA-Z]+/) { |c| c.to_i(16) }
|
|
82
93
|
Dentaku::Calculator.new.store(vars).evaluate(str)
|
|
83
94
|
end
|
|
84
95
|
|
|
96
|
+
# @api private
|
|
97
|
+
#
|
|
98
|
+
# Read exactly +size+ bytes from +stream+.
|
|
99
|
+
#
|
|
100
|
+
# @param [#read] stream
|
|
101
|
+
# Stream to read.
|
|
102
|
+
# @param [Integer] size
|
|
103
|
+
# Number of bytes to read.
|
|
104
|
+
#
|
|
105
|
+
# @return [String]
|
|
106
|
+
# The bytes read.
|
|
107
|
+
#
|
|
108
|
+
# @raise [EOFError]
|
|
109
|
+
# Fewer than +size+ bytes remain in +stream+.
|
|
110
|
+
#
|
|
111
|
+
# @example
|
|
112
|
+
# Util.read_exactly(StringIO.new('1234'), 8)
|
|
113
|
+
# # EOFError: Requires 0x8 bytes, but only 0x4 bytes remain
|
|
114
|
+
def read_exactly(stream, size)
|
|
115
|
+
str = stream.read(size)
|
|
116
|
+
remain = str.nil? ? 0 : str.size
|
|
117
|
+
raise ::EOFError, format('Requires 0x%x bytes, but only 0x%x bytes remain', size, remain) if remain < size
|
|
118
|
+
|
|
119
|
+
str
|
|
120
|
+
end
|
|
121
|
+
|
|
85
122
|
# Unpack a string into an integer.
|
|
86
|
-
# Little endian is used.
|
|
87
123
|
#
|
|
88
124
|
# @param [String] str
|
|
89
125
|
# String.
|
|
126
|
+
# @param [:little, :big] endian
|
|
127
|
+
# Byte order of +str+.
|
|
90
128
|
#
|
|
91
129
|
# @return [Integer]
|
|
92
130
|
# Result.
|
|
@@ -96,12 +134,14 @@ module MemoryIO
|
|
|
96
134
|
# #=> 255
|
|
97
135
|
# Util.unpack("@\xE2\x01\x00")
|
|
98
136
|
# #=> 123456
|
|
99
|
-
|
|
100
|
-
|
|
137
|
+
# Util.unpack("\x00\x01\xE2@", :big)
|
|
138
|
+
# #=> 123456
|
|
139
|
+
def unpack(str, endian = :little)
|
|
140
|
+
bytes = endian == :little ? str.bytes.reverse : str.bytes
|
|
141
|
+
bytes.reduce(0) { |s, c| (s * 256) + c }
|
|
101
142
|
end
|
|
102
143
|
|
|
103
144
|
# Pack an integer into +b+ bytes.
|
|
104
|
-
# Little endian is used.
|
|
105
145
|
#
|
|
106
146
|
# @param [Integer] val
|
|
107
147
|
# The integer to pack.
|
|
@@ -109,6 +149,8 @@ module MemoryIO
|
|
|
109
149
|
# only lower +b+ bytes in +val+ will be packed.
|
|
110
150
|
#
|
|
111
151
|
# @param [Integer] b
|
|
152
|
+
# @param [:little, :big] endian
|
|
153
|
+
# Byte order to pack +val+ in.
|
|
112
154
|
#
|
|
113
155
|
# @return [String]
|
|
114
156
|
# Packing result with length +b+.
|
|
@@ -116,8 +158,12 @@ module MemoryIO
|
|
|
116
158
|
# @example
|
|
117
159
|
# Util.pack(0x123, 4)
|
|
118
160
|
# #=> "\x23\x01\x00\x00"
|
|
119
|
-
|
|
120
|
-
|
|
161
|
+
# Util.pack(0x123, 4, :big)
|
|
162
|
+
# #=> "\x00\x00\x01\x23"
|
|
163
|
+
def pack(val, b, endian = :little)
|
|
164
|
+
bytes = Array.new(b) { |i| (val >> (i * 8)) & 0xff }
|
|
165
|
+
bytes.reverse! if endian == :big
|
|
166
|
+
bytes.pack('C*')
|
|
121
167
|
end
|
|
122
168
|
|
|
123
169
|
# Remove extension name (.so) and version in library name.
|
|
@@ -135,9 +181,13 @@ module MemoryIO
|
|
|
135
181
|
# #=> 'libcrypto'
|
|
136
182
|
# Util.trim_libname('not_a_so')
|
|
137
183
|
# #=> 'not_a_so'
|
|
184
|
+
# Util.trim_libname('ld-linux-x86-64.so.2')
|
|
185
|
+
# #=> 'ld'
|
|
138
186
|
def trim_libname(name)
|
|
187
|
+
return 'ld' if name.start_with?('ld-')
|
|
188
|
+
|
|
139
189
|
type1 = '(-[\d.]+)?\.so$'
|
|
140
|
-
type2 = '\.so
|
|
190
|
+
type2 = '\.so.[\.\d]+$'
|
|
141
191
|
name.sub(/#{type1}|#{type2}/, '')
|
|
142
192
|
end
|
|
143
193
|
end
|
data/lib/memory_io/version.rb
CHANGED
data/lib/memory_io.rb
CHANGED
|
@@ -9,12 +9,36 @@ module MemoryIO
|
|
|
9
9
|
# Get a process by process id.
|
|
10
10
|
#
|
|
11
11
|
# @param [Integer] pid
|
|
12
|
-
# Process
|
|
12
|
+
# Process id in Linux.
|
|
13
|
+
# @param [:little, :big, :native, nil] endian
|
|
14
|
+
# Byte order of the process's memory.
|
|
15
|
+
# @param [Integer?] pointer_size
|
|
16
|
+
# Size of a pointer in the process, in bytes.
|
|
17
|
+
#
|
|
18
|
+
# Both are taken from the process's executable when not given.
|
|
13
19
|
#
|
|
14
20
|
# @return [MemoryIO::Process]
|
|
15
21
|
# A process object for further usage.
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
#
|
|
23
|
+
# @raise [MemoryIO::ProcessNotFoundError]
|
|
24
|
+
# The memory of +pid+ is not accessible.
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# process = MemoryIO.attach(`pidof victim`.to_i)
|
|
28
|
+
# process.read('heap', 8)
|
|
29
|
+
# @example
|
|
30
|
+
# # a program started through an interpreter is described by the
|
|
31
|
+
# # interpreter, so state the context of the program instead
|
|
32
|
+
# MemoryIO.attach(`pidof victim32`.to_i, pointer_size: 4)
|
|
33
|
+
#
|
|
34
|
+
# @note
|
|
35
|
+
# The context is read from +/proc/[pid]/exe+, which names the interpreter
|
|
36
|
+
# when the process was started through one. Pass +endian+ and
|
|
37
|
+
# +pointer_size+ for such a process, whose context may differ from
|
|
38
|
+
# the interpreter running it.
|
|
39
|
+
# @see MemoryIO::Process#initialize
|
|
40
|
+
def attach(pid, endian: nil, pointer_size: nil)
|
|
41
|
+
MemoryIO::Process.new(pid, endian: endian, pointer_size: pointer_size)
|
|
18
42
|
end
|
|
19
43
|
end
|
|
20
44
|
|
metadata
CHANGED
|
@@ -1,29 +1,76 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: memory_io
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.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: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: dentaku
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '4'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '4'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: elftools
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
16
29
|
requirements:
|
|
17
30
|
- - ">="
|
|
18
31
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
32
|
+
version: '1.3'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '3.0'
|
|
20
36
|
type: :runtime
|
|
21
37
|
prerelease: false
|
|
22
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
39
|
requirements:
|
|
24
40
|
- - ">="
|
|
25
41
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
42
|
+
version: '1.3'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '3.0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: logger
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '1.6'
|
|
53
|
+
type: :runtime
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '1.6'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: ostruct
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '0.6'
|
|
67
|
+
type: :development
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0.6'
|
|
27
74
|
- !ruby/object:Gem::Dependency
|
|
28
75
|
name: rake
|
|
29
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,28 +105,28 @@ dependencies:
|
|
|
58
105
|
requirements:
|
|
59
106
|
- - "~>"
|
|
60
107
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
108
|
+
version: '1'
|
|
62
109
|
type: :development
|
|
63
110
|
prerelease: false
|
|
64
111
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
112
|
requirements:
|
|
66
113
|
- - "~>"
|
|
67
114
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
115
|
+
version: '1'
|
|
69
116
|
- !ruby/object:Gem::Dependency
|
|
70
117
|
name: simplecov
|
|
71
118
|
requirement: !ruby/object:Gem::Requirement
|
|
72
119
|
requirements:
|
|
73
120
|
- - "~>"
|
|
74
121
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0
|
|
122
|
+
version: '1.0'
|
|
76
123
|
type: :development
|
|
77
124
|
prerelease: false
|
|
78
125
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
126
|
requirements:
|
|
80
127
|
- - "~>"
|
|
81
128
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0
|
|
129
|
+
version: '1.0'
|
|
83
130
|
- !ruby/object:Gem::Dependency
|
|
84
131
|
name: yard
|
|
85
132
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -96,17 +143,22 @@ dependencies:
|
|
|
96
143
|
version: '0.9'
|
|
97
144
|
description: 'Read/Write complicated structures in memory easily.
|
|
98
145
|
|
|
99
|
-
'
|
|
146
|
+
'
|
|
100
147
|
email:
|
|
101
148
|
- david942j@gmail.com
|
|
102
149
|
executables: []
|
|
103
150
|
extensions: []
|
|
104
151
|
extra_rdoc_files: []
|
|
105
152
|
files:
|
|
153
|
+
- LICENSE
|
|
106
154
|
- README.md
|
|
107
155
|
- lib/memory_io.rb
|
|
156
|
+
- lib/memory_io/context.rb
|
|
157
|
+
- lib/memory_io/error.rb
|
|
108
158
|
- lib/memory_io/io.rb
|
|
159
|
+
- lib/memory_io/logger.rb
|
|
109
160
|
- lib/memory_io/process.rb
|
|
161
|
+
- lib/memory_io/stream.rb
|
|
110
162
|
- lib/memory_io/types/basic/number.rb
|
|
111
163
|
- lib/memory_io/types/clang/c_str.rb
|
|
112
164
|
- lib/memory_io/types/cpp/string.rb
|
|
@@ -118,8 +170,8 @@ files:
|
|
|
118
170
|
homepage: https://github.com/david942j/memory_io
|
|
119
171
|
licenses:
|
|
120
172
|
- MIT
|
|
121
|
-
metadata:
|
|
122
|
-
|
|
173
|
+
metadata:
|
|
174
|
+
rubygems_mfa_required: 'true'
|
|
123
175
|
rdoc_options: []
|
|
124
176
|
require_paths:
|
|
125
177
|
- lib
|
|
@@ -127,15 +179,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
127
179
|
requirements:
|
|
128
180
|
- - ">="
|
|
129
181
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '
|
|
182
|
+
version: '3.3'
|
|
131
183
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
184
|
requirements:
|
|
133
185
|
- - ">="
|
|
134
186
|
- !ruby/object:Gem::Version
|
|
135
187
|
version: '0'
|
|
136
188
|
requirements: []
|
|
137
|
-
rubygems_version: 3.
|
|
138
|
-
signing_key:
|
|
189
|
+
rubygems_version: 3.6.9
|
|
139
190
|
specification_version: 4
|
|
140
191
|
summary: memory_io
|
|
141
192
|
test_files: []
|