Break up code blocks by inserting opaque predicates. Requires that at least the InitOpaque transform has been previously issued and, preferably, that one or more UpdateOpaque transformations have been given.

Diversity
There are two sources of diversity:- The types of opaque predicates used, which is controlled by the InitOpaque transformation.
- The location in the target function where the split takes places, which is randomized.
Usage
This is the code generated for the arguments options to --AddOpaqueKinds:
| Argument | Generated code |
|---|---|
| call |
if expr=false then
call to random existing function
|
| fake |
if expr=false then
call to non-existing function
|
| true |
if expr=true then
existing statement
|
| bug |
if expr=true then
existing statement
else
buggified version of the statement
|
| question |
if expr=true || false then
existing statement
else
obfuscated version of the statement
|
| junk |
if expr=false then
asm(".byte RandomBytes")
|
| question |
if expr=? then
original statement
else
obfuscated version of the statement
|
Question-mark Predicates
Consider the following script:
tigress --Seed=0 \
--Inputs="+1:int:42,-1:length:1?10" \
--Transform=InitImplicitFlow \
--Transform=InitEntropy \
--Transform=InitOpaque --Functions=main --InitOpaqueCount=2 --InitOpaqueStructs=list,array,input,env \
--Transform=AddOpaque --Functions=obf3 --AddOpaqueKinds=question --AddOpaqueSplitKinds=inside \
--AddOpaqueCount=10 arith.c --out=arith_out.c
The result will look something like this:
{
strcmp_result5 = (int )strlen(*(_4_main__argv + (_4_main__argc - 1)));
}
if (x >= strcmp_result5 + y) {
{
}
if (_2entropy * 3 >= _4_main__opaque_list1_1->data) {
{
atoi_result10 = atoi(*(_4_main__argv + 1));
}
if (y >= atoi_result10 + x) {
{
}
if (_4_main__opaque_array_0[(atoi_result10 & 2147483647) % 30] >= 22) {
{
}
if (_4_main__opaque_list2_2->data <= (_2entropy | 6) + (_2entropy & 6)) {
{
strcmp_result15 = (int )strlen(*(_4_main__argv + (_4_main__argc - 1)));
}
if (x >= strcmp_result15 + y) {
{
atoi_result17 = atoi(*(_4_main__argv + 1));
}
if (y >= atoi_result17 + y) {
{
atoi_result19 = atoi(*(_4_main__argv + 1));
}
if (y >= atoi_result19 + y) {
{
strcmp_result21 = (int )strlen(*(_4_main__argv + (_4_main__argc - 1)));
}
if (y >= strcmp_result21 + atoi_result10) {
{
strcmp_result23 = (int )strlen(*(_4_main__argv + (_4_main__argc - 1)));
if (y <= strcmp_result23 + x) {
QUESTION_10_1(& z);
} else {
QUESTION_10_1_COPY(atoi_result24, & z);
}
}
} else {
{
QUESTION_9_1_COPY(& z, 6.);
}
}
{
}
} else {
{
QUESTION_8_1_COPY(& z, 1L);
}
}
{
}
} else {
{
QUESTION_7_1_COPY(& z, z);
}
}
{
}
} else {
{
QUESTION_6_1_COPY(0, & z);
}
}
{
}
} else {
{
QUESTION_5_1_COPY(& z, 0);
}
}
{
}
} else {
{
QUESTION_4_1_COPY(0, & z);
}
}
{
}
} else {
{
QUESTION_3_1_COPY(& z, 0.);
}
}
{
}
} else {
{
QUESTION_2_1_COPY(4L, & z);
}
}
{
}
} else {
{
QUESTION_1_1_COPY(6L, & z);
}
}
{
}
}
}
Notice that we attempt (and sometimes fail) to select opaque predicates
expr1<=expr2 such that
- all the expri are independent of each other (i.e. analyzing a function should require you to break all the predicates in it);
- the expri depend on input (either because they are created from an entropy source or from invariants over the command line arguments); and
- expr1 and expr2 are computed from different sources.
Options
| Option | Arguments | Description |
|---|---|---|
| --Transform | AddOpaque | Add opaque predicates to split up control-flow. |
| --AddOpaqueCount | INTSPEC | How many opaques to add to each function. Default=1. |
| --AddOpaqueKinds | call, bug, true, junk, fake, question, * | Comma-separated list of the types of insertions of bogus computation allowed. Default=call,bug,true,junk,question.
|
| --AddOpaqueObfuscate | BOOL | Perform some light obfuscation of copied code when using the 'question' opaque predicate. Default=true. |
| --AddOpaqueSplitKinds | top, block, deep, recursive, level, inside | Comma-separated list specifying the order in which different split methods are attempted when --AddOpaqueKinds=question is specified. Default=top,block,deep,recursive.
|
| --AddOpaqueSplitLevel | INTSPEC | Levels which could be split out when specifying --AddOpaqueSplitKinds=level Default=1. |
| --AddOpaqueStructs | list, array, input, env, * | Default=list,array.
|
Examples
| Add Opaque Branches |
|---|
| Break up code by inserting bogus branches, protected by opaque predicates. |
tigress --Environment=x86_64:Darwin:Clang:5.1 --Verbosity=1 \ --Transform=InitOpaque --Functions=main \ --Transform=UpdateOpaque --Functions=fib --UpdateOpaqueCount=10 \ --Transform=AddOpaque --Functions=fib --AddOpaqueCount=10 --AddOpaqueKinds=call,bug,true,junk \ --Transform=CleanUp --CleanUpKinds=annotations \ --out=gen/opaque.c test1.c |
| test1.c ⇒ opaque.sh.txt ⇒ opaque.c |
Issues
--AddOpaqueKinds=fake will result in undefined symbols being generated. You need to coerce the linker to ignore such errors. With gcc you can use this option:
-Wl,--unresolved-symbols=ignore-in-object-files
No similar option seems to exist for clang.