Ptrace 0.9.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.
- data/ChangeLog +6 -0
- data/LICENSE +674 -0
- data/README +105 -0
- data/examples/kill.rb +14 -0
- data/examples/peekpoke.rb +53 -0
- data/examples/regs.rb +47 -0
- data/examples/syscall.rb +33 -0
- data/lib/Ptrace.rb +571 -0
- data/module/Ptrace.c +683 -0
- data/module/Ptrace.h +51 -0
- data/module/extconf.rb +11 -0
- data/module/rdoc_input/Ptrace_ext.rb +120 -0
- data/module/ruby_compat.c +17 -0
- data/module/ruby_compat.h +28 -0
- data/tests/ut_ptrace.rb +597 -0
- metadata +83 -0
data/README
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
Ptrace
|
3
|
+
|
4
|
+
A Ruby C extension (and gem) for the POSIX ptrace facility.
|
5
|
+
|
6
|
+
NOTE: This is currently alpha software, and is released only to publish the
|
7
|
+
API of the module. This should not be used in production software, as
|
8
|
+
many features are incomplete.
|
9
|
+
|
10
|
+
BUILD
|
11
|
+
-----
|
12
|
+
|
13
|
+
The standard C extension build process is used:
|
14
|
+
|
15
|
+
bash# ruby extconf.rb
|
16
|
+
bash# make
|
17
|
+
|
18
|
+
Note that the Ruby headers must be installed. On Ubuntu, these are in the
|
19
|
+
ruby-dev or ruby1.9-dev package.
|
20
|
+
|
21
|
+
|
22
|
+
The gem is built using the standard gem build command:
|
23
|
+
|
24
|
+
bash# gem build Ptrace.gemspec
|
25
|
+
|
26
|
+
|
27
|
+
The top-level Makefile supports each of these builds with the commands
|
28
|
+
'make' and 'make gem'.
|
29
|
+
|
30
|
+
bash# make
|
31
|
+
# builds C extension
|
32
|
+
bash# make gem
|
33
|
+
# builds the gem
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
EXAMPLES
|
38
|
+
|
39
|
+
Extended examples are provided in the 'examples' directory. The following
|
40
|
+
code snippets give a brief overview of using the Ptrace extension.
|
41
|
+
|
42
|
+
# attach to process
|
43
|
+
pid = 10167 # just an example, use a real PID!
|
44
|
+
tgt = Ptrace::Target.attach(pid)
|
45
|
+
# terminate process
|
46
|
+
tgt.kill
|
47
|
+
|
48
|
+
# launch process
|
49
|
+
cmd = './a.out'
|
50
|
+
tgt = Ptrace::Target.launch cmd
|
51
|
+
# detach
|
52
|
+
tgt.detach
|
53
|
+
|
54
|
+
# step first 10 instructions, printing general registers
|
55
|
+
tgt = Ptrace::Target.launch cmd
|
56
|
+
10.times do |i|
|
57
|
+
puts "DEBUGGER STEP #{i}"
|
58
|
+
# print registers
|
59
|
+
tgt.regs.read.each { |name, val| puts "%s: %016X" % name, val }
|
60
|
+
tgt.step
|
61
|
+
end
|
62
|
+
# continue process
|
63
|
+
tgt.cont
|
64
|
+
|
65
|
+
|
66
|
+
# print register contents on syscall in/out
|
67
|
+
tgt = Ptrace::Target.launch cmd
|
68
|
+
cont = true
|
69
|
+
while cont
|
70
|
+
begin
|
71
|
+
tgt.syscall
|
72
|
+
puts "IN: #{tgt.regs.read.inspect}"
|
73
|
+
tgt.syscall
|
74
|
+
puts "OUT: #{tgt.regs.read.inspect}"
|
75
|
+
rescue Ptrace::InvalidProcessError
|
76
|
+
cont = false
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
# step first ten instructions, printing bytes at EIP and ESP
|
82
|
+
tgt = Ptrace::Target.launch cmd
|
83
|
+
10.times do |i|
|
84
|
+
sleep 1
|
85
|
+
begin
|
86
|
+
tgt.step
|
87
|
+
|
88
|
+
regs = tgt.regs.read
|
89
|
+
ip = (regs.include? 'rip') ? regs['rip'] : regs['eip']
|
90
|
+
puts ("DEBUGGER STEP %d: %X in %d" % [i, ip, tgt.pid])
|
91
|
+
|
92
|
+
v = tgt.text.peek(ip)
|
93
|
+
bytes = [v, v >> 8, v >> 12, v >> 16].map { |byte| byte & 0xFF }
|
94
|
+
puts " BYTES AT EIP: #{bytes.map{ |byte| "%02X" % byte }.join(' ')}"
|
95
|
+
|
96
|
+
esp_name = (regs.include? 'rsp') ? 'rsp' : 'esp'
|
97
|
+
v = tgt.data.peek(regs[esp_name])
|
98
|
+
bytes = [v, v >> 8, v >> 12, v >> 16].map { |byte| byte & 0xFF }
|
99
|
+
puts " BYTES AT ESP: #{bytes.map{ |byte| "%02X" % byte }.join(' ')}"
|
100
|
+
|
101
|
+
# Modify ESP, just because we can
|
102
|
+
puts " ...WRITING ESP..."
|
103
|
+
tgt.data.poke(regs[esp_name], v + 0x100)
|
104
|
+
end
|
105
|
+
tgt.cont
|
data/examples/kill.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'Ptrace'
|
4
|
+
|
5
|
+
if __FILE__ == $0
|
6
|
+
|
7
|
+
cmd = ARGV.join(' ')
|
8
|
+
|
9
|
+
tgt = Ptrace::Target.launch cmd
|
10
|
+
return if not tgt
|
11
|
+
|
12
|
+
puts "launched CMD #{cmd} as #{tgt.pid}"
|
13
|
+
10.times do |i|
|
14
|
+
sleep 1
|
15
|
+
begin
|
16
|
+
tgt.step
|
17
|
+
|
18
|
+
regs = tgt.regs.read
|
19
|
+
ip = (regs.include? 'rip') ? regs['rip'] : regs['eip']
|
20
|
+
puts ("DEBUGGER STEP %d: %X in %d" % [i, ip, tgt.pid])
|
21
|
+
|
22
|
+
v = tgt.text.peek(ip)
|
23
|
+
bytes = [v, v >> 8, v >> 12, v >> 16].map { |byte| byte & 0xFF }
|
24
|
+
puts " BYTES AT EIP: #{bytes.map{ |byte| "%02X" % byte }.join(' ')}"
|
25
|
+
|
26
|
+
esp_name = (regs.include? 'rsp') ? 'rsp' : 'esp'
|
27
|
+
v = tgt.data.peek(regs[esp_name])
|
28
|
+
bytes = [v, v >> 8, v >> 12, v >> 16].map { |byte| byte & 0xFF }
|
29
|
+
puts " BYTES AT ESP: #{bytes.map{ |byte| "%02X" % byte }.join(' ')}"
|
30
|
+
|
31
|
+
puts " ...WRITING ESP..."
|
32
|
+
tgt.data.poke(regs[esp_name], v + 0x100)
|
33
|
+
|
34
|
+
regs = tgt.regs.read
|
35
|
+
v1 = tgt.data.peek(regs[esp_name])
|
36
|
+
bytes = [v1, v1 >> 8, v1 >> 12, v1 >> 16].map { |byte| byte & 0xFF }
|
37
|
+
puts " BYTES AT EBP: #{bytes.map{ |byte| "%02X" % byte }.join(' ')}"
|
38
|
+
|
39
|
+
puts " ...REVERTING ESP..."
|
40
|
+
tgt.data.poke(regs[esp_name], v)
|
41
|
+
|
42
|
+
regs = tgt.regs.read
|
43
|
+
v2 = tgt.data.peek(regs[esp_name])
|
44
|
+
bytes = [v2, v2 >> 8, v2 >> 12, v2 >> 16].map { |byte| byte & 0xFF }
|
45
|
+
puts " BYTES AT EBP: #{bytes.map{ |byte| "%02X" % byte }.join(' ')}"
|
46
|
+
rescue Exception => e
|
47
|
+
puts e.message
|
48
|
+
puts e.backtrace.join("\n")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
puts "DEBUGGER RESUME"
|
52
|
+
tgt.cont
|
53
|
+
end
|
data/examples/regs.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# TODO : read, write regs
|
3
|
+
|
4
|
+
require 'Ptrace'
|
5
|
+
|
6
|
+
if __FILE__ == $0
|
7
|
+
|
8
|
+
cmd = ARGV.join(' ')
|
9
|
+
|
10
|
+
tgt = Ptrace::Target.launch cmd
|
11
|
+
return if not tgt
|
12
|
+
|
13
|
+
puts "launched CMD #{cmd} as #{tgt.pid}"
|
14
|
+
10.times do |i|
|
15
|
+
sleep 1
|
16
|
+
begin
|
17
|
+
tgt.step
|
18
|
+
regs = tgt.regs.read
|
19
|
+
ebx_name = (regs.include? 'rbx') ? 'rbx' : 'ebx'
|
20
|
+
|
21
|
+
v = regs[ebx_name]
|
22
|
+
puts "EBX ORIG : %016X" % v
|
23
|
+
|
24
|
+
tgt.regs[ebx_name] = v + 0x1000
|
25
|
+
tgt.regs.write
|
26
|
+
|
27
|
+
regs = tgt.regs.read
|
28
|
+
v1 = regs[ebx_name]
|
29
|
+
puts "EBX AFTER WRITE : %016X" % v1
|
30
|
+
|
31
|
+
tgt.regs[ebx_name] = v
|
32
|
+
tgt.regs.write
|
33
|
+
|
34
|
+
regs = tgt.regs.read
|
35
|
+
v2 = regs[ebx_name]
|
36
|
+
puts "EBX AFTER REVERT : %016X" % v2
|
37
|
+
|
38
|
+
# TODO: FPREGS
|
39
|
+
|
40
|
+
rescue Exception => e
|
41
|
+
puts e.message
|
42
|
+
puts e.backtrace.join("\n")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
puts "DEBUGGER RESUME"
|
46
|
+
tgt.cont
|
47
|
+
end
|
data/examples/syscall.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'Ptrace'
|
4
|
+
|
5
|
+
if __FILE__ == $0
|
6
|
+
|
7
|
+
cmd = ARGV.join(' ')
|
8
|
+
|
9
|
+
tgt = Ptrace::Target.launch cmd
|
10
|
+
puts "launched CMD #{tgt.pid}"
|
11
|
+
cont = true
|
12
|
+
while cont
|
13
|
+
|
14
|
+
begin
|
15
|
+
# test PT_SYSCALL directly
|
16
|
+
# call
|
17
|
+
tgt.syscall
|
18
|
+
puts "IN: #{tgt.regs.read.inspect}"
|
19
|
+
|
20
|
+
# ret
|
21
|
+
tgt.syscall
|
22
|
+
puts "OUT: #{tgt.regs.read.inspect}"
|
23
|
+
|
24
|
+
# test syscall wrapper
|
25
|
+
state = tgt.syscall_state
|
26
|
+
puts state.inspect
|
27
|
+
rescue Ptrace::InvalidProcessError
|
28
|
+
cont = false
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|