Obfuscation




Too obvious

When writing programs in C, many are struck by the all too obvious intentions of their programs. As an example, take the classic ``hello world'' program:


  #include <stdio.h>

  main () {
    printf ("Hello world!\n");
    return 0;
  }

One may wonder how the striking clarity of this program is best avoided.




A first try

Two things spring to mind:
  • get rid of the printf() call
  • encode the blatantly obvious "Hello world!"
It is easy to write a function that passes all characters of a string to putchar() and another one to decode encoded characters using a simple lookup string.

Let us use the string "&$#%@!)*~'!dowl\n Hre", consisting of the characters after encoding (a number of randomly picked, unique characters), followed by the original characters. Of course, the letters from the original "Hello world!" are thoroughly shuffled.

This brings us at our first attempt


  dec (char *code, int c) {
    if (!*code) return 0;
    else if (*code == c) return code[10];
    else return dec (code+1, c);
  }

  decode (int c) {
    return dec ("&$#%@!)*~'!dowl\n Hre", c);
  }

  void output(char *s) {
    if (*s) {putchar (decode (*s)); output (s+1);}
  }

  main () {
    output ("*'@@#)%#~@$&!");
    return 0;
  }

Notes

  • putchar() doesn't need a prototype, so we do not need to include stdio.h.
  • there are numerous other ways to encode and decode. For example, the same encoded string "*'@@#)%#~@$&!" we used above could be decoded using a string of pairs of characters where each pair is an encoded and a decoded character. But there is nothing to stop you from using true encryption techniques.



Going further

Clearly, this is not yet up to standards, but it is getting somewhere. Some of the things about the program so far that still nag me are that
  • the function names are too informative
  • the control flow is too easily retrieved
The latter is not difficult to make much harder by using C's ternary operator ?: and putting everything in a single function. Let us agree on the following:
  • f = 1 represents a call to dec()
  • f = 2 represents a call to decode()
  • f = 4 represents a call to output()
We will use the function skeleton


  x(f) {
    return f==1 ? /* code for dec() */ :
           f==2 ? /* code for decode() */ :
           f==4 ? /* code for output() */ : 0;
  }



Hello obfuscated world!

And finally, using some more C tricks, we get an obfuscated ``hello world'' program:


  x(f,s,c)char*s; { return
    f&1 ? *s ? *s-c ? x(f,++s,c) : 10[s] : 0 :
    f&2 ? x(--f,"&$#%@!)*~'!dowl\n Hre",c) :
    f&4 ? *s ? x(f,s+1,putchar(x(f-2,"^&%!*)",*s))) : 0 : 0;
  }

  main() {
    return x(4,"*'@@#)%#~@$&!",65);
  }

Which is more like it :-)

Some of the `tricks':

  • K&R style function headings. These allow us to leave out the types for all arguments for which the implicit int is fine.
  • the tests f==1 etc. are replaced with f&1 etc. which is okay since the f's are powers of two.
  • in a context where a condition is expected, *s!=c and *s-c are the same.
  • 10[s] is the same as s[10] but looks funnier.
  • if f isn't used afterwards, we can use ++f instead of f+1. Or even: f++ instead of f. But beware of mixing an f++ (or --f or whatever) with other uses of f between sequence points!
  • decode() and output() take one argument, but x() takes two besides the function code f. To enhance obfuscation, when x() is called with f = 2 or 4, the unused arguments are set to some wild value. The totally irrelevant string "^&%!*)" in the call x(f-2,..) is one example. The use of putchar() as argument to x() is another. We could even write 'A'+putchar(..) to puzzle the reader even more.
  • not a trick, but check that main() always returns 0 to the environment!

A little bit more can be done about this program like removing white space, changing the layout so the f&'s don't stick out so clearly, and removing the f&4. Like so


  x(f,s,c)char*s;{return
  f&1?*s?*s-c?x(f,++s,c):10[s]:0:f&2?
  x(--f,"&$#%@!)*~'!dowl\n Hre",c):*s?
  x(f,s+1,putchar(x(f-2,"^&%!*)",*s))):0;}

  main(){return x(4,"*'@@#)%#~@$&!",65);}



To hell with the standard?

True hackers don't care about standards. The code above is (strictly, as far as I can tell) ANSI/ISO C compliant, but can be tricked even more if we
  • use main() with three arguments, of types int, char*, int
This breaks the rules in two respects: it declares one argument too many, and the second one is of wrong type (it should be char**).

To avoid dependence on the value main() gets as first argument from the environment (all we know is that it is positive), we shall use negative function codes (the f's above). We can now put everything in one function:


  main(f,s,c)char*s;{return
  -f<0?main(-4,"*'@@#)%#~@$&!",65):f&1?*s?
  *s-c?main(f,++s,c):10[s]:0:f&2?main(++f,"&$#%@!)*~'!dowl\n Hre",c):
  *s?main(f,s+1,putchar(main(f+2,"^&%!*)",*s))):0;}

Since this version is non-standard, there is a slight chance that it won't run as expected on your system.




Would you like more stuff like this?

Well, you are lucky then! I can offer you a few programs rightaway: I didn't write bug.c, hammer.c and pi.c; I collected them from postings to the comp.lang.c newsgroup. Unfortunately, the names of the authors have been lost in the mists of time... Well, somebody must know who wrote these masterpieces!

You will notice that there are many more obfuscation techniques than I discussed here. Those are really just beginners work. Often people will do something about the visual appearance of their programs, use the preprocessor to make things even less intelligible, or even use system-specific tricks to prevent other people from understanding what their programs do.

I am particularly fond of my contribution in smile.c: using my own name in the decode string. You will have to understand the program in order to change it to your own name and still keep the program working!

But if you want more of the real stuff out there, check out the archives of the International Obfuscated C Code Contest (or IOCCC for short) whose winning entries are accessible at




Ha! You want to try your luck?

Receive everlasting fame, enter the annals of the IOCCC, never get an employer, show off as a Great Hacker... Is that the Ultimate Goal for you? Then here's where you should go!



And back to Arjan's home page...

This page was written by Arjan Kenter.