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.
@@ -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 [#readable?, #writable?, nil]
43
- # Struct with two boolean method.
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
- stat = File.stat(file)
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
- def unpack(str)
100
- str.bytes.reverse.reduce(0) { |s, c| s * 256 + c }
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
- def pack(val, b)
120
- Array.new(b) { |i| (val >> (i * 8)) & 0xff }.pack('C*')
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.\d+[\d.]+$'
190
+ type2 = '\.so.[\.\d]+$'
141
191
  name.sub(/#{type1}|#{type2}/, '')
142
192
  end
143
193
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module MemoryIO
4
4
  # Current gem version.
5
- VERSION = '0.2.0'
5
+ VERSION = '1.0.0'
6
6
  end
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 Id in linux.
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
- def attach(pid)
17
- MemoryIO::Process.new(pid)
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.2.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: 2020-03-07 00:00:00.000000000 Z
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: 2.0.11
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: 2.0.11
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: '0.59'
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: '0.59'
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.17'
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.17'
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
- post_install_message:
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: '2.3'
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.0.3
138
- signing_key:
189
+ rubygems_version: 3.6.9
139
190
  specification_version: 4
140
191
  summary: memory_io
141
192
  test_files: []