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:
Option | Arguments | Description |
---|---|---|
--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.
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 beone 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);
}
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:
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;
}
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);
}
Using plugins you can write your own opaque predicate library. Two more plugin functions are available to facilitate this, initialize and update:
Option | Arguments | Description |
---|---|---|
--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:
If your program is using ADTs such as 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):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
HashMap
or Set
you can provide
these to Tigress. The only use at the moment is with the RandomFuns transformation.
Option Arguments Description
--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.
--Transform=InitPlugins \
--InitPluginsCollectionPrefix=Set \
--InitPluginsDictionaryPrefix=HashMap
#include "tigress.h"
#include