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.
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_PyBool, etc.) have been defined.
The OCaml abstract type
vol_obj represents any Volatility object instance (i.e. instance of
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
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
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")
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
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).