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__ |