Skip to content

Latest commit

 

History

History
327 lines (259 loc) · 11.4 KB

READMEv2.adoc

File metadata and controls

327 lines (259 loc) · 11.4 KB

Retrace v2

Work in a new version of retrace is ongoing, this new version features a new method of interposing functions and an advanced configuration mechanism that allows to intercept all calls using custom code. We still consider retrace v2 in beta, but it already works almost to the level of the first version of retrace.

Build

retrace v2 build is not enabled by default, to enable it you should run

sh autogen.sh
./configure --enable-v2
make

This will leave retrace build as a shared library in src/v2/.libs/libretrace_v2.so. Please refer to the main README to see how to run your program with this shared library.

Retrace wrapper

Optionally there’s a wrapper that makes running retrace easier. You can enable it using ./configure --enable-v2 --enable-v2_wrapper. It depends in libnereon which you can find in https://github.com/riboseinc/libnereon. Please follow its build and installation procedure before enabling the retrace wrapper. The wrapper parameters are:

Usage: retrace2 [options]
  -c  <config file>            : set configuration file
  -p  <process command line>   : set process command line to be traced
  -l  <retrace v2 library path>: set retrace v2 library path
  -d  <log level>              : set log verbosity level(0 : SILENT, 1 : NORMAL, 2 : VERBOSE, 3 : DEBUG)
  -l  <log file>               : specify log file
  -v                           : print version
  -h                           : print help message

Example:

./retrace2 -l .libs/libretrace_v2.so -p /bin/ls

Configuration

retrace v2 is configuration file is specified using the RETRACE_JSON_CONFIG environment variable. This is a JSON configuration file that will specify different "intercept scripts".

The default (hardcoded) configuration is the following:

{
        "intercept_scripts": [
                {
                        "func_name": "*",
                        "actions": [
                                {
                                        "action_name": "log_params"
                                },
                                {
                                        "action_name": "call_real"
                                }
                        ]
                }
        ]
}

This defines an intercept_scripts json object that contains a func_name and an array of actions. func_name refers to the function name this particular intercept will apply too. and actions defines a list of actions that will be executed when a call to that function is intercepted.

Actions are programmable (via plugins, more on that later) but we ship with five basic actions:

log_params would simply print the params of the call into the retrace log.

call_real would call into the actual function that we intercepted. If you don’t specify this action the call would be skipped.

modify_in_param_str

This action lets you modify string parameters in functions call. Take for example the following program:

#include <stdlib.h>

int main(void)
{
        getenv("TEST");
        getenv("OTHERTEST");

        return 0;
}

If we want to intercept the call to getenv() and change "TEST" and "OTHERTEST" for "PATH", the config file would look something like this:

{ "intercept_scripts": [ { "func_name": "getenv", "actions": [ { "action_name": "log_params" }, { "action_name": "modify_in_param_str", "action_params": { "param_name" : "name", "new_str" : "PATH" } }, { "action_name": "call_real" } ] } ] }

This will change all calls to getenv() to pass its name parameter as "PATH". If we only wanted to change the first one ("TEST") we can specify a matching parameter so it will only be changed when we have a positive match:

{
        "intercept_scripts": [
                {
                        "func_name": "getenv",
                        "actions": [
                         {
                                       "action_name": "log_params"
                         },
                         {
                                        "action_name": "modify_in_param_str",
                                        "action_params": {
                                                "param_name" : "name",
                                                "match_str" : "TEST",
                                                "new_str" : "PATH"
                                        }
                         },
                         {
                                "action_name": "call_real"
                         }
                        ]
                }
        ]
}

We added the match_str directive inside the action_params of modify_in_param_str so that the argument would be compared against the one provided ("TEST") and only perform the substitution if it matches. A link to all the functions we support and their parameter names can be found in the source code

modify_in_param_int

This action let’s you modify integer parameters in functions call. Take for example the following program:

#include <sys/types.h>
#include <unistd.h>

int main(void)
{
        setuid(111);
        setuid(112);
        return 0;
}

If we want to intercept the call to setuid() and change the 111 and 112 for a 42, the config file would look something like this:

{
        "intercept_scripts": [
                {
                        "func_name": "setuid",
                        "actions": [
                         {
                                       "action_name": "log_params"
                         },
                         {
                                        "action_name": "modify_in_param_int",
                                        "action_params": {
                                                "param_name" : "uid",
                                                "new_int" : 5
                                        }
                         },
                         {
                                "action_name": "call_real"
                         }
                        ]
                }
        ]
}

This will change all calls to setuid() to pass its uid parameter as 5. If we only wanted to change the first one (111) we can specify a matching parameter so it will only be changed when we have a positive match:

{
        "intercept_scripts": [
                {
                        "func_name": "setuid",
                        "actions": [
                         {
                                       "action_name": "log_params"
                         },
                         {
                                        "action_name": "modify_in_param_int",
                                        "action_params": {
                                                "param_name" : "uid",
                                                "match_int" : 111,
                                                "new_int" : 5
                                        }
                         },
                         {
                                "action_name": "call_real"
                         }
                        ]
                }
        ]
}

We added the match_int directive inside the action_params of modify_int_param_int so that the argument would be compared against the one provided (111) and only perform the substitution if it matches. A link to all the functions we support and their parameter names can be found in the source code

modify_in_param_arr

To be added.

modify_return_value_int

This action let’s you modify integer return values. Take for example the following program:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>

int main(void)
{
        uid_t a =  getuid();

        printf("%d\n", a);
}

If we want to intercept the call to getuid() and change the return value to 42, the config file would look something like this:

{
        "intercept_scripts": [
                {
                        "func_name": "getuid",
                        "actions": [
                         {
                                       "action_name": "call_real"
                         },
                         {
                                        "action_name": "modify_return_value_int",
                                        "action_params": {
                                                "retval_int" : 42
                                        }
                         }
                        ]
                }
        ]
}

This will make the program always print 42, regardless of the actual return value of guid().

memory_fuzz

This action let’s you randomly fail calls to malloc(), realloc() or even any other function that returns a pointer type. Take for example the following program:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
        int fail = 0;
        int pass = 0;

        for (int i = 0; i < 10; i++) {
                void *p = malloc(10);
                if (p)
                        pass++;
                else
                        fail++;
        }

        printf("fail %d, pass %d\n", fail, pass);
}

If we want to fail 10% of the calls to malloc we would use a config file like the following:

{
        "intercept_scripts": [
                {
                        "func_name": "malloc",
                        "actions": [
                         {
                                       "action_name": "call_real"
                         },
                         {
                                        "action_name": "memory_fuzz",
                                        "action_params": {
                                                "fail_rate" : 0.1
                                        }
                         }
                        ]
                }
        ]
}

This will randomly fail 10% of the calls to malloc returning NULL, this is useful to test programs that don’t check the return value of malloc or similar functions.

Custom actions

The above setup only allows very basic substitutions of basic type parameters. The power of retrace lies on its abaility to extended it to intercept and modify any function and parameter type. For an example of this, the basic actions source code is in here. We plan to add many rich actions in the future but you can also add yours for whatever you like.

Logging configuration variables

The retrace logger can be controlled using the following environment variables:

RETRACE_LOGGER_DEF_ENA Setting this to 0 will completely disable the retrace logging engine. RETRACE_LOGGER_DEF_STDOUT_ENA Setting this to 0 will prevent the retrace logging going to the standard output, this will prevent the program output and retrace output to get mixed. RETRACE_LOGGER_DEF_FN Set this to a file path where retrace will write its log. Useful when you disabled RETRACE_LOGGER_DEF_STDOUT_ENA.