Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
338 lines (256 sloc) 12.6 KB

Nerve is a simple cross platform (Win32, Linux, OSX) x86 scriptable debugger

What is it?

Nerve is based on, and requires, Ragweed
Ragweed is a cross platform x86 debugging library written in Ruby.

To learn more about Ragweed, read this:

Nerve can be a dynamic hit tracer, an in memory fuzzer or a simple scriptable debugger.
All you need to do is give it a configuration file telling it what breakpoints to
set, events to hook and what ruby scripts to execute when it happens.

Nerve showcases the best part about Ragweed: cross platform debugging. I originally
wrote Nerve as a small Ragweed script that kept stats on the functions my fuzzers
were triggering in a target process. This told me what code paths my fuzzer was
reaching and which ones it wasn't. It only took a few hours to make it work on all Ragweed
supported platforms, and since then it has grown into a much more capable tool. It now
supports configuration files for breakpoints, event handler scripts and more.

We have included several working examples with Nerve so that you aren't lost the first
time you try it. If you develop some useful scripts with it let us know and we can make
them part of the default package.

Supported Platforms

Nerve is supported and has been tested on the following platforms:

Windows XP, 7
Ubuntu Linux 9.10 -> 11.04
Mac OS X 10.5, 10.6

Ruby 1.8.7
Ruby 1.9.x


- Cross platform
- Easy configuration files you can write by hand or generate using our tools
- Run Ruby scripts with full access to the debugger when breakpoints are hit
- Run Ruby scripts when specific debugging events occur
- Extend Nerve through handlers.rb or output.rb with minimal code changes
- Nerve comes with a few example breakpoint scripts such as hooking RtlAllocateHeap/malloc

Todo / Ideas

Nerve is a simple tool, but the plan is to grow it with optional add ons and more...

- Lots of helper scripts for breakpoints such as heap inspection, in memory fuzzing, SSL reads etc...
- Helper methods and better named instance variables for making breakpoint scripts easier to write
- Better output such as graphviz, statistics, function arguments etc...
- Redis database support for offline analysis of output
- Cleaner 1.8 and 1.9 support for launching processes
- Continous re-attach to any targets that match the process name
- Change the configuration to Ruby code (the text file will be an optional fallback)
- Nerve is also helping us find the areas of Ragweed that need the most improvement


Nerve has one small dependency. But don't worry, theres no need to install an SQL server
or compile any code! The dependency, Ragweed, can be installed via Ruby gems on any platform.

Ragweed (a cross platform x86 debugger library)

git clone    (the preferred method)

... OR ...

gem install -r ragweed   (you might get an older version!)

Ragweed requires FFI which you can install with rubygems:

gem install -r ffi

YES, thats it!

If you want to run the bleeding edge stuff we commit to github everyday then I suggest
checking out the github repositories of both Nerve and Ragweed and executing a 'git pull'
before using the tool. But we can't promise it will work perfectly.


$ ruby nerve.rb  --help

Nerve 1.9 | Chris Rohlf 2009-2011

-p, --pid PID/Name               Attach to this pid OR process name (ex: -p 12345 | -p gcalctool | -p notepad.exe)
-x, --exec_proc FILE             Launch a process according to the configuration found in this file
-b, --config_file FILE           Read all breakpoints and handler event configurations from this file
-o, --output FILE                Dump all output to a file (default is STDOUT)
-f                               Optional flag indicates whether or not to trace forked child processes (Linux only)

Configuration File Example

Keywords in configuration files:
(the order does not matter but each line represents a unique breakpoint)

bp - An address (or a symbolic name for Win32) where the debugger should set a breakpoint
name - A name describing the breakpoint, typically a symbol or function name
lib - An optional library name indicating where the symbol can be found, only useful with Linux/OSX
bpc - Number of times to let this breakpoint hit before uninstalling it
code - Location of a script that holds ruby code to be executed when this breakpoint hits
nargs - The number of arguments the function takes (only used with Win32)
hook - A true or false configuration to hook both a function entry and exit (Win32 only)


Win32 Configuration Example:
bp=0x12345678, name=SomeFunction, bpc=2, code=scripts/SomeFunctionAnalysis.rb, hook=true
bp=kernel32!CreateFileW, name=CreateFileW, code=scripts/CreateFileW_Analysis.rb

Linux Configuration Example:
bp=0x12345678, name=function_name,, bpc=1, code=scripts/ncurses_trace.rb
name=malloc, lib=/lib/tls/i686/cmov/, bpc=10, bp=0x006ff40, code=scripts/malloc_linux.rb

OS X Configuration Example:
bp=0x12345678, name=function_name, bpc=6

Process Launching Configurations

You can instruct Nerve to launch a target process with arguments and environment
variables of your choosing. Nerve takes the -x flag along with a filename containing
your configuration. Be aware that Nerve currently uses exec() to launch processes on
nix. This means stdout will be written to by the new process. Supporting Process.spawn()
is easy but its also not Ruby 1.9 compatible. This is on my list to rework!

Process launching configuration keywords

target - The location of the application you want to run
args - A string of arguments to pass to the application
env - A string of environment variables for the application

target: /usr/bin/gcalctool
args: -s 1+1
env: BLAH=test
env: MYLIBPATH=/usr/lib

Breakpoint Scripts

Nerve supports breakpoint scripts that run when a breakpoint you have specified is executed. These
can be specified using the 'code=' keyword in your Nerve configuration file (see above).
These scripts run within the scope of Nerve and the Ragweed breakpoint. This means your scripts
have access to all the helper methods and instance variables Ragweed makes available. Documenting
each of these is going to take a bit of time but heres some stuff you can start with.

