John Viega suggests a method to obfuscate function calls in his book Secure Programming Cookbook for C and C++. It can be read here.
#define SET_FN_PTR(func, num) \
static inline void *get_##func(void) { \
int i, j = num / 4; \
long ptr = (long)func + num; \
for (i = 0; i < 2; i++) ptr -= j; \
return (void *)(ptr - (j * 2)); \
}
#define GET_FN_PTR(func) get_##func( )
#include <stdio.h>
void my_func(void) {
printf("my_func( ) called!\n");
}
SET_FN_PTR(my_func, 0x01301100); /* 0x01301100 is some arbitrary value */
int main(int argc, char *argv[ ]) {
void (*ptr)(void);
ptr = GET_FN_PTR(my_func); /* get the real address of the function */
(*ptr)( ); /* make the function call */
return 0;
}
I compiled it with gcc fp.c -S -O2
, Ubuntu 15.10 64bit, gcc5.2.1, and checked the assemby:
...
my_func:
.LFB23:
.cfi_startproc
movl $.LC0, %edi
jmp puts
.cfi_endproc
.LFE23:
.size my_func, .-my_func
.section .text.unlikely
.LCOLDE1:
.text
.LHOTE1:
.section .text.unlikely
.LCOLDB2:
.section .text.startup,"ax",@progbits
.LHOTB2:
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB25:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
call my_func
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
...
I see that my_func is called in main
. Can somebody explain how this method obfuscates the function call?
I see that many readers just come and downvote. I took the time the understand the problem, and when I failed to post it here. Please at least write some comment, instead of pushing the downvote button.
UPDATE: Turning off optimization I got:
...
my_func:
...
get_my_func:
...
main:
...
call get_my_func
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
call *%rax
...
I think there is no inlineing now. However I do not really understand why is it important...
I am still looking for an explanation what was the goal of the author with this code, even if it not working with today's smart compilers.
Obfuscation means to make something difficult to understand. Programming code is often obfuscated to protect intellectual property or trade secrets, and to prevent an attacker from reverse engineering a proprietary software program. Encrypting some or all of a program's code is one obfuscation method.
Antonyms for obfuscate. clarify, clear (up), illuminate.
Obfuscation: Obfuscation is a noun for the act of casting shadow or muddling the facts. Example: The overwrought, pretentious wording of her term paper was a poor obfuscation of the fact that she hadn't researched her topic.
verbbecome foggy or obscured. adumbrate. becloud. befog. blur.
The problem with that way to obfuscating function call relies on the compiler not being smart enough to see through the obfuscation. The idea here was that the caller shouldn't contain a direct reference to the function to be called, but to retrieve the pointer to the function from another function.
However modern compiler does this and when applying optimization they remove the obfuscation again. What the compiler does is probably simple inline expansion of GET_FN_PTR
and when inline expanded it is quite obvious how to optimize - it's just a bunch of constants that's combined into a pointer which is then called. Constant expressions are quite easy to compute at compile time (and is often done).
Before you obfuscate your code you should probably have a good reason to do so, and use a method suitable for the needs.
The idea of the suggested approach is to use an indirect function call so that the function address must be computed first and then called. The C Preprocessor is used to provide a way to define a proxy function for the actual function and this proxy function provides the calculation needed to determine the actual address of the real function which the proxy function provides access to.
See Wikipedia article Proxy pattern for details about the Proxy design pattern which has this to say:
The proxy design pattern allows you to provide an interface to other objects by creating a wrapper class as the proxy. The wrapper class, which is the proxy, can add additional functionality to the object of interest without changing the object's code.
I would suggest an alternative which implements the same type of indirect call however it does not require using the C Preprocessor to hide implementation details in such a fashion as to make reading of the source code difficult.
The C compiler allows for a struct
to contain function pointers as members. What is nice about this is that you can define an externally visible struct variable with function pointers a members yet when the struct is defined, the functions specified in the definition of the struct variable can be static
meaning they have file visibility only (see What does "static" mean in a C program.)
So I can have two files, a header file func.h and an implementation file func.c which define the struct
type, the declaration of the externally visible struct variable, the functions used with a static
modifier, and the externally visible struct variable definition with the function addresses.
What is attractive about this approach is that the source code is easy to read and most IDEs will handle this sort of indirect much nicer because the C Preprocessor is not being used to create source at compile time which affects readability by people and by software tools such as IDEs.
An example func.h file, which would be #included into the C source file using the functions, could look like:
// define a type using a typedef so that we can declare the externally
// visible struct in this include file and then use the same type when
// defining the externally visible struct in the implementation file which
// will also have the definitions for the actual functions which will have
// file visibility only because we will use the static modifier to restrict
// the functions' visibility to file scope only.
typedef struct {
int (*p1)(int a);
int (*p2)(int a);
} FuncList;
// declare the externally visible struct so that anything using it will
// be able to access it and its members or the addresses of the functions
// available through this struct.
extern FuncList myFuncList;
And the func.c file example could look like:
#include <stdio.h>
#include "func.h"
// the functions that we will be providing through the externally visible struct
// are here. we mark these static since the only access to these is through
// the function pointer members of the struct so we do not want them to be
// visible outside of this file. also this prevents name clashes between these
// functions and other functions that may be linked into the application.
// this use of an externally visible struct with function pointer members
// provides something similar to the use of namespace in C++ in that we
// can use the externally visible struct as a way to create a kind of
// namespace by having everything go through the struct and hiding the
// functions using the static modifier to restrict visibility to the file.
static int p1Thing(int a)
{
return printf ("-- p1 %d\n", a);
}
static int p2Thing(int a)
{
return printf ("-- p2 %d\n", a);
}
// externally visible struct with function pointers to allow indirect access
// to the static functions in this file which are not visible outside of
// this file. we do this definition here so that we have the prototypes
// of the functions which are defined above to allow the compiler to check
// calling interface against struct member definition.
FuncList myFuncList = {
p1Thing,
p2Thing
};
A simple C source file using this externally visible struct could look like:
#include "func.h"
int main(int argc, char * argv[])
{
// call function p1Thing() through the struct function pointer p1()
myFuncList.p1 (1);
// call function p2Thing() through the struct function pointer p2()
myFuncList.p2 (2);
return 0;
}
The assembler emitted by Visual Studio 2005 for the above main()
looks like the following showing a computed call through the specified address:
; 10 : myFuncList.p1 (1);
00000 6a 01 push 1
00002 ff 15 00 00 00
00 call DWORD PTR _myFuncList
; 11 : myFuncList.p2 (2);
00008 6a 02 push 2
0000a ff 15 04 00 00
00 call DWORD PTR _myFuncList+4
00010 83 c4 08 add esp, 8
; 12 : return 0;
00013 33 c0 xor eax, eax
As you can see this function calls are now indirect function calls through a struct specified by an offset within the struct.
The nice thing about this approach is that you can do whatever you want to the memory area containing the function pointers so long as before you call a function through the data area, the correct function addresses have been put there. So you could actually have two functions, one that would initialize the area with the correct addresses and a second that would clear the area. So before using the functions you would call the function to initialize the area and after finishing with the functions call the function to clear the area.
// file scope visible struct containing the actual or real function addresses
// which can be used to initialize the externally visible copy.
static FuncList myFuncListReal = {
p1Thing,
p2Thing
};
// NULL addresses in externally visible struct to cause crash is default.
// Must use myFuncListInit() to initialize the pointers
// with the actual or real values.
FuncList myFuncList = {
0,
0
};
// externally visible function that will update the externally visible struct
// with the correct function addresses to access the static functions.
void myFuncListInit (void)
{
myFuncList = myFuncListReal;
}
// externally visible function to reset the externally visible struct back
// to NULLs in order to clear the addresses making the functions no longer
// available to external users of this file.
void myFuncListClear (void)
{
memset (&myFuncList, 0, sizeof(myFuncList));
}
So you could do something like this modified main()
:
myFuncListInit();
myFuncList.p1 (1);
myFuncList.p2 (2);
myFuncListClear();
However what you would really want to do is to have the call to myFuncListInit()
be someplace in the source that would not be near where the functions are actually used.
Another interesting option would be to have the data area encrypted and in order to use the program, the user would need to enter the correct key to properly decrypt the data to get the correct pointer addresses.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With