Wiki

Clone wiki

libbap / BAP

OCaml Interface to Volatility

When writing OCaml code analysis modules, it is sometimes useful to be able to query a Volatility session to extract OS related information such as: _EPROCESS, _ETHREAD, etc. data structures; process address space memory pages; thread stacks, process heaps, etc.. The OCaml Volatility module provides this type of capability by allowing OCaml data structures to be mapped to equivalent Python data structures (and vice versa). As a result, it becomes possible to call methods belonging to Python Volatility objects.

Python Objects

In order to aid mapping between OCaml datatypes and Python objects, a py_obj OCaml datatype has been defined. This type allows OCaml to directly create Python objects (these OCaml heap objects get mapped to Python heap objects by the libbap C code).

To deal with Python objects being returned, as series of OCaml helper functions (i.e. as_PyInt, as_PyBool, etc.) have been defined.

Example usages may be seen in the libbap OCaml files bap_analysis.ml, bap_ast.ml, etc. - where we map BAP/OCaml return types into Python objects.

Volatility Objects

The OCaml abstract type vol_obj represents any Volatility object instance (i.e. instance of volatility.obj.BaseObject).

VolatilityObject instances are used to create an interface between Volatility base object instances and OCaml code. This interface is designed so that Volatility plugins (or similar) may be written in OCaml.

To do this in OCaml, a user implements a function with the type py_obj -> py_obj. Callbacks into Volatility code are possible within this function using the py_eval type to, for example, access members of the OCaml vol_obj type.

Finally, so that the OCaml code may be called from Python/Volatility, it is necessary to register the defined function as follows:

let _ = Callback.register "example_plugin" plugin_code

where plugin_code: py_obj -> py_obj is the OCaml plugin code.

This code can then be called from (for example) the Volatility shell as follows:

Volshell> from bap import *
Volshell> obj = VolatilityObject(x)
Volshell> obj("example_plugin")

where x is the Volatility object (of OCaml type vol_obj) to be passed as an argument to the plugin_code OCaml function.

Note: currently, it is necessary to add in your code explicitly to the build procedure (this code should be placed in the ocaml/plugins directory). Separate compilation of your OCaml code is not currently supported.

Example: Virtual Page Permissions

Here we examine how to create OCaml code that, when supplied with a Volatility _EPROCESS instance, iterates through each page of the processes address space printing out if the page has write or execute permissions.

To do this, we first need to define some OCaml code to model process address spaces (we place this code in the file ocaml/plugins/example.ml):

class _AddressSpace addrspace = 
object(self)
  method read addr length = (as_Option as_PyString)(_VolatilityObject_callback addrspace [
      PyMethod( "read", PyTuple [| PyInt addr; PyInt length |], empty_dict )
    ])

  method zread addr length = as_PyString(_VolatilityObject_callback addrspace [
      PyMethod( "zread", PyTuple [| PyInt addr; PyInt length |], empty_dict )
    ])

  method is_valid_address addr = as_PyBool(_VolatilityObject_callback addrspace [
      PyMethod( "is_valid_address", PyTuple [| PyInt addr |], empty_dict )
    ])

  method get_available_pages = 
    let pages = as_PyObject(_VolatilityObject_callback addrspace [ PyMethod( "get_available_pages", empty_tuple, empty_dict ) ])
    in
      py_iter as_PyTuple pages

  method get_pdpi addr = as_PyInt(_VolatilityObject_callback addrspace [
      PyMethod( "get_pdpi", PyTuple [| PyInt addr |], empty_dict )
    ])

  method get_pgd addr pdpte = as_PyInt(_VolatilityObject_callback addrspace [
      PyMethod( "get_pgd", PyTuple [| PyInt addr; PyInt pdpte |], empty_dict )
    ])

  method get_pte addr pde = as_PyInt(_VolatilityObject_callback addrspace [
      PyMethod( "get_pte", PyTuple [| PyInt addr; PyInt pde |], empty_dict )
    ])

  method is_writeable addr =
    let pdpte = self#get_pdpi addr in
    let pde = self#get_pgd addr pdpte in
    let pte = self#get_pte addr pde in
      (UInt64.logand (UInt64.shift_right pte 1) UInt64.one) = UInt64.one

  method is_executable addr =
    let pdpte = self#get_pdpi addr in
    let pde = self#get_pgd addr pdpte in
    let pte = self#get_pte addr pde in
      (UInt64.shift_right pte 63) = UInt64.zero
end

and _EPROCESS objects:

class _EPROCESS eproc =
object(self)
  method pid = as_PyInt(_VolatilityObject_callback eproc [
      PyMethod( "__getattr__", PyTuple [| PyString "UniqueProcessId" |], empty_dict );
      PyMethod( "v", empty_tuple, empty_dict )
    ])

  method get_process_address_space = as_PyObject(_VolatilityObject_callback eproc [
      PyMethod( "get_process_address_space", empty_tuple, empty_dict )
    ])
end

Next, we can define our example plugin code as follows:

let plugin_example vol_eproc =
  let eproc = new _EPROCESS(vol_eproc) in
  let addrspace = new _AddressSpace(eproc#get_process_address_space) in
  let process_page page =
    let base_addr = as_PyInt(Array.get page 0) in
      if addrspace#is_writeable base_addr || addrspace#is_executable base_addr then print_string ((Printf.sprintf "0x%08x" (UInt64.to_int base_addr)) ^ ": ");
      if addrspace#is_writeable base_addr then print_string "W";
      if addrspace#is_executable base_addr then print_string "X";
      if addrspace#is_writeable base_addr || addrspace#is_executable base_addr then print_endline ""
  in
    BatLazyList.iter process_page addrspace#get_available_pages

Finally, we can register this plugin code for callbacks as follows:

let _ = Callback.register "example" plugin_example

Once this code has been compiled into our libbap library code (i.e. rebuild libbap), it can then be accessed and used as follows:

Volshell> from bap import *
Volshell> cc(pid=1752)
Volshell> obj = VolatilityObject(self.proc) # Volatility/OCaml interface object
Volshell> obj("example")                    # Python wrapper for the OCaml call: plugin_example(PyObject self.proc)

0x00010000: W
0x00020000: W
0x00030000: WX
0x00031000: WX
0x00032000: WX
0x00033000: WX
0x00034000: WX
0x00035000: WX
0x00036000: WX
0x00037000: WX
0x00038000: WX
0x00039000: WX
...

Note: Here we assume that volshell has loaded the Honeynet Challenge 3 memory image (see http://honeynet.org/challenges/2010_3_banking_troubles).

Updated