Helper Methods:

(please refer to Ragweed sources for now

Instance Variables:

@ragweed - The Ragweed instance, use this to call all Ragweed methods

Win32 Specific:
    evt - A debugger event
    ctx - A context structure holding registers
    dir - a string indicating function 'enter' or 'leave'

Event Handlers Configuration Example

Event handler scripts work just like breakpoint file scripts. They have full access to the debugger
but are triggered when specific debug events occur such as 'on_load_dll'. See handlers.rb for how
they are implemented. Some are OS specific and just wont trigger if you are on a different platform.

Keywords for configuration files:


This example will run the My_OnLoad_DLL.rb script whenever the LOAD_DLL debug event occurs:



Heres some example output from Nerve running on Ubuntu Linux:

chris@ubuntu:/# ruby nerve.rb -b example_configuration_files/generic_ubuntu_910_libc_trace.txt -p test
Nerve ...
Setting breakpoint: [ 0x0964f40, malloc /lib/tls/i686/cmov/ ]
Setting breakpoint: [ 0x08055590, mp_add ]
Setting breakpoint: [ 0x0971830, wmemcpy /lib/tls/i686/cmov/ ]
Setting breakpoint: [ 0x0969f20, memcpy /lib/tls/i686/cmov/ ]
Setting breakpoint: [ 0x0964e60, free /lib/tls/i686/cmov/ ]
Setting breakpoint: [ 0x09b2de0, read /lib/tls/i686/cmov/ ]
Setting breakpoint: [ 0x09b2e60, write /lib/tls/i686/cmov/ ]
^CDumping stats
0x0a3cf40 - malloc | 5279
0x08055590 - mp_add | 0
0x0a49830 - wmemcpy | 0
0x0a41f20 - memcpy | 0
0x0a3ce60 - free | 8385
0x0a8ade0 - read | 0
0x0a8ae60 - write | 0
... Done!

Here is Nerve running on Windows 7 and debugging an example program that calls HeapAlloc. For
this test program we want to run a simple ruby script each time we enter and leave RtlAllocateHeap.
This script should extract the arguments to the function upon entry and the return values on exit.

#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
   void *a;
   HANDLE h1 = HeapCreate(0, 1024, 1024);
   int i = atol(argv[1]);

       a = HeapAlloc(h1, HEAP_ZERO_MEMORY, i);
       HeapFree(h1, 0, a);

    return 0;

Here is the configuration file:

bp=ntdll!RtlAllocateHeap, name=RtlAllocateHeap, code=scripts/RtlAllocateHeap.rb, hook=true

And here is the scripts/RtlAllocateHeap.rb referenced in the configuration file:

## This script is for Win32 RtlAllocateHeap

  if dir.to_s =~ /enter/
    @log.str "RtlAllocateHeap -> Size requested #{@ragweed.process.read32(ctx.esp+12)}"
    @log.str "RtlAllocateHeap -> Heap handle is @ #{@ragweed.process.read32(ctx.esp+4).to_s(16)}"
    @log.str "RtlAllocateHeap <- Heap chunk returned @ #{ctx.eax.to_s(16)}"
rescue =>
  puts "Does your configuration use hook=true?"

Below is the output of hooking the malloc.exe program:

PS C:\Nerve> ruby .\nerve.rb -p test.exe -b .\example_configuration_files\Win32_notepad.txt
Nerve ...
Setting breakpoint: [ ntdll!RtlAllocateHeap, RtlAllocateHeap ]
RtlAllocateHeap -> Size requested 1024
RtlAllocateHeap -> Heap handle is @ 750000
RtlAllocateHeap -> Heap chunk returned @ 750590
RtlAllocateHeap -> Size requested 1024
RtlAllocateHeap -> Heap handle is @ 750000
RtlAllocateHeap -> Heap chunk returned @ 750590
RtlAllocateHeap -> Size requested 1024
RtlAllocateHeap -> Heap handle is @ 750000
RtlAllocateHeap -> Heap chunk returned @ 750590         <- ( This is where I CTRL+C the test program )
RtlAllocateHeap -> Size requested 24
RtlAllocateHeap -> Heap handle is @ 470000
RtlAllocateHeap -> Heap chunk returned @ 47f640

EIP: 77b564f4

EAX: 000000c0
EBX: 7ffd3000
ECX: 77b6350f
EDX: 00000000
EDI: 00000000
ESI: 002af704
EBP: 002af728
ESP: 002af6c0
EFL: 00000000000000000000001000000010 cvvavrxniiodItszxaxpXc
Dumping stats
Pid is 3224
Tid is 4048
ntdll!RtlAllocateHeap - RtlAllocateHeap | 4

Useful Tips

- If you need to declare some global variables you should do it in an on_attach
  script. This code will only run once when the debugger attaches to the target.

- On windows you can launch Nerve before launching your process. This avoids the
  the need for a process launching script. This is a side effect of how Ragweed
  is designed. This should be the default for all platforms in the future.


Ragweed and Nerve do not ship with a disassembly library. We feel that sort of lies outside of the
scope of a core debugger library. We do however recommend the following Ruby disassembly libraries: - FFI UDis86 library - A Ruby C extension for distorm64


Nerve was written by Chris Rohlf with contributions from AlexRad and Timur Duehr

Ragweed was written by Thomas Ptacek, ported to OSX by Timur Duehr and ported to Linux by Chris Rohlf

Thanks to the team and a few other individuals for providing feedback and ideas