Skip to content

Loader and binary rewriter API

Cristian Cadar edited this page Apr 22, 2020 · 4 revisions

How to write a plugin

All API definitions are located in a single header file: https://github.com/parras/varan/blob/split-loader/includes/vx_api_defs.h
Examples of existing plugins can be found in https://github.com/parras/varan/tree/split-loader/plugins

The basic steps to create a minimal working plugin are as follows:

  1. Clone the split-loader branch: git clone [email protected]:parras/varan.git
  2. Under plugins/ create a new subdir new_plugin and add it to the root CMakeLists.txt: add_subdirectory("plugins/new_plugin")
  3. Implement a syscall handler function of type vx_sc_handler_fn. This function is responsible for actually issuing the real syscall.
  4. Implement the vx_init function of type vx_init_fn. It should at least set the vx_sc_handler_fn function pointer argument to the function defined in 3, and adjust argv and argc to point to the client ELF path.
  5. Write a CMakeLists.txt to build a shared object, see for instance https://github.com/parras/varan/blob/split-loader/plugins/vstrace/CMakeLists.txt
  6. Build and run: ./loader/run plugins/new_plugin/libnew_plugin.so

Definitions

Loader: executable that loads and rewrites the client application to redirect system calls to the plugin
Plugin: library implementing syscall handlers (e.g. varan for MVE or vstrace for tracing)
Client: executable whose system calls have to be intercepted

Protocol of plugin registration and startup

  1. the loader is passed the path of the plugin (which is an ELF shared object)
  2. the loader uses dlopen() to open the ELF and dlsym() to find vx_init()
  3. the loader calls the vx_init(), providing the following arguments:
    a) pointers to argc and argv—both of them will be modified by the plugin
    b) pointer to interception callback registration function to be called by the plugin
    c) pointer to vDSO callback function to be populated by the plugin
    d) pointer to syscall handler function to be populated by the plugin
    e) pointer to post-load function to be populated by the plugin, that will be called after rewriting just before jumping into the client

Plugin side (in vx_init())

  1. the plugin parses arguments and leaves them in a state such that when vx_init returns, argv[0] is the path to the client ELF to be executed and subsequent arguments are the client's actual arguments
  2. the plugin populates syscall_handler with appropriate function
  3. the plugin calls fn_icept_reg for each of the functions that need to be intercepted (optional)
  4. the plugin populates vdso_callback with appropriate function (optional)
  5. the plugin populates post_load with appropriate function (optional)

Loader side

  1. the loader loads the ELF file
  2. the loader performs the interception of the functions registered:
    a) each function is located and its first few instructions are relocated to make room for unconditional jump - pointer to the first function is saved and an unconditional jump is injected at the end to connect these instructions to the rest of the function
    b) some static ASM code is injected to divert the flow of control through a pointer returned by the callback (that has to be called with the pointer of the start of the original function - the first relocated instruction)
    c) the unconditional jump mentioned in a) now jumps to code in b)
  3. the loader performs the interception of vDSO if vdso_callback was populated in a similar way to 10
  4. the loader performs the interception of all system calls in a similar way
  5. the loader transfers control to the entry point of the client ELF