File Coverage

blib/lib/Proc/Memory.pm
Criterion Covered Total %
statement 55 66 83.3
branch 12 22 54.5
condition 1 5 20.0
subroutine 13 15 86.6
pod 5 7 71.4
total 86 115 74.7


line stmt bran cond sub pod time code
1 3     3   167564 use strict;
  3         8  
  3         83  
2 3     3   16 use warnings;
  3         6  
  3         134  
3             package Proc::Memory;
4              
5             # ABSTRACT: Peek/Poke other processes' address spaces
6             our $VERSION = '0.008'; # VERSION
7              
8 3     3   17 use Carp;
  3         7  
  3         169  
9 3     3   847 use Sentinel;
  3         2450  
  3         145  
10 3     3   21 use Scalar::Util 'looks_like_number';
  3         5  
  3         126  
11 3     3   701 use Alien::libvas;
  3         107830  
  3         26  
12 3         17 use Inline 'C' => 'DATA' =>
13 3     3   20015 enable => 'autowrap';
  3         38826  
14 3     3   347 use Inline 0.56 with => 'Alien::libvas';
  3         56  
  3         13  
15              
16             =pod
17              
18             =encoding utf8
19              
20             =head1 NAME
21              
22             Proc::Memory - Peek/Poke into processes' address spaces
23              
24             =head1 SYNOPSIS
25              
26             use Proc::Memory;
27              
28             my $mem = Proc::Memory->new(pid => $$);
29              
30             my $byte = $mem->peek(0x1000);
31             my $u32 = $mem->read(0x1000, 4);
32             $mem->poke(0x1000, 'L') = 12;
33              
34              
35             =head1 DESCRIPTION
36              
37             PEEK/POKE are a BASIC programming language extension for reading and writing memory at a specified address across process boundaries. This module brings similiar capability to Perl.
38              
39             Eventually, Memory searching capability will also be added.
40              
41             =head1 IMPLEMENTATION
42              
43             The module is a Perlish wrapper for L and doesn't expose any extra functionality. L claims support for following backends:
44              
45             • win32 - Windows API's {Read,Write}ProcessMemory
46             • mach - Mach Virtual Memory API (vm_copy) - macOS and GNU Hurd
47             • process_vm - process_vm_{readv, writev} on Linux 3.2+
48             • procfs-mem - /proc/$pid/mem on Linux and some BSDs
49             • procfs-as - /proc/$pid/as on SunOS/Solaris
50             • ptrace - ptrace(2), available on many Unices
51             • memcpy - Trivial implementation that doesn't supports foreign address spaces
52              
53             I am not able to extensively test all these configurations (or test at all for Solaris). Continous Integration is set up for the Windows, macOS and Linux backends and they should work well. Additionally CPAN testers test it across a multitude of BSD and Linux systems. Filing issues (Preferably on Github) about more exotic systems is more than welcome!
54              
55             =head1 METHODS AND ARGUMENTS
56              
57             =over 4
58              
59             =item new(pid)
60              
61             Constructs a new Proc::Memory instance.
62              
63             =cut
64              
65             sub new {
66 3     3 1 2262048 my $class = shift;
67 3         25 my @opts = @_;
68 3 100       26 unshift @opts, 'pid' if @_ % 2 == 1;
69              
70 3         17 my $self = {
71             @opts
72             };
73              
74             looks_like_number $self->{pid}
75 3 50       27 or croak q/Pid isn't numeric/;
76              
77             $self->{vas} = xs_vas_open($self->{pid}, 0)
78 3 50       32 or do {
79 0 0       0 if (kill 0, $self->{pid}) {
80 0         0 croak "PID doesn't exist"
81             } else {
82 0         0 croak "Process access permission denied"
83             }
84             };
85              
86 3         11 bless $self, $class;
87 3         18 return $self;
88             }
89              
90             =item peek(addr [, 'pack-string'])
91              
92             Peeks at the given memory address. C defaults to C<'C'> (A single byte)
93              
94             =cut
95              
96             sub peek {
97 0     0 1 0 my $self = shift;
98 0         0 my $addr = shift;
99 0   0     0 my $fmt = shift // 'C';
100 0 0       0 $fmt eq 'C'
101             or croak 'Pack strings not supported yet';
102              
103 0         0 my $buf = xs_vas_read($self->{vas}, $addr, 1);
104 0         0 return $buf;
105             }
106              
107              
108              
109             =item poke(addr [, 'pack-string']) = $value # or = ($a, $b)
110              
111             Pokes a given memory address. If no pack-string is given, the rvalue is written as is
112              
113             =cut
114              
115             sub get_poke {
116 0     0 0 0 carp 'Useless use of poke';
117 0         0 undef;
118             }
119             sub set_poke {
120 3     3 0 7 my @args = @{+shift};
  3         9  
121 3         8 my $self = shift @args;
122 3         6 my $buf = shift;
123 3 50       13 my $addr = shift @args or croak 'Address must be specified';
124 3 100       11 if (my $fmt = shift @args) {
125 2 100       13 $buf = pack($fmt, ref($buf) eq 'ARRAY' ? @{$buf} : $buf);
  1         5  
126             }
127              
128 3         33 my $nbytes = xs_vas_write($self->{vas}, $addr, $buf, length $buf);
129 3 50       16 return $nbytes >= 0 ? $nbytes : undef;
130             }
131              
132             sub poke :lvalue {
133 3 50   3 1 4768 defined wantarray or croak 'Useless use of poke';
134 3         28 sentinel obj => [@_], get => \&get_poke, set => \&set_poke
135             }
136              
137             =item read(addr, size)
138              
139             Reads size bytes from given memory address.
140              
141             =cut
142              
143             #SV *xs_vas_read(void* vas, unsigned long src, size_t size) {
144             sub read {
145 3     3 1 11081 my $self = shift;
146 3         10 my $addr = shift;
147 3         7 my $size = shift;
148              
149 3         50 my $buf = xs_vas_read($self->{vas}, $addr, $size);
150 3         16 return $buf;
151             }
152              
153             =item write(addr, buf [, count])
154              
155             Writes C to C
156              
157             =cut
158              
159             #ssize_t xs_vas_write(void* vas, unsigned long dst, SV *sv) {
160             sub write {
161 1     1 1 6303 my $self = shift;
162 1         5 my $addr = shift;
163 1         3 my $buf = shift;
164 1   33     13 my $bytes = shift || length $buf;
165              
166 1         12 my $nbytes = xs_vas_write($self->{vas}, $addr, $buf, $bytes);
167 1 50       7 return $nbytes >= 0 ? $nbytes : undef;
168             }
169              
170             =item tie(addr, 'pack-string')
171              
172             Returns a tied variable which can be used like any other variable.
173             To be implemented
174              
175             =cut
176              
177             =item search('pack-string')
178              
179             To be implemented when libvas provides it
180              
181             =cut
182              
183             Inline->init();
184             1;
185              
186             =back
187              
188             =head1 GIT REPOSITORY
189              
190             L
191              
192             =head1 SEE ALSO
193              
194             L
195             L
196              
197             =head1 AUTHOR
198              
199             Ahmad Fatoum C<< >>, L
200              
201             =head1 COPYRIGHT AND LICENSE
202              
203             Copyright (C) 2016 Ahmad Fatoum
204              
205             This library is free software; you can redistribute it and/or modify
206             it under the same terms as Perl itself.
207              
208             =cut
209              
210             __DATA__