Opaque Predicates

Posted by Eric | Research, Reversing | Wednesday 23 July 2008 12:21 pm

An Opaque predicate is a line of code, or lines of code that are basically useless. They actually do something, but in terms of usefulness they don’t do anything. Does that even make sense? Anyway they are designed to make it appear as if the code was doing something to divert the attention of an analyst. The purpose of an opaque predicate is to add a layer of obfuscation into code that makes reversing the code more time consuming. As you sift through useless instruction you realize the code is more of a distraction than anything else. Opaque Predicates make static analysis more time consuming because they are often difficult to discover. You stumble upon a code block only to find after a few minutes that it’s dead.

So what does an opaque predicate look like?

In a high level language such as C/C++ we can have the most basic opaque predicate as a simple IF Insertion. Inserting IF statements to always come back true will surely not be effective on performance but in terms of static analysis it can throw off an analyst for a few minutes as they chase down a rabbit hole.

So we have something similar to this:

1
2
3
4
5
6
7
8
9
if(true)
{
Dosomething</code>
 
}
else
{
Dosomething
}

True will always come back as true. The first branch will always be the one chosen in actual execution. However, the code in the first branch and the second branch will be the same. This adds an additional branch to a graph in IDA, and adds more code the analyst must sort through. Just two are manageable, imagine if you had multiple if else’s in the code!

Ida Graph

And here is the deadlist

You can add an additional level of complexity to the code with math operations. An opaque predicate such as:

1
2
3
4
5
6
int    add,q = 2;</code>
 
if(((q+q^2)%2)==0)
printf("here");
else
add = 2+8;

Utilizes Dead code insertions. The else operation can be anything you want, for simplicity sake I chose the addition of two numbers and the assignment into a variable. However in real examples the code should be more complex. It should be dead code so that it may be functional but does not truly serve a purpose to the program. This will often lead the analyst down a path where they may spend anywhere from 10 minutes to an hour with their attention diverted from the real path. This type of predicate has the general layout of

1
2
3
4
if(math expression that always resolved)
Real work
Else
Dead code

In IDA this would graph similar to:
Graph
The dead listing would look like the following:
Dead List

These examples are rather simple, but adding complex junk code to them can often divert an analyst’s attention for a period of time. Especially if you make the junk or dead code seems more interesting then the code behind the real work!