peepmem 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: