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 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
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ *~
15
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in peepmem.gemspec
4
+ gemspec
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,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rake/extensiontask"
4
+
5
+ Rake::ExtensionTask.new("peepmem") do |ext|
6
+ ext.lib_dir = "lib/peepmem"
7
+ end
@@ -0,0 +1,3 @@
1
+ require "mkmf"
2
+
3
+ create_makefile("peepmem/peepmem")
@@ -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
@@ -0,0 +1,6 @@
1
+ require "peepmem/version"
2
+ require "peepmem/peepmem"
3
+
4
+ module Peepmem
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,3 @@
1
+ module Peepmem
2
+ VERSION = "0.0.1"
3
+ end
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: