peepmem 0.0.1
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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +28 -0
- data/README.md +50 -0
- data/Rakefile +7 -0
- data/ext/peepmem/extconf.rb +3 -0
- data/ext/peepmem/peepmem.c +825 -0
- data/lib/peepmem.rb +6 -0
- data/lib/peepmem/version.rb +3 -0
- data/peepmem.gemspec +24 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cb5e807044c59546c78906ecf2fa9368a148bb14
|
4
|
+
data.tar.gz: 773031f05a438edbbbb6f0a7e17e78862172c07b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 31be993426c47f5df3c9037d4ba841b393bc8561b51cc8bd78307cd94fb2fae674bf8cd422fc6f50700233288899b459829a7014214e6b8f712d43b75e99fe17
|
7
|
+
data.tar.gz: 62b9089ef7b2215e46769977e0bd8d69f3b76c38e3d2468e927b53354a27cb8ad16d1ea861f392d3b5683beb8a0e2d7e917f877ea0b409d620ac6dc8fd24726e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Copyright (C) 2015 Kubo Takehiro <kubo@jiubao.org>
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
|
9
|
+
2. Redistributions in binary form must reproduce the above
|
10
|
+
copyright notice, this list of conditions and the following
|
11
|
+
disclaimer in the documentation and/or other materials provided
|
12
|
+
with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR
|
15
|
+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
16
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
|
18
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
19
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
20
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
21
|
+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
22
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
23
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
24
|
+
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
25
|
+
|
26
|
+
The views and conclusions contained in the software and documentation
|
27
|
+
are those of the authors and should not be interpreted as representing
|
28
|
+
official policies, either expressed or implied, of the authors.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Peepmem
|
2
|
+
|
3
|
+
ruby module to peep memory of another process.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'peepmem'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install peepmem
|
20
|
+
|
21
|
+
## Example
|
22
|
+
|
23
|
+
# Open process (pid = 3657)
|
24
|
+
handle = Peepmem.open(3657) # => #<Peepmem::Handle: PID=3657>
|
25
|
+
# Read memory at 0x00400000 as 16-byte string
|
26
|
+
pointer = handle[0x00400000] # => #<Peepmem::Pointer:0x00000000400000 PID=3657>
|
27
|
+
pointer['s16'] # => "\x7FELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
28
|
+
# Read as null-terminated string
|
29
|
+
pointer['s'] # => "\x7FELF\x02\x01\x01"
|
30
|
+
# Seek 16 bytes and read memory as 2-byte unsigned integer
|
31
|
+
pointer += 16
|
32
|
+
pointer['u2'] # => 2
|
33
|
+
# Read as 2-byte unsigned integer, 4-byte unsigned integer and
|
34
|
+
# 8-byte unsigned integer
|
35
|
+
(pointer + 2)['u2 u4 u8'] # => [62, 1, 4209768]
|
36
|
+
# Read memory at 0x00400018 as a pointer
|
37
|
+
pointer = handle[0x00400018]['p'] # => #<Peepmem::Pointer:0x00000000403c68 PID=3657>>
|
38
|
+
|
39
|
+
## Supported Platforms
|
40
|
+
|
41
|
+
* Linux
|
42
|
+
* Windows
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
1. Fork it ( https://github.com/kubo/ruby-peepmem/fork )
|
47
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
48
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
49
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
50
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,825 @@
|
|
1
|
+
/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*-
|
2
|
+
*
|
3
|
+
* peepmem - Peep memory of other process
|
4
|
+
* https://github.com/kubo/ruby-peepmem
|
5
|
+
*
|
6
|
+
* Copyright (C) 2015 Kubo Takehiro <kubo@jiubao.org>
|
7
|
+
*
|
8
|
+
* Redistribution and use in source and binary forms, with or without
|
9
|
+
* modification, are permitted provided that the following conditions are met:
|
10
|
+
*
|
11
|
+
* 1. Redistributions of source code must retain the above copyright
|
12
|
+
* notice, this list of conditions and the following disclaimer.
|
13
|
+
*
|
14
|
+
* 2. Redistributions in binary form must reproduce the above
|
15
|
+
* copyright notice, this list of conditions and the following
|
16
|
+
* disclaimer in the documentation and/or other materials provided
|
17
|
+
* with the distribution.
|
18
|
+
*
|
19
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR
|
20
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
+
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
|
23
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
26
|
+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
27
|
+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
28
|
+
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
29
|
+
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
*
|
31
|
+
* The views and conclusions contained in the software and documentation
|
32
|
+
* are those of the authors and should not be interpreted as representing
|
33
|
+
* official policies, either expressed or implied, of the authors.
|
34
|
+
*/
|
35
|
+
#include <ruby.h>
|
36
|
+
#include <ruby/encoding.h>
|
37
|
+
#include <stdint.h>
|
38
|
+
#ifndef WIN32
|
39
|
+
#include <sys/types.h>
|
40
|
+
#include <sys/stat.h>
|
41
|
+
#include <unistd.h>
|
42
|
+
#include <fcntl.h>
|
43
|
+
#include <errno.h>
|
44
|
+
#endif
|
45
|
+
|
46
|
+
#ifndef MIN
|
47
|
+
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
48
|
+
#endif
|
49
|
+
|
50
|
+
/* Use this macro not to be parsed by yard. */
|
51
|
+
#define rb_define_method_nodoc rb_define_method
|
52
|
+
|
53
|
+
static VALUE cHandle;
|
54
|
+
static VALUE cPointer;
|
55
|
+
static rb_encoding *utf16le_encoding;
|
56
|
+
|
57
|
+
#ifdef WIN32
|
58
|
+
typedef HANDLE mem_handle_t;
|
59
|
+
#define INVALID_MEM_HANDLE (NULL)
|
60
|
+
#else
|
61
|
+
typedef int mem_handle_t;
|
62
|
+
#define INVALID_MEM_HANDLE (-1)
|
63
|
+
#endif
|
64
|
+
|
65
|
+
typedef struct peepmem_handle peepmem_handle_t;
|
66
|
+
typedef struct peepmem_pointer peepmem_pointer_t;
|
67
|
+
|
68
|
+
struct peepmem_handle {
|
69
|
+
VALUE self;
|
70
|
+
mem_handle_t mem_handle;
|
71
|
+
size_t (*read_mem)(peepmem_handle_t *, size_t, void *, size_t, int);
|
72
|
+
pid_t pid;
|
73
|
+
size_t addr;
|
74
|
+
size_t len;
|
75
|
+
char buf[4096];
|
76
|
+
};
|
77
|
+
|
78
|
+
struct peepmem_pointer {
|
79
|
+
peepmem_handle_t *handle;
|
80
|
+
size_t address;
|
81
|
+
};
|
82
|
+
|
83
|
+
static size_t read_mem_no_buffering(peepmem_handle_t *hndl, size_t address, void *buf, size_t buflen, int full);
|
84
|
+
static size_t read_mem_buffering(peepmem_handle_t *hndl, size_t address, void *buf, size_t buflen, int full);
|
85
|
+
|
86
|
+
/*
|
87
|
+
* call-seq:
|
88
|
+
* open(process_id)
|
89
|
+
*
|
90
|
+
* @param [Integer] process_id
|
91
|
+
* @return [Peepmem::Handle]
|
92
|
+
*/
|
93
|
+
static VALUE peepmem_s_open(VALUE klass, VALUE process_id)
|
94
|
+
{
|
95
|
+
VALUE argv[1];
|
96
|
+
|
97
|
+
argv[0] = process_id;
|
98
|
+
return rb_class_new_instance(1, argv, cHandle);
|
99
|
+
}
|
100
|
+
|
101
|
+
static void peepmem_handle_free(peepmem_handle_t *hndl)
|
102
|
+
{
|
103
|
+
if (hndl->mem_handle != INVALID_MEM_HANDLE) {
|
104
|
+
#ifdef WIN32
|
105
|
+
CloseHandle(hndl->mem_handle);
|
106
|
+
#else
|
107
|
+
close(hndl->mem_handle);
|
108
|
+
#endif
|
109
|
+
hndl->mem_handle = INVALID_MEM_HANDLE;
|
110
|
+
}
|
111
|
+
hndl->pid = -1;
|
112
|
+
}
|
113
|
+
|
114
|
+
static VALUE peepmem_handle_s_allocate(VALUE klass)
|
115
|
+
{
|
116
|
+
peepmem_handle_t *hndl;
|
117
|
+
VALUE obj;
|
118
|
+
|
119
|
+
obj = Data_Make_Struct(klass, peepmem_handle_t, NULL, peepmem_handle_free, hndl);
|
120
|
+
hndl->self = obj;
|
121
|
+
hndl->mem_handle = INVALID_MEM_HANDLE;
|
122
|
+
hndl->read_mem = read_mem_no_buffering;
|
123
|
+
hndl->pid = -1;
|
124
|
+
return obj;
|
125
|
+
}
|
126
|
+
|
127
|
+
/*
|
128
|
+
* call-seq:
|
129
|
+
* initialize(process_id)
|
130
|
+
*
|
131
|
+
* @private
|
132
|
+
*/
|
133
|
+
static VALUE peepmem_handle_initialize(VALUE self, VALUE process_id)
|
134
|
+
{
|
135
|
+
peepmem_handle_t *hndl = DATA_PTR(self);
|
136
|
+
pid_t pid = NUM2INT(process_id);
|
137
|
+
|
138
|
+
#ifdef WIN32
|
139
|
+
static BOOL se_debug_is_enabled = 0;
|
140
|
+
|
141
|
+
if (!se_debug_is_enabled) {
|
142
|
+
/* Enable SE_DEBUG_NAME privilege */
|
143
|
+
HANDLE hToken;
|
144
|
+
LUID luid;
|
145
|
+
TOKEN_PRIVILEGES tp;
|
146
|
+
|
147
|
+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
|
148
|
+
rb_raise(rb_eRuntimeError, "Failed to get the process token");
|
149
|
+
}
|
150
|
+
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
|
151
|
+
CloseHandle(hToken);
|
152
|
+
rb_raise(rb_eRuntimeError, "Failed to get SE_DEDUG_NAME privilege");
|
153
|
+
}
|
154
|
+
|
155
|
+
tp.PrivilegeCount = 1;
|
156
|
+
tp.Privileges[0].Luid = luid;
|
157
|
+
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
158
|
+
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
|
159
|
+
CloseHandle(hToken);
|
160
|
+
rb_raise(rb_eRuntimeError, "Failed to set SE_DEDUG_NAME privilege");
|
161
|
+
}
|
162
|
+
CloseHandle(hToken);
|
163
|
+
se_debug_is_enabled = 1;
|
164
|
+
}
|
165
|
+
|
166
|
+
hndl->mem_handle = OpenProcess(PROCESS_VM_READ, FALSE, pid);
|
167
|
+
if (hndl->mem_handle == NULL) {
|
168
|
+
if (GetLastError() == ERROR_ACCESS_DENIED) {
|
169
|
+
rb_raise(rb_eRuntimeError, "You need root privilege to open process %lu.", pid);
|
170
|
+
}
|
171
|
+
rb_raise(rb_eRuntimeError, "OpenProcess(pid = %d): %s", pid, rb_w32_strerror(-1));
|
172
|
+
}
|
173
|
+
#else
|
174
|
+
char buf[64];
|
175
|
+
|
176
|
+
sprintf(buf, "/proc/%d/mem", pid);
|
177
|
+
hndl->mem_handle = open(buf, O_RDONLY);
|
178
|
+
if (hndl->mem_handle == -1) {
|
179
|
+
if (errno == EACCES && getuid() != 1) {
|
180
|
+
rb_raise(rb_eRuntimeError, "You need root privilege to open '%s'.", buf);
|
181
|
+
}
|
182
|
+
rb_sys_fail("open");
|
183
|
+
}
|
184
|
+
fcntl(hndl->mem_handle, F_SETFD, fcntl(hndl->mem_handle, F_GETFD) | FD_CLOEXEC);
|
185
|
+
#endif
|
186
|
+
hndl->pid = pid;
|
187
|
+
return self;
|
188
|
+
}
|
189
|
+
|
190
|
+
/*
|
191
|
+
* call-seq:
|
192
|
+
* handle.process_id -> integer
|
193
|
+
*
|
194
|
+
*/
|
195
|
+
static VALUE peepmem_handle_get_process_id(VALUE self)
|
196
|
+
{
|
197
|
+
peepmem_handle_t *hndl = DATA_PTR(self);
|
198
|
+
return LONG2FIX(hndl->pid);
|
199
|
+
}
|
200
|
+
|
201
|
+
/*
|
202
|
+
* call-seq:
|
203
|
+
* handle[integer] -> Peepmem::Pointer
|
204
|
+
*
|
205
|
+
*/
|
206
|
+
static VALUE peepmem_handle_aref(VALUE self, VALUE pointer)
|
207
|
+
{
|
208
|
+
VALUE argv[2];
|
209
|
+
|
210
|
+
argv[0] = self;
|
211
|
+
argv[1] = pointer;
|
212
|
+
return rb_class_new_instance(2, argv, cPointer);
|
213
|
+
}
|
214
|
+
|
215
|
+
/*
|
216
|
+
* call-seq:
|
217
|
+
* handle.buffering -> Boolean
|
218
|
+
*
|
219
|
+
* @see buffering=
|
220
|
+
*/
|
221
|
+
static VALUE peepmem_handle_get_buffering(VALUE self)
|
222
|
+
{
|
223
|
+
peepmem_handle_t *hndl = DATA_PTR(self);
|
224
|
+
|
225
|
+
if (hndl->read_mem == read_mem_buffering) {
|
226
|
+
return Qtrue;
|
227
|
+
} else {
|
228
|
+
return Qfalse;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
/*
|
233
|
+
* call-seq:
|
234
|
+
* handle.buffering = Boolean
|
235
|
+
*
|
236
|
+
*/
|
237
|
+
static VALUE peepmem_handle_set_buffering(VALUE self, VALUE bool_val)
|
238
|
+
{
|
239
|
+
peepmem_handle_t *hndl = DATA_PTR(self);
|
240
|
+
|
241
|
+
if (RTEST(bool_val)) {
|
242
|
+
hndl->read_mem = read_mem_buffering;
|
243
|
+
hndl->len = 0;
|
244
|
+
} else {
|
245
|
+
hndl->read_mem = read_mem_no_buffering;
|
246
|
+
}
|
247
|
+
return bool_val;
|
248
|
+
}
|
249
|
+
|
250
|
+
/*
|
251
|
+
* call-seq:
|
252
|
+
* handle.inspect -> String
|
253
|
+
*
|
254
|
+
* @private
|
255
|
+
*/
|
256
|
+
static VALUE peepmem_handle_inspect(VALUE self)
|
257
|
+
{
|
258
|
+
peepmem_handle_t *hndl = DATA_PTR(self);
|
259
|
+
|
260
|
+
return rb_sprintf("#<%s: PID=%ld>", rb_obj_classname(self), (long)hndl->pid);
|
261
|
+
}
|
262
|
+
|
263
|
+
/*
|
264
|
+
* call-seq:
|
265
|
+
* handle.close
|
266
|
+
*
|
267
|
+
*/
|
268
|
+
static VALUE peepmem_handle_close(VALUE self)
|
269
|
+
{
|
270
|
+
peepmem_handle_t *hndl = DATA_PTR(self);
|
271
|
+
|
272
|
+
peepmem_handle_free(hndl);
|
273
|
+
return Qnil;
|
274
|
+
}
|
275
|
+
|
276
|
+
static void peepmem_pointer_mark(peepmem_pointer_t *ptr)
|
277
|
+
{
|
278
|
+
rb_gc_mark(ptr->handle->self);
|
279
|
+
}
|
280
|
+
|
281
|
+
static VALUE peepmem_pointer_s_allocate(VALUE klass)
|
282
|
+
{
|
283
|
+
peepmem_pointer_t *ptr;
|
284
|
+
|
285
|
+
return Data_Make_Struct(klass, peepmem_pointer_t, peepmem_pointer_mark, NULL, ptr);
|
286
|
+
}
|
287
|
+
|
288
|
+
/*
|
289
|
+
* call-seq:
|
290
|
+
* pointer.initialize(handle, address)
|
291
|
+
*
|
292
|
+
*/
|
293
|
+
static VALUE peepmem_pointer_initialize(VALUE self, VALUE handle, VALUE address)
|
294
|
+
{
|
295
|
+
peepmem_pointer_t *ptr = DATA_PTR(self);
|
296
|
+
peepmem_handle_t *hndl;
|
297
|
+
|
298
|
+
if (!rb_obj_is_instance_of(handle, cHandle)) {
|
299
|
+
rb_raise(rb_eArgError, "wrong argument type %s (expected %s)",
|
300
|
+
rb_obj_classname(handle), rb_class2name(cHandle));
|
301
|
+
}
|
302
|
+
Data_Get_Struct(handle, peepmem_handle_t, hndl);
|
303
|
+
ptr->handle = hndl;
|
304
|
+
ptr->address = NUM2SIZET(address);
|
305
|
+
return self;
|
306
|
+
}
|
307
|
+
|
308
|
+
/*
|
309
|
+
* call-seq:
|
310
|
+
* pointer.to_i -> address
|
311
|
+
*
|
312
|
+
* Returns address as a integer.
|
313
|
+
*
|
314
|
+
* @example
|
315
|
+
* handle = Peepmem.open(7658)
|
316
|
+
* pointer = handle[0x00400000]
|
317
|
+
* pointer.to_i # => 4194304
|
318
|
+
*
|
319
|
+
* @return [Integer]
|
320
|
+
*/
|
321
|
+
static VALUE peepmem_pointer_to_i(VALUE self)
|
322
|
+
{
|
323
|
+
peepmem_pointer_t *ptr = DATA_PTR(self);
|
324
|
+
|
325
|
+
return SIZET2NUM(ptr->address);
|
326
|
+
}
|
327
|
+
|
328
|
+
/*
|
329
|
+
* call-seq:
|
330
|
+
* pointer.to_s -> hex_string
|
331
|
+
*
|
332
|
+
* Returns address as a hexadecimal string.
|
333
|
+
*
|
334
|
+
* @example
|
335
|
+
* handle = Peepmem.open(7658)
|
336
|
+
* pointer = handle[0x00400000]
|
337
|
+
* pointer.to_s # => "0x00000000400000"
|
338
|
+
*
|
339
|
+
* @return [String]
|
340
|
+
*/
|
341
|
+
static VALUE peepmem_pointer_to_s(VALUE self)
|
342
|
+
{
|
343
|
+
peepmem_pointer_t *ptr = DATA_PTR(self);
|
344
|
+
|
345
|
+
return rb_sprintf("%p", (void*)ptr->address);
|
346
|
+
}
|
347
|
+
|
348
|
+
/*
|
349
|
+
* call-seq:
|
350
|
+
* pointer.inspect -> string
|
351
|
+
*
|
352
|
+
*/
|
353
|
+
static VALUE peepmem_pointer_inspect(VALUE self)
|
354
|
+
{
|
355
|
+
peepmem_pointer_t *ptr = DATA_PTR(self);
|
356
|
+
|
357
|
+
return rb_sprintf("#<%s:%p PID=%ld>", rb_obj_classname(self), (void*)ptr->address, (long)ptr->handle->pid);
|
358
|
+
}
|
359
|
+
|
360
|
+
/*
|
361
|
+
* call-seq:
|
362
|
+
* self + integer
|
363
|
+
*
|
364
|
+
* Returns a pointer which shifts to upper address.
|
365
|
+
*
|
366
|
+
* @example
|
367
|
+
* handle = Peepmem.open(7658)
|
368
|
+
* pointer = handle[0x00400000]
|
369
|
+
* pointer + 0x10 # => #<Peepmem::Pointer:0x00000000400010 PID=7658>
|
370
|
+
*
|
371
|
+
* @return [Peepmem::Pointer]
|
372
|
+
*/
|
373
|
+
static VALUE peepmem_pointer_add(VALUE lhs, VALUE rhs)
|
374
|
+
{
|
375
|
+
peepmem_pointer_t *ptr = DATA_PTR(lhs);
|
376
|
+
VALUE argv[2];
|
377
|
+
|
378
|
+
argv[0] = ptr->handle->self;
|
379
|
+
argv[1] = SIZET2NUM(ptr->address + NUM2SSIZET(rhs));
|
380
|
+
return rb_class_new_instance(2, argv, cPointer);
|
381
|
+
}
|
382
|
+
|
383
|
+
/*
|
384
|
+
* call-seq:
|
385
|
+
* self - integer
|
386
|
+
*
|
387
|
+
* Returns a pointer which shifts to lower address.
|
388
|
+
*
|
389
|
+
* @example
|
390
|
+
* handle = Peepmem.open(7658)
|
391
|
+
* pointer = handle[0x00400000]
|
392
|
+
* pointer - 0x10 # => #<Peepmem::Pointer:0x000000003ffff0 PID=7658>
|
393
|
+
*
|
394
|
+
* @return [Peepmem::Pointer]
|
395
|
+
*/
|
396
|
+
static VALUE peepmem_pointer_sub(VALUE lhs, VALUE rhs)
|
397
|
+
{
|
398
|
+
peepmem_pointer_t *ptr = DATA_PTR(lhs);
|
399
|
+
VALUE argv[2];
|
400
|
+
|
401
|
+
argv[0] = ptr->handle->self;
|
402
|
+
argv[1] = SIZET2NUM(ptr->address - NUM2SSIZET(rhs));
|
403
|
+
return rb_class_new_instance(2, argv, cPointer);
|
404
|
+
}
|
405
|
+
|
406
|
+
/* read process memory without buffering */
|
407
|
+
static size_t read_mem_no_buffering(peepmem_handle_t *hndl, size_t address, void *buf, size_t buflen, int full)
|
408
|
+
{
|
409
|
+
#ifdef WIN32
|
410
|
+
size_t readlen;
|
411
|
+
if (!ReadProcessMemory(hndl->mem_handle, (void*)address, buf, buflen, &readlen)) {
|
412
|
+
readlen = 0;
|
413
|
+
}
|
414
|
+
if (full && readlen != buflen) {
|
415
|
+
rb_raise(rb_eRuntimeError, "Cannot read the specified memory region");
|
416
|
+
}
|
417
|
+
return readlen;
|
418
|
+
#else
|
419
|
+
ssize_t readlen = pread(hndl->mem_handle, buf, buflen, address);
|
420
|
+
if (full && readlen != buflen) {
|
421
|
+
rb_raise(rb_eRuntimeError, "Cannot read the specified memory region");
|
422
|
+
}
|
423
|
+
return (readlen != -1) ? readlen : 0;
|
424
|
+
#endif
|
425
|
+
}
|
426
|
+
|
427
|
+
/* read process memory with buffering */
|
428
|
+
static size_t read_mem_buffering(peepmem_handle_t *hndl, size_t address, void *buf, size_t buflen, int full)
|
429
|
+
{
|
430
|
+
size_t readlen = 0;
|
431
|
+
size_t copylen;
|
432
|
+
|
433
|
+
if (hndl->addr <= address && address < hndl->addr + hndl->len) {
|
434
|
+
size_t offset = address - hndl->addr;
|
435
|
+
size_t copylen = MIN(buflen, hndl->len - offset);
|
436
|
+
|
437
|
+
memcpy(buf, hndl->buf + offset, copylen);
|
438
|
+
if (buflen == copylen) {
|
439
|
+
return copylen;
|
440
|
+
}
|
441
|
+
readlen = copylen;
|
442
|
+
address += copylen;
|
443
|
+
buf = (char*)buf + copylen;
|
444
|
+
buflen -= copylen;
|
445
|
+
}
|
446
|
+
if (buflen > sizeof(hndl->buf)) {
|
447
|
+
return readlen + read_mem_no_buffering(hndl, address, buf, buflen, full);
|
448
|
+
}
|
449
|
+
hndl->len = read_mem_no_buffering(hndl, address, hndl->buf, sizeof(hndl->buf), 0);
|
450
|
+
hndl->addr = address;
|
451
|
+
if (buflen <= hndl->len) {
|
452
|
+
copylen = buflen;
|
453
|
+
} else {
|
454
|
+
if (full) {
|
455
|
+
rb_raise(rb_eRuntimeError, "Cannot read the specified memory region");
|
456
|
+
}
|
457
|
+
copylen = hndl->len;
|
458
|
+
}
|
459
|
+
memcpy(buf, hndl->buf, copylen);
|
460
|
+
return readlen + copylen;
|
461
|
+
}
|
462
|
+
|
463
|
+
static int get_num(const char **fmt, const char *fmt_end, int default_value)
|
464
|
+
{
|
465
|
+
int has_number = 0;
|
466
|
+
int num = 0;
|
467
|
+
|
468
|
+
while (*fmt < fmt_end) {
|
469
|
+
switch (**fmt) {
|
470
|
+
case '0':
|
471
|
+
case '1':
|
472
|
+
case '2':
|
473
|
+
case '3':
|
474
|
+
case '4':
|
475
|
+
case '5':
|
476
|
+
case '6':
|
477
|
+
case '7':
|
478
|
+
case '8':
|
479
|
+
case '9':
|
480
|
+
num *= 10;
|
481
|
+
num += **fmt - '0';
|
482
|
+
(*fmt)++;
|
483
|
+
has_number = 1;
|
484
|
+
continue;
|
485
|
+
}
|
486
|
+
break;
|
487
|
+
}
|
488
|
+
if (has_number) {
|
489
|
+
return num;
|
490
|
+
} else {
|
491
|
+
return default_value;
|
492
|
+
}
|
493
|
+
}
|
494
|
+
|
495
|
+
static VALUE read_object(peepmem_handle_t *hndl, size_t *address, char type, int length)
|
496
|
+
{
|
497
|
+
VALUE obj = Qnil;
|
498
|
+
VALUE ptr = Qnil;
|
499
|
+
union {
|
500
|
+
int8_t d8;
|
501
|
+
int16_t d16;
|
502
|
+
int32_t d32;
|
503
|
+
int64_t d64;
|
504
|
+
uint8_t u8;
|
505
|
+
uint16_t u16;
|
506
|
+
uint32_t u32;
|
507
|
+
uint64_t u64;
|
508
|
+
float flt;
|
509
|
+
double dbl;
|
510
|
+
} val;
|
511
|
+
|
512
|
+
switch (type) {
|
513
|
+
case 'd':
|
514
|
+
hndl->read_mem(hndl, *address, &val, length, 1);
|
515
|
+
switch (length) {
|
516
|
+
case 1:
|
517
|
+
obj = INT2FIX(val.d8);
|
518
|
+
break;
|
519
|
+
case 2:
|
520
|
+
obj = INT2FIX(val.d16);
|
521
|
+
break;
|
522
|
+
case 4:
|
523
|
+
obj = INT2NUM(val.d32);
|
524
|
+
break;
|
525
|
+
case 8:
|
526
|
+
obj = LL2NUM(val.d64);
|
527
|
+
break;
|
528
|
+
}
|
529
|
+
break;
|
530
|
+
case 'u':
|
531
|
+
hndl->read_mem(hndl, *address, &val, length, 1);
|
532
|
+
switch (length) {
|
533
|
+
case 1:
|
534
|
+
obj = INT2FIX(val.u8);
|
535
|
+
break;
|
536
|
+
case 2:
|
537
|
+
obj = INT2FIX(val.u16);
|
538
|
+
break;
|
539
|
+
case 4:
|
540
|
+
obj = UINT2NUM(val.u32);
|
541
|
+
break;
|
542
|
+
case 8:
|
543
|
+
obj = ULL2NUM(val.u64);
|
544
|
+
break;
|
545
|
+
}
|
546
|
+
break;
|
547
|
+
case 'f':
|
548
|
+
hndl->read_mem(hndl, *address, &val, length, 1);
|
549
|
+
switch (length) {
|
550
|
+
case 4:
|
551
|
+
obj = DBL2NUM(val.flt);
|
552
|
+
break;
|
553
|
+
case 8:
|
554
|
+
obj = DBL2NUM(val.dbl);
|
555
|
+
break;
|
556
|
+
}
|
557
|
+
break;
|
558
|
+
case 's':
|
559
|
+
if (length == 0) {
|
560
|
+
char c;
|
561
|
+
obj = rb_str_buf_new(64);
|
562
|
+
hndl->read_mem(hndl, *address, &c, 1, 1);
|
563
|
+
*address += 1;
|
564
|
+
while (c != 0) {
|
565
|
+
rb_str_buf_cat(obj, &c, 1);
|
566
|
+
hndl->read_mem(hndl, *address, &c, 1, 1);
|
567
|
+
*address += 1;
|
568
|
+
}
|
569
|
+
} else {
|
570
|
+
obj = rb_str_buf_new(length);
|
571
|
+
rb_str_set_len(obj, length);
|
572
|
+
hndl->read_mem(hndl, *address, RSTRING_PTR(obj), length, 1);
|
573
|
+
}
|
574
|
+
OBJ_TAINT(obj);
|
575
|
+
break;
|
576
|
+
case 'w':
|
577
|
+
if (length == 0) {
|
578
|
+
uint16_t c;
|
579
|
+
obj = rb_str_buf_new(64);
|
580
|
+
hndl->read_mem(hndl, *address, &c, 2, 1);
|
581
|
+
*address += 2;
|
582
|
+
while (c != 0) {
|
583
|
+
rb_str_buf_cat(obj, (char*)&c, 2);
|
584
|
+
hndl->read_mem(hndl, *address, &c, 2, 1);
|
585
|
+
*address += 2;
|
586
|
+
}
|
587
|
+
} else {
|
588
|
+
obj = rb_str_buf_new(length);
|
589
|
+
rb_str_set_len(obj, length);
|
590
|
+
hndl->read_mem(hndl, *address, RSTRING_PTR(obj), length, 1);
|
591
|
+
}
|
592
|
+
OBJ_TAINT(obj);
|
593
|
+
rb_enc_associate(obj, utf16le_encoding);
|
594
|
+
break;
|
595
|
+
case 'p':
|
596
|
+
hndl->read_mem(hndl, *address, &val, length, 1);
|
597
|
+
switch (length) {
|
598
|
+
case 4:
|
599
|
+
ptr = UINT2NUM(val.u32);
|
600
|
+
break;
|
601
|
+
case 8:
|
602
|
+
ptr = ULL2NUM(val.u64);
|
603
|
+
break;
|
604
|
+
}
|
605
|
+
obj = peepmem_pointer_s_allocate(cPointer);
|
606
|
+
peepmem_pointer_initialize(obj, hndl->self, ptr);
|
607
|
+
break;
|
608
|
+
}
|
609
|
+
*address += length;
|
610
|
+
return obj;
|
611
|
+
}
|
612
|
+
|
613
|
+
static VALUE read_directive(peepmem_handle_t *hndl, size_t *address, const char **fmt, const char *fmt_end)
|
614
|
+
{
|
615
|
+
char type;
|
616
|
+
int count;
|
617
|
+
int length;
|
618
|
+
|
619
|
+
read_again:
|
620
|
+
count = get_num(fmt, fmt_end, 0);
|
621
|
+
type = **fmt;
|
622
|
+
switch (type) {
|
623
|
+
case 'd':
|
624
|
+
case 'u':
|
625
|
+
(*fmt)++;
|
626
|
+
if (*fmt < fmt_end && **fmt == 'L') {
|
627
|
+
(*fmt)++;
|
628
|
+
length = sizeof(long);
|
629
|
+
} else if (*fmt < fmt_end && **fmt == 'P') {
|
630
|
+
(*fmt)++;
|
631
|
+
length = sizeof(void *);
|
632
|
+
} else {
|
633
|
+
length = get_num(fmt, fmt_end, 4);
|
634
|
+
}
|
635
|
+
switch (length) {
|
636
|
+
case 1:
|
637
|
+
case 2:
|
638
|
+
case 4:
|
639
|
+
case 8:
|
640
|
+
break;
|
641
|
+
default:
|
642
|
+
rb_raise(rb_eArgError, "wrong format");
|
643
|
+
}
|
644
|
+
break;
|
645
|
+
case 'f':
|
646
|
+
(*fmt)++;
|
647
|
+
length = get_num(fmt, fmt_end, 8);
|
648
|
+
switch (length) {
|
649
|
+
case 4:
|
650
|
+
case 8:
|
651
|
+
break;
|
652
|
+
default:
|
653
|
+
rb_raise(rb_eArgError, "wrong format");
|
654
|
+
}
|
655
|
+
break;
|
656
|
+
case 's':
|
657
|
+
(*fmt)++;
|
658
|
+
length = get_num(fmt, fmt_end, 0);
|
659
|
+
break;
|
660
|
+
case 'w':
|
661
|
+
if (utf16le_encoding == NULL) {
|
662
|
+
utf16le_encoding = rb_enc_find("UTF-16LE");
|
663
|
+
}
|
664
|
+
(*fmt)++;
|
665
|
+
length = get_num(fmt, fmt_end, 0) * 2;
|
666
|
+
break;
|
667
|
+
case 'p':
|
668
|
+
(*fmt)++;
|
669
|
+
length = get_num(fmt, fmt_end, sizeof(size_t));
|
670
|
+
switch (length) {
|
671
|
+
case 4:
|
672
|
+
case 8:
|
673
|
+
break;
|
674
|
+
default:
|
675
|
+
rb_raise(rb_eArgError, "wrong format");
|
676
|
+
}
|
677
|
+
break;
|
678
|
+
case '>':
|
679
|
+
(*fmt)++;
|
680
|
+
length = get_num(fmt, fmt_end, 1);
|
681
|
+
break;
|
682
|
+
default:
|
683
|
+
rb_raise(rb_eArgError, "wrong format");
|
684
|
+
}
|
685
|
+
|
686
|
+
while (*fmt < fmt_end) {
|
687
|
+
switch (**fmt) {
|
688
|
+
case ' ':
|
689
|
+
case '\t':
|
690
|
+
case '\r':
|
691
|
+
case '\n':
|
692
|
+
(*fmt)++;
|
693
|
+
continue;
|
694
|
+
}
|
695
|
+
break;
|
696
|
+
}
|
697
|
+
if (type == '>') {
|
698
|
+
if (*fmt == fmt_end) {
|
699
|
+
return Qundef;
|
700
|
+
}
|
701
|
+
if (count == 0) {
|
702
|
+
*address += length;
|
703
|
+
} else {
|
704
|
+
*address += count * length;
|
705
|
+
}
|
706
|
+
goto read_again;
|
707
|
+
}
|
708
|
+
|
709
|
+
if (count == 0) {
|
710
|
+
return read_object(hndl, address, type, length);
|
711
|
+
} else {
|
712
|
+
VALUE ary = rb_ary_new2(count);
|
713
|
+
int i;
|
714
|
+
|
715
|
+
for (i = 0; i < count; i++) {
|
716
|
+
rb_ary_push(ary, read_object(hndl, address, type, length));
|
717
|
+
}
|
718
|
+
return ary;
|
719
|
+
}
|
720
|
+
}
|
721
|
+
|
722
|
+
/*
|
723
|
+
* call-seq:
|
724
|
+
* pointer[format]
|
725
|
+
*
|
726
|
+
* Reads memory according to the format string. The format string consists
|
727
|
+
* of one or more directives separated by spaces. A directive consists of
|
728
|
+
* (optional) <tt>count</tt>, <tt>data type</tt> and (optional) <tt>data
|
729
|
+
* length</tt>. If a format string conststs of one directive without
|
730
|
+
* <tt>count</tt>, it returns an object. Otherwise, it returns an array.
|
731
|
+
*
|
732
|
+
* Directive | Returns | Meaning
|
733
|
+
* -----------------------------------------------------------------
|
734
|
+
* d1 | Integer | 8-bit signed integer
|
735
|
+
* d2 | Integer | 16-bit signed integer
|
736
|
+
* d4 | Integer | 32-bit signed integer
|
737
|
+
* d8 | Integer | 64-bit signed integer
|
738
|
+
* dL | Integer | signed integer whose length is same with long
|
739
|
+
* dP | Integer | signed integer whose length is same with poiner
|
740
|
+
* u1 | Integer | 8-bit unsigned integer
|
741
|
+
* u2 | Integer | 16-bit unsigned integer
|
742
|
+
* u4 | Integer | 32-bit unsigned integer
|
743
|
+
* u8 | Integer | 64-bit unsigned integer
|
744
|
+
* uL | Integer | unsigned integer whose length is same with long
|
745
|
+
* uP | Integer | unsigned integer whose length is same with pointer
|
746
|
+
* f4 | Double | 32-bit floating point number (float)
|
747
|
+
* f8 | Double | 64-bit floating point number (double)
|
748
|
+
* s | String | null-terminated string
|
749
|
+
* s(number) | String | string whose length is specified by (number)
|
750
|
+
* w | String | null-terminated string, UTF16-LE encoding
|
751
|
+
* w(number) | String | string whose length is specified by (number), UTF16-LE encoding
|
752
|
+
* p | Peepmem::Pointer | pointer
|
753
|
+
* > | | skip one byte
|
754
|
+
* >(number) | | skip bytes specified by (number)
|
755
|
+
*
|
756
|
+
* @example
|
757
|
+
* pointer = Peepmem.open(7658)[0x00400000]
|
758
|
+
* pointer['d4']
|
759
|
+
* # => 32-bit signed integer
|
760
|
+
* pointer['1d4']
|
761
|
+
* # => [32-bit signed integer]
|
762
|
+
* pointer['2d4']
|
763
|
+
* # => [32-bit signed integer, 32-bit signed integer]
|
764
|
+
* pointer['u4 u2 >2 f8'] # '>2': skip two-byte padding
|
765
|
+
* # => [32-bit unsigned integer, 16-bit unsigned integer, double]
|
766
|
+
*/
|
767
|
+
static VALUE peepmem_pointer_aref(VALUE self, VALUE format)
|
768
|
+
{
|
769
|
+
peepmem_pointer_t *ptr = DATA_PTR(self);
|
770
|
+
const char *fmt, *fmt_end;
|
771
|
+
size_t address = ptr->address;
|
772
|
+
volatile VALUE rv;
|
773
|
+
VALUE obj;
|
774
|
+
|
775
|
+
SafeStringValue(format);
|
776
|
+
|
777
|
+
fmt = RSTRING_PTR(format);
|
778
|
+
fmt_end = fmt + RSTRING_LEN(format);
|
779
|
+
obj = read_directive(ptr->handle, &address, &fmt, fmt_end);
|
780
|
+
if (fmt >= fmt_end) {
|
781
|
+
if (obj == Qundef) {
|
782
|
+
rb_raise(rb_eArgError, "wrong format");
|
783
|
+
}
|
784
|
+
return obj;
|
785
|
+
}
|
786
|
+
rv = rb_ary_new4(1, &obj);
|
787
|
+
do {
|
788
|
+
obj = read_directive(ptr->handle, &address, &fmt, fmt_end);
|
789
|
+
if (RB_TYPE_P(obj, T_ARRAY)) {
|
790
|
+
rb_ary_concat(rv, obj);
|
791
|
+
} else if (obj != Qundef) {
|
792
|
+
rb_ary_push(rv, obj);
|
793
|
+
}
|
794
|
+
} while (fmt < fmt_end);
|
795
|
+
|
796
|
+
return rv;
|
797
|
+
}
|
798
|
+
|
799
|
+
void Init_peepmem(void)
|
800
|
+
{
|
801
|
+
VALUE mPeepmem = rb_define_module("Peepmem");
|
802
|
+
|
803
|
+
cHandle = rb_define_class_under(mPeepmem, "Handle", rb_cObject);
|
804
|
+
cPointer = rb_define_class_under(mPeepmem, "Pointer", rb_cObject);
|
805
|
+
|
806
|
+
rb_define_singleton_method(mPeepmem, "open", peepmem_s_open, 1);
|
807
|
+
|
808
|
+
rb_define_alloc_func(cHandle, peepmem_handle_s_allocate);
|
809
|
+
rb_define_private_method(cHandle, "initialize", peepmem_handle_initialize, 1);
|
810
|
+
rb_define_method(cHandle, "process_id", peepmem_handle_get_process_id, 0);
|
811
|
+
rb_define_method(cHandle, "[]", peepmem_handle_aref, 1);
|
812
|
+
rb_define_method(cHandle, "buffering", peepmem_handle_get_buffering, 0);
|
813
|
+
rb_define_method(cHandle, "buffering=", peepmem_handle_set_buffering, 1);
|
814
|
+
rb_define_method_nodoc(cHandle, "inspect", peepmem_handle_inspect, 0);
|
815
|
+
rb_define_method(cHandle, "close", peepmem_handle_close, 0);
|
816
|
+
|
817
|
+
rb_define_alloc_func(cPointer, peepmem_pointer_s_allocate);
|
818
|
+
rb_define_private_method(cPointer, "initialize", peepmem_pointer_initialize, 2);
|
819
|
+
rb_define_method(cPointer, "to_i", peepmem_pointer_to_i, 0);
|
820
|
+
rb_define_method(cPointer, "to_s", peepmem_pointer_to_s, 0);
|
821
|
+
rb_define_method_nodoc(cPointer, "inspect", peepmem_pointer_inspect, 0);
|
822
|
+
rb_define_method(cPointer, "+", peepmem_pointer_add, 1);
|
823
|
+
rb_define_method(cPointer, "-", peepmem_pointer_sub, 1);
|
824
|
+
rb_define_method(cPointer, "[]", peepmem_pointer_aref, 1);
|
825
|
+
}
|
data/lib/peepmem.rb
ADDED
data/peepmem.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'peepmem/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "peepmem"
|
8
|
+
spec.version = Peepmem::VERSION
|
9
|
+
spec.authors = ["Kubo Takehiro"]
|
10
|
+
spec.email = ["kubo@jiubao.org"]
|
11
|
+
spec.extensions = ["ext/peepmem/extconf.rb"]
|
12
|
+
spec.summary = %q{Peep memory of another process.}
|
13
|
+
spec.homepage = "https://github.com/kubo/ruby-peepmem"
|
14
|
+
spec.license = "2-clause BSD-style"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rake-compiler"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: peepmem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kubo Takehiro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake-compiler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- kubo@jiubao.org
|
58
|
+
executables: []
|
59
|
+
extensions:
|
60
|
+
- ext/peepmem/extconf.rb
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- ext/peepmem/extconf.rb
|
69
|
+
- ext/peepmem/peepmem.c
|
70
|
+
- lib/peepmem.rb
|
71
|
+
- lib/peepmem/version.rb
|
72
|
+
- peepmem.gemspec
|
73
|
+
homepage: https://github.com/kubo/ruby-peepmem
|
74
|
+
licenses:
|
75
|
+
- 2-clause BSD-style
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.4.5
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Peep memory of another process.
|
97
|
+
test_files: []
|
98
|
+
has_rdoc:
|