The goal of this transformation is to hide calls to external functions, such as system calls or calls to standard library functions. Our current implementation has two variants:
Option | Arguments | Description |
---|---|---|
--Transform | InitEncodeExternal | Replace direct system calls with indirect ones through dlsym. |
Here, we simply replace direct calls with indirect ones, and load the address of the functions using dlsym().
Option | Arguments | Description |
---|---|---|
--Transform | InitEncodeExternal | Replace direct system calls with indirect ones through dlsym. |
--InitEncodeExternalSymbols | Comma-separated list of strings | List of of external functions to be encoded. |
Option | Arguments | Description |
---|---|---|
--Transform | EncodeExternal | Replace direct system calls with indirect ones through dlsym. |
--EncodeExternalObfuscateIndex | BOOLSPEC | Use opaque expressions to compute function addresses. Default=true. |
Consider the following program syscall.c
which makes two system calls to getpid
and gettimeofday
:
void tigress_init() {}
int main () {
tigress_init();
int x = getpid();
printf("%i\n", x);
struct timeval tv;
int y = gettimeofday(&tv, NULL);
printf ("%ld.%06ld\n", tv.tv_sec, tv.tv_usec);
}
We obfuscate using the following script:
> tigress -ldl \
--Environment=x86_64:Darwin:Clang:5.1 \
--Transform=InitEncodeExternal \
--Functions=tigress_init \
--InitEncodeExternalSymbols=getpid,gettimeofday \
--Transform=EncodeLiterals \
--Functions=tigress_init \
--EncodeLiteralsKinds=string \
--EncodeLiteralsEncoderName=STRINGENCODER \
--Transform=Virtualize \
--Functions=STRINGENCODER \
--Transform=EncodeExternal \
--Functions=main \
--EncodeExternalKind=dynamic \
--EncodeExternalSymbols=getpid,gettimeofday \
--out=syscall_out.c syscall.c
> gcc -o syscall_out syscall_out.c -ldl
The InitEncodeExternal
transformation uses dlsym()
to load
the system calls we want to hide by name, the EncodeLiterals
transformation
hides these names in a function we call STRINGENCODER
, the
Virtualize
transformation hides what's going on in the STRINGENCODER
function, and finally, the EncodeExternal
transformation replaces the
direct calls to the system calls in main()
with indirect ones.
The resulting code will look something like this:
void *_externalFunctionPtrArray[2];
void tigress_init(void) {
STRINGENCODER(0, encodeStrings_litStr0);
_externalFunctionPtrArray[0] = dlsym((void *)-3, encodeStrings_litStr0);
STRINGENCODER(1, encodeStrings_litStr1);
_externalFunctionPtrArray[1] = dlsym((void *)-3, encodeStrings_litStr1);
}
void STRINGENCODER(int n , char str[] ) {
STRINGENCODER_$sp[0] = STRINGENCODER_$stack[0];
STRINGENCODER_$pc[0] = STRINGENCODER_$array[0];
while (1) {
switch (*(STRINGENCODER_$pc[0])) {
case STRINGENCODER__store_char$left_STA_0$right_STA_1:
(STRINGENCODER_$pc[0]) ++;
*((char *)(STRINGENCODER_$sp[0] + 0)->_void_star) = (STRINGENCODER_$sp[0] + -1)->_char;
STRINGENCODER_$sp[0] += -2;
break;
...
}
int main( ) {
int x,y;
struct timeval tv ;
...
tigress_init();
x = ((pid_t (*)(void))_externalFunctionPtrArray[1])();
printf((char const *)"%i\n", x);
y = ((int (*)(struct timeval * __restrict , void * __restrict ))
_externalFunctionPtrArray[0])((struct timeval *)(& tv),
(void *)((void *)0));
printf((char const *)"%ld.%06ld\n", tv.tv_sec, tv.tv_usec);
}
Note how this program no longer has any mention of getpid
and gettimeofday
. The transformation is purely static,
of course; at runtime it is trivial to see that these functions are
being called.
IDA Pro's F.L.I.R.T.
attempts to identify common library functions by creating a signature from the first
32 bytes of each function. It also provides a tool to identify signatures for
third party libraries called idenlib.
To thwart such attacks you can embed libraries into your program and then obfuscate them.
You can of course do this manually, but Tigress provides functionality to make this simple.
Currently we only support the
We obfuscate using the following script:
string
library.
> tigress -ldl \
--Environment=x86_64:Darwin:Clang:5.1 \
--Transform=EncodeExternal \
--Functions=main \
--EncodeExternalKind=embed \
--EncodeExternalSymbols=strcmp \
--Transform=Inline \
--Functions=string_strcmp \
--out=embed.c embed.c
Note that, after embedding the string
library functions they get prefixed by
string__
. This is, after embedding, the strcmp
function is now called
string__strcmp
. By further obfuscating the embedded functions (here we're
inlining strcmp
), you can bypass the F.L.I.R.T. identification.
We currently support embedding the following functions:
memccpy → string___memccpy
memchr → string___memchr
memcmp → string___memcmp
memcpy → string___memcpy
memmem → string___memmem
memmove → string___memmove
memrchr → string___memrchr
stpcpy → string___stpcpy
stpncpy → string___stpncpy
strcasecmp → string___strcasecmp
strncasecmp → string___strncasecmp
strcasestr → string___strcasestr
strcat → string___strcat
strchr → string___strchr
strcmp → string___strcmp
strcoll → string___strcoll
strcspn → string___strcspn
strdup → string___strdup
strlcat → string___strlcat
strlcpy → string___strlcpy
strlen → string___strlen
strncat → string___strncat
strncmp → string___strncmp
strncpy → string___strncpy
strndup → string___strndup
strnlen → string___strnlen
strpbrk → string___strpbrk
strsep → string___strsep
strspn → string___strspn
strstr → string___strstr
strtok → string___strtok
strtok_r → string___strtok_r
strxfrm → string___strxfrm
wcslcpy → string___wcslcpy
ioctl.
dlsym
idea in a
DEFCON
talk.
-ldl
option.
On MacOS/clang this is not necessary.
--Environment=
wrong, your program will fail.
LibC
string
library. We have, however,
developed a tool that makes embedding new libraries into Tigress more-or-less trivial. If you
have a library you would like to add to Tigress, let us know: we just require the
source to be published under a permissive license.