Matasano
June 22nd, 2009, 13:17
Last summer I was given the task of porting a Win32 Ruby scriptable debugger to OSX… a task I accomplished with some consternation.
Over the last year of working here at Matasano, I’ve had some time to refine the code enough for release. I’ve cleaned up a bunch of the code resulting from my inexperience with Ruby, implemented a couple features and fixed several bugs. Meanwhile, Chris Rohlf began a Linux port and its beginnings are included in the gem. And yes, Ragweed is now available as a gem through github. ( for the impatient with github as a gem source)
Why a scriptable debugger?
When reversing, the usual debugging tools for developers aren’t as useful. They’re built for stepping interactively through programs you have source code for. They don’t generally have methods to get data out.
Reversing also requires being able to do mean and nasty things to the running process. When tracing calls, you want to watch how they interact. The last thing you want to do is anything manual. Automation is a requirement.
Also helpful is the ability to automate information gathering tasks, or the ability to dynamically add, remove or change breakpoints. These features are why scriptable debuggers have been created: To play with black boxes in a more dynamic and seedier manner.
What’s available already?
There are already scriptable debuggers out there. The most notable are PaiMei/PyDbg, Immunity Debugger and IDA.
PaiMei is written in Python, bills itself as “a reverse engineer’s swiss army knife” and uses the Python ctypes library for low level win32 calls.
Immunity Debugger is a GUI debuggger for win32 that uses Python for its scripting functionality.
IDA Pro is largely a win32 disassembler, but it is scriptable, again in Python, and includes a debugging module.
Before I get run off by a screaming mob with pitchforks, flightless birds, members of the family bovidae, etc., I will also mention GDB which has a library in development (libgdb) and can be scripted through macros.
With the exception of GDB which runs on most platforms and has its own macro language, these all share two common problems: Win32 and Python. Matasano is a Ruby shop. We like Ruby. It is good to us. We also wanted a tool for non-Win32 applications. But mostly, we just wanted something in Ruby.
Enter Ragweed
I’m going to stick to the OSX side of Ragweed for this article since I’m most familiar with it and there is still work to be done to unify the (currently) three debugging APIs —- Win32, Linux, and OSX —- inside Ragweed.
Under the hood, Ragweed (on OSX) uses Ruby/DL to perform the various low level system calls necessary to create a debugger. (More about that in my post from last year ("http://www.matasano.com/log/1100/what-ive-been-doing-on-my-summer-vacation-or-it-has-to-work-otherwise-gdb-wouldnt/")). These calls are abstracted somewhat to provide a smoother, more Ruby-like interface.
There are two caveats for Ragweed in OSX:
A quick example (this we can do in IRB):
# debugging ftp using default signal handlers, printing registers every stop and logging calls to _lpwd
require ‘ragweed’
class DebugFtp < Debuggerosx
# print the registers every time the process stops
def on_stop(signal)
puts "Stopped with signal #{signal}"
self.threads.each {|t| self.get_registers(t).dump}
end
end
# no process lookup by name yet
d = DebugFtp.new(pid) # where pid is the id of ftp for this example
# set breakpoint for lpwd
d.breakpoint_set(0x420f,‘lpwd’, (bpl = lambda do | t, r, s | puts "#{ s.breakpoints[r.eip].first.function } hit in thread #{ t }\n"; end))
d.install_breakpoints
d.continue
d.loop #loop until child exits
# now go do stuff in in your other terminal window running ftp
That’s it. We just override the signal handlers for the signals we want to know about (or not), attach to a running process, set and install breakpoints, and it’s off to the traces. A simple hit tracer is only a CSV file and read loop away from this.
Want info on a region of memory?
d.region_info(0x0,:basic).dump
What about?
d.threadinfo(threadid).dump
Break stuff by playing with registers?
There you have it. It’s not pretty but it’s only begun.
http://www.matasano.com/log/1799/ruby-for-pentesters-the-dark-side-i-ragweed/
Over the last year of working here at Matasano, I’ve had some time to refine the code enough for release. I’ve cleaned up a bunch of the code resulting from my inexperience with Ruby, implemented a couple features and fixed several bugs. Meanwhile, Chris Rohlf began a Linux port and its beginnings are included in the gem. And yes, Ragweed is now available as a gem through github. (
Code:
sudo gem install tduehr-ragweed
Why a scriptable debugger?
When reversing, the usual debugging tools for developers aren’t as useful. They’re built for stepping interactively through programs you have source code for. They don’t generally have methods to get data out.
Reversing also requires being able to do mean and nasty things to the running process. When tracing calls, you want to watch how they interact. The last thing you want to do is anything manual. Automation is a requirement.
Also helpful is the ability to automate information gathering tasks, or the ability to dynamically add, remove or change breakpoints. These features are why scriptable debuggers have been created: To play with black boxes in a more dynamic and seedier manner.
What’s available already?
There are already scriptable debuggers out there. The most notable are PaiMei/PyDbg, Immunity Debugger and IDA.
PaiMei is written in Python, bills itself as “a reverse engineer’s swiss army knife” and uses the Python ctypes library for low level win32 calls.
Immunity Debugger is a GUI debuggger for win32 that uses Python for its scripting functionality.
IDA Pro is largely a win32 disassembler, but it is scriptable, again in Python, and includes a debugging module.
Before I get run off by a screaming mob with pitchforks, flightless birds, members of the family bovidae, etc., I will also mention GDB which has a library in development (libgdb) and can be scripted through macros.
With the exception of GDB which runs on most platforms and has its own macro language, these all share two common problems: Win32 and Python. Matasano is a Ruby shop. We like Ruby. It is good to us. We also wanted a tool for non-Win32 applications. But mostly, we just wanted something in Ruby.
Enter Ragweed
I’m going to stick to the OSX side of Ragweed for this article since I’m most familiar with it and there is still work to be done to unify the (currently) three debugging APIs —- Win32, Linux, and OSX —- inside Ragweed.
Under the hood, Ragweed (on OSX) uses Ruby/DL to perform the various low level system calls necessary to create a debugger. (More about that in my post from last year ("http://www.matasano.com/log/1100/what-ive-been-doing-on-my-summer-vacation-or-it-has-to-work-otherwise-gdb-wouldnt/")). These calls are abstracted somewhat to provide a smoother, more Ruby-like interface.
There are two caveats for Ragweed in OSX:
Due to the changes in Ruby 1.9 to DL, it is currently incompatible with 1.9.
Also, under OSX, Ragweed wants to run as root due to restrictions on.Code:task_for_pid
A quick example (this we can do in IRB):
# debugging ftp using default signal handlers, printing registers every stop and logging calls to _lpwd
require ‘ragweed’
class DebugFtp < Debuggerosx
# print the registers every time the process stops
def on_stop(signal)
puts "Stopped with signal #{signal}"
self.threads.each {|t| self.get_registers(t).dump}
end
end
# no process lookup by name yet
d = DebugFtp.new(pid) # where pid is the id of ftp for this example
# set breakpoint for lpwd
d.breakpoint_set(0x420f,‘lpwd’, (bpl = lambda do | t, r, s | puts "#{ s.breakpoints[r.eip].first.function } hit in thread #{ t }\n"; end))
d.install_breakpoints
d.continue
d.loop #loop until child exits
# now go do stuff in in your other terminal window running ftp
That’s it. We just override the signal handlers for the signals we want to know about (or not), attach to a running process, set and install breakpoints, and it’s off to the traces. A simple hit tracer is only a CSV file and read loop away from this.
Want info on a region of memory?
d.region_info(0x0,:basic).dump
What about
Code:
thread_info
d.threadinfo(threadid).dump
Break stuff by playing with registers?
regs = d.get_registers(thread_id) regs.eip = 0x420f d.set_registers(thread_id, regs)Grope through the child’s memory?
Code:
Ragweed::Wraposx::vm_read(d.task, address, size) #returns a string of child's memory
There you have it. It’s not pretty but it’s only begun.
http://www.matasano.com/log/1799/ruby-for-pentesters-the-dark-side-i-ragweed/