RDBG
RDBG
RDBG is a ruby library that provides binary level debug access to processes.
it wraps the ptrace api to
control the target (and read registers), it uses the /proc/$pid/mem
and /proc$pid/mapping
interfaces to allow proper access to
the targets memory and it allows to set breakpoints. Using ptrace to inject breakpoints is
actually suprisingly complicated and requires a somewhat complex statemachine to handle events like singlestepping on a
breakpoint or capturing signals in combination with breakpoints.
This can be used to automatize various debuggin or reversing task without relaying on the GDB scripting facilites or (even worse) the GDB Machine Interface. For example, we used it to build a cheat engine like tool that allows to find and fixate values in games memory, as demonstrated with the free linux game PARSEC47.
require_relative '../rdbg.rb'
cmd = "/usr/games/parsec47"
# step first ten instructions, printing bytes at EIP and ESP
def get_writable_mappings(d)
d.mappings.select{|m| m[:permissions].include?("w") && !m[:permissions].include?("s")}
end
def run_and_wait(d)
# run the debugger until the user enters a value
d.continue
puts "Enter the current value of lives"
target = gets.strip.to_i
# pause the debugger
d.pause
return target
end
d = RDBG.new(cmd)
target = run_and_wait(d)
offsets =[]
puts "blubl"
get_writable_mappings(d).each do |m|
puts "reading mapping #{m}"
str = d.read_mem(m[:range].min, m[:range].max-m[:range].min+1)
puts str[0..1000].inspect
str.each_byte.with_index do |byte,offset|
offsets << m[:range].min+offset if byte == target
end
puts "found #{offsets.length} addresses"
end
loop do
# while no unique locations was identified, loop
next_target = run_and_wait(d)
next_offsets =[]
offsets.each do |o|
next_offsets << o if d.read_mem(o,1) == next_target.chr
end
offsets = next_offsets
if offsets.length == 1
puts "found offset #{offsets.first}"
break
end
puts "found #{offsets.length} addresses"
puts offsets if offsets.length < 10
end
# we found a pointer to the relevant value, overwrite it with 9999
d.write_mem(offsets.first,[9999].pack("L"))
d.continue
# continue the target application forever
loop do
sleep 1
end