Plugins

 

Tigress has a simple way of letting you add code (we call them plugins) that we can then incorporate into the obfuscated program. For now, there are just two applications of plugins: you can provide your own responders and invariants which are proprietary to you and unique to your application. Responders are code snippets that will be invoked when there has been some sort of integrity violation, such as a checksum failure, and invariants are true facts about your code that can be used to create opaque predicates. These are the available options:

OptionArgumentsDescription
--Transform InitPlugins Initialize the plugin system.
--InitPluginsMBAPrefix String The prefix for the name of MBA plugin functions. Default=NONE.
--InitPluginsInvariantPrefix String The prefix for the name of invariant plugin functions. Default=NONE.
--InitPluginsResponderPrefix String The prefix for the name of responder plugin functions. Default=NONE.

Plugin functions can take parameters. Two special parameters argc and argv (of type int and char**, respectively) will be initialized with the arguments passed to main(). Any remaining formal parameters (which need to be of arithmetic types) will be initialized with random local variables found where the plugin function is inserted. They can be read but should, obviously, not be modified, unless you're writing a responder that you want to crash the program.

The plugin functions are only used during obfuscation and are removed from the resulting program.

 

MBA Expressions

For the --Transform=EncodeArithmetic transformation, you can extend the built-in MBA expressions with your own.

The plugin functions should be named prefix_operator or prefix_operator_number (if there is need for disambiguation). The operator should be one of PlusA, MinusA, Mult;Div, Mod, Shiftlt, Shiftrt, Lt, Gt, Le, Ge, Eq, Ne, BAnd, BXor, BOr, LAnd, LOr (for binary operators) and one of Neg, BNot, LNot (for unary operators). For binary operators the functions should have 2 arguments which need to be of the same arithmetic types as the return type. The body of the functions should be a single return statement that is the MBA expression. Here are a few examples.

int mba_PlusA_1 (int x, int y) {
  return (x|y)+(x&y);
}

int mba_PlusA_2 (int x, int y) {
  return x− ~y −1;
}

long mba_PlusA_3 (long x, long y) {
  return x− ~y −1;
}

int mba_BXor (int x, int y) {
   return (x|y)–(x&y);
}

 

Invariants

The purpose of invariant plugins is to allow the application developer to provide properties about the program that Tigress itself would not be able to figure out. These properties can then be used in Tigress transformations, often causing the program to crash or misbehave if an adversary modifies the program such that the invariants no longer hold.

Invariant plugin functions should always return an int. The name of an invariant plugin function should start with the prefix (set by the --InitPluginsInvariantPrefix=prefix option) and be followed by an encoding of the value(s) the function will return. These are the possible specifications:

  • ANY : the function returns an arbitrary value.
  • VALUE_value : the function always returns the integer value.
  • MIN_value : the function always returns an integer greater than or equal to value.
  • MAX_value : the function always returns an integer less than or equal to value.
  • RANGE_from_to : the function always returns an integer in the range [from, to], inclusive.

Extra underscore characters can be freely added to the name (for disabiguation) and will be stripped out.

For example, if you have set --InitPluginsInvariantPrefix=acme you could add the following function to your application:

int highScore = 0;     /* Always less than 100! */
...
int acme_RANGE_0_99 (void) {
   return highScore;
}

The idea here is that transformations which use opaque predicates will introduce code that assumes that 0 ≤ highScore < 100. If an attacker modifies the code so that highScore is outside of this range, the program will start to misbehave.

Here is how you would call Tigress with the AddOpaque transform:

tigress \
           --Transform=InitPlugins  --InitPluginsInvariantPrefix=acme    \
           --Transform=InitOpaque --Functions=main --InitOpaqueStructs=plugin    \
           --Transform=AddOpaque \
              --Functions=* \
              --AddOpaqueKinds=true \
              --AddOpaqueStructs=plugin	\

Here are some examples of invariant plugin functions:

/* Returns a random value */
int acme_ANY (void) {
   return list_length(myList);
}

/* Returns 42. */
int acme_VALUE_42 (void) {
   return aes_key[4];
}

/* Returns 5. */
int acme_VALUE_5 (void) {
   return 5;
}

/* Returns 5, assuming the program is supposed to always be called with 5 arguments. */
/* Note the use of the extra _ to disambiguate from the previous function. */
int acme_VALUE_5_ (int argc) {
   return argc;
}

/* Returns a value in [10..]. */
int acme_MIN_10 () {
   return 10 + random();
}

/* Returns a value in [..10]. */
int acme_MAX_10 () {
   return (random() % 11);
}

/* Returns a value in [10..20]. */
int acme_RANGE_10_20 () {
   return 10 + (random() % 11);
}

/* Returns the length of the argument list, for a program
   that is expected to be called with 1,2, or 3 arguments . */
int acme_RANGE_1_3 (int argc) {
   return argc;
}

 

Responders

Tigress has a few simple built-in responders that can be used in the Checksum transformation. With plugins, you can easily add your own responders to your application. Responder plugin functions should always return void.

Here is how you would invoke Tigress to use your own plugin responders with the Checksum transform:

tigress \
         --Transform=InitPlugins  --InitPluginsResponderPrefix=acme_responder    \
         --Transform=Checksum ... --ChecksumResponseKinds=plugin ... \
         ...

Here are some simple examples of responder plugins:

void acme_responder_1 () {
   abort();
}
void acme_responder_2 (int argc, char** argv) {
   argv[argc-1] = '\0';
}
void acme_responder_3 () {
   for(int i=0; i<10; i++) {
      printf("Hacked %i!\n", i);
   }
   exit(-1);
}
void acme_responder_4 () {
   phone_home("We've been hacked!");
}

void acme_responder_5 () {
   memset(aes_key, 0, 16);
}

 

Write Your Own Opaque Library!

Using plugins you can write your own opaque predicate library. Two more plugin functions are available to facilitate this, initialize and update:

OptionArgumentsDescription
--Transform InitPlugins Initialize the plugin system.
--InitPluginsInvariantPrefix String The prefix for the name of invariant plugin functions. Default=NONE.
--InitPluginsInitializePrefix String The prefix for the name of initializer plugin functions. Default=NONE.
--InitPluginsUpdatePrefix String The prefix for the name of update plugin functions. Default=NONE.

Here's a simple example of a library that you would add to the beginning of your application:

int alwaysEven;

void acme_initialize_even (int argc) {
   alwaysEven = (argc+1) * 2;
}

void acme_update_even_1 () {
   alwaysEven += 2;
}

void acme_update_even_2 (int f) {
   alwaysEven += f + (f % 2);
}

int acme_invariant_even_VALUE_0 (void) {
   return alwaysEven%2;
}

This library adds a global variable alwaysEven which, as the name implies, is alwyas even. The body of the initialize_even function will be inserted in the function where you want initializations to take place, and the two update functions will be inserted by the UpdateOpaque transformation. Here, there is no particular significance to the _number in the function names -- each function just has to have a unique name. Here is the call to Tigress:

tigress  ... \
     --Transform=InitPlugins  \
        --InitPluginsInvariantPrefix=acme_invariant    \
        --InitPluginsInitializePrefix=acme_initialize    \
        --InitPluginsUpdatePrefix=acme_update    \
     --Transform=InitOpaque   \
         --Functions=main \
          --InitOpaqueStructs=plugin    \
     --Transform=AddOpaque \
        --Functions=* \
        --AddOpaqueKinds=true \
        --AddOpaqueStructs=plugin \
     --Transform=UpdateOpaque \
        --Functions=* \
        --UpdateOpaqueCount=10 

Below is another example which uses linked list to make an opaque predicate library. There are two invariants that hold over this list: the list is always of even length and the elements of the list are always odd:

typedef struct intList {
   struct intListNode* first;
} *INTLIST;

typedef struct intListNode {
   int value;
   struct intListNode* next;
} *INTLISTNODE;

typedef INTLIST intList_t;
typedef INTLISTNODE intListNode_t;

intList_t list;

void acme_initialize_list (int argc) {
   list = (intList_t) malloc(sizeof(struct intList));
   list->first = NULL;
   for(int i=0; i < (argc * 2); i++) {
      intListNode_t r = (intListNode_t) malloc(sizeof(struct intListNode));
      r->value = i*2+1;
      r->next = list->first;
      list->first = r;
   }
}

void acme_update_list (int v1, int v2) {
   intListNode_t r = (intListNode_t) malloc(sizeof(struct intListNode));
   r->value = v1*2+1;
   r->next = list->first;
   list->first = r;
   r = (intListNode_t) malloc(sizeof(struct intListNode));
   r->value = v2*4+1;
   r->next = list->first;
   list->first = r;
}

int acme_invariant_length_VALUE_0 () {
   int length = 0;
   intListNode_t i = list->first;
   while (i != NULL) {
     length++;
     i = i->next;
   };
   return length % 2;
}

int acme_invariant_odd_VALUE_0 (int v) {
   int value = 3;
   intListNode_t i = list->first;
   int times = v;
   while ((i != NULL) && (times > 0)) {
     value = i->value;
     times--;
     i = i->next;
   };
   return value % 2 - 1;
}

 

Abstract Datatype Plugins

If your program is using ADTs such as HashMap or Set you can provide these to Tigress. The only use at the moment is with the RandomFuns transformation.

OptionArgumentsDescription
--InitPluginsCollectionPrefix String, String, ... Comma-separated list of names of abstract datatypes that have been included in the program. If you specify Set as the prefix, for example, you are expected to provide types Set_TYPE (the type of the ADT itself), Set_ELEMENT (the type of the elements of the set) and Set_ITER (the type of an iterator over the set). You should also provide implementations for the following functions: Set_CREATE (create a set), Set_JOIN (merge two sets), Set_INSERT (insert an element in the set), Set_DELETE (delete an element from the set), Set_MEMBER (check if an element is in the set), Set_DESTROY (delete the set), Set_SIZE (return the number of elements in the set), Set_FIRST (return the reference to the first element of the set), Set_NEXT (return the reference to the next element of the set), Set_DONE (indicate if we are done with the interation), Set_GET (get the current element of the iteration). Default=NONE.
--InitPluginsDictionaryPrefix String, String, ... Comma-separated list of names of dictionary abstract datatypes that have been included in the program. Otherwise similar to --InitPluginsCollectionPrefix. Default=NONE.

Below is a simple example of two ADT libraries that you would add to the beginning of your application and the corresponding Tigress command line options (you can give multiple ADTs in each option):

   --Transform=InitPlugins \
       --InitPluginsCollectionPrefix=Set \
       --InitPluginsDictionaryPrefix=HashMap


// Types for Sets.
typedef ... Set_TYPE;
typedef ... Set_ELEMENT;
typedef ... Set_ITER;

// Create and destroy a set
Set_TYPE Set_CREATE(){ ... }
void Set_DESTROY(Set_TYPE *a) { ... }

// Merge sets in different ways (union, intersection, etc). 
Set_TYPE Set_JOIN_union(Set_TYPE a, Set_TYPE b) { ... } 
Set_TYPE Set_JOIN_diff(Set_TYPE a, Set_TYPE b){ ... }

// Insert and delete elements from the set.
void Set_INSERT(Set_TYPE a, Set_ELEMENT b) { ... }
void Set_DELETE(Set_TYPE a, Set_ELEMENT b) { ... }

// Query the set
int Set_MEMBER(Set_TYPE a, Set_ELEMENT b){ ... }
int Set_SIZE(Set_TYPE a){ ... }

// Iterate through the elements of the set. Here's the pattern to use:
// Set_ITER i = Set_FIRST(s);
// while not Set_DONE(s, i) {
//   Set_ELEMENT e = Set_GET(s,i);
//   Do something with e;
//   i = Set_NEXT(s,i);
// }
Set_ITER Set_FIRST(Set_TYPE a){ ... }
Set_ITER Set_NEXT(Set_TYPE a, Set_ITER n){ ... }
int Set_DONE(Set_TYPE a, Set_ITER n){ ... }
Set_ELEMENT Set_GET(Set_TYPE a, Set_ITER n){ ... }

// Similar for HashMaps (instances of a Dictionary ADT).
typedef ... HashMap_TYPE;
typedef ... HashMap_KEY;
typedef ... HashMap_ELEMENT;
typedef ... HashMap_ITER;

HashMap_TYPE HashMap_CREATE(){ ... }
void HashMap_INSERT(HashMap_TYPE a, HashMap_KEY b, HashMap_ELEMENT c) { ... }
void HashMap_DELETE(HashMap_TYPE a, HashMap_KEY b) { ... }
int HashMap_MEMBER(HashMap_TYPE a, HashMap_KEY b){ ... }
HashMap_ELEMENT HashMap_LOOKUP(HashMap_TYPE a, HashMap_KEY b){ ... }
void HashMap_DESTROY(HashMap_TYPE *a) { ... }
int HashMap_SIZE(Set_TYPE a){ ... }
HashMap_KEY HashMap_NTH(Set_TYPE a, int n){ ... }
HashMap_ITER HashMap_FIRST(HashMap_TYPE a){ ... }
HashMap_ITER HashMap_NEXT(HashMap_TYPE a, HashMap_ITER n){ ... }
int HashMap_DONE(HashMap_TYPE a, HashMap_ITER n){ ... }
HashMap_ELEMENT HashMap_GET(HashMap_TYPE a, HashMap_ITER n){ ... }