main(){printf(&unix["\021%six\012\0"], (unix)["have"]+"fun"-0x60);}

having fun abusing the C language

Posted by eric on October 24, 2016

I was looking through the book Expert C Programming again when I came upon their “light relief” section on The International Obfuscated C Code Competition. It’s a contest to write the most obscure code. That the language C has a competition where you write confusing code in it probably says something about the language. I wanted to see how one of the entries to this contest works. Not finding any explanation in Internet searches, I decided to investigate for myself.

The IOCCC was inspired by Steve Bournes decision to (ab)use the C preprocessor when writing his Unix shell in a syntax of C more like Algol-68 with explicit end statement cues where you have code like

if
  ...
fi    

He achieved this with code similar to

#define IF if(
#define THEN ){
#define ELSE } else {
#define FI ;}

This let him write code like

IF *s2++ == 0
THEN return(0);
FI

As Expert C says about this kind of code;

Shun any use of the C preprocessor that modifies the underlying language

One of the early winners in 1987 was a one-liner from David Korn, writer of the Korn shell (what’s up with these shell writers):

main(){printf(&unix["\021%six\012\0"], (unix)["have"]+"fun"-0x60);}

That’s it. Go ahead and compile it. What does it print?

It won’t work on Microsoft (hint!), but I found it already on the online compiler ideone where you can try it. (Some things added to get it to work there, but otherwise the same.)

It just prints

unix

Where does that come from? There’s what looks like an array named unix, but it’s not declared here. Is unix a keyword? Is it somehow printing the variable name?

I blindly tried to test it by adding a

printf(unix);

at the end to have it tell me printf takes a char * and not an int.

Printing it out as an int it tells me that its value is 1. This makes me think it’s a #define, like define this as being compiled on a Unix system. Searching the gcc source code, I find that it’s a run-time target specification. This explains why it won’t work on Windows.

unix is just 1. Rewritten we have

main(){printf(&1["\021%six\012\0"], (1)["have"]+"fun"-0x60);}

So unix wasn’t the name of an array variable, but how does 1[] work? I’ve seen this before and it’s one of my favorite facts about C.

C has origins in the langauge BCPL. From Dr. Martin Richards, creator BCPL;

The monodic indirection operator ! takes a pointer as it’s argument and returns the contents of the word pointed to. If v is a pointer !(v+I) will access the word pointed to by v+I. … The dyadic version of ! is defined so that v!i = !(v+I). v!i behaves like a subscripted expression with v being a one dimensional array and I being an integer subscript. Note that, in BCPL v!5 = !(v+5) = !(5+v) = 5!v. The same happens in C, v[5] = 5[v].

In other words, subscripts are just adding to pointers, and since addition is commutative, then so is the subscript operator. Go ahead and try that too.

int x[] = {1, 2, 3};
printf("%d\n%d\n", x[1], 1[x]);

Then what’s 1["\021%six\012\0"]? Written the normal way we see accessing array elements with the subscript operator, we have "\021%six\012\0"[1]. Still not typical, but you can see it’s array[index], albeit usually a string literal isn’t used. But that works too, try that as well;

printf("%c\n", "hello, world"[1]);

Let’s rewrite just that first array while we’re figuring it out.

main() {
  char str[] = "\021%six\012\0";
  printf(&str[1], (1)["have"]+"fun"-0x60);
}

That still works the same. Looking at str, I wonder about the \0 which is the null character (or NUL character?) I thought C string literals have a null character by default. Seeing what happens when we remove it;

printf("%s", "\021%six\012");

prints

█%six

I use the format string "%s" because the string I’m trying to print contains the format character %. (C programming tip: don’t just print strings like printf(myStr) in case the string you’re trying to print has a format character. Print through %s as shown above. Or use puts as a commentor mentioned below.)

It still seems to work without the \0. Maybe you had to add your own null characters to string literals in some pre-ANSI C? I guess not since other strings in the program don’t have it. Or that was more obfuscation? Regardless, let’s leave out that \0.

While we’re at it, let’s look at the rest of that string. \xxx is how to give a single character in octal, \021 is some control character and \012 is line feed, or \n as you normally see at the end of strings you want to print.

Knowing \021 is just a single character, str[1] is %. &str[1] then is the string starting at %. So the string can actually just be %six\n, leaving out that control character that I don’t even know what it means.

main() {
  char str[] = "%six\n";
  printf(str, (1)["have"]+"fun"-0x60);
}

The first string passed to printf is the format string, and %s means put the next string in its place. With this string ending in ix, we can guess that the next string passed to printf must be un some how. That’s simple enough for us to get rid of that character array we used to pull it out.

main() {
  printf("%six\n", (1)["have"]+"fun"-0x60);
}

For the next string, we have (1)["have"]+"fun"-0x60. There’s a un in fun, now we have to get to it.

We have that indexing trick again with (1)["have"]. The parentheses around 1 aren’t needed. Again, required in old C, or more misdirection? "have"[1] gives us a. The hex value for the character a is 0x61 and we subtract 0x60. This is then just 1+"fun".

Similar to before, "fun" basically resolves to a char *. Adding 1 to it gives us the string starting at the second character, or un. This becomes

main() {
  printf("%six\n", "un");
}

There’s the unobfuscated code.

I like how there’s some obfuscation that’s more semantic, like using the defined word unix to perhaps try to throw you off thinking it’s printing that #define itself somehow. The character \021 being a kind of reversed \012 may be to make you think it’s significant when it’s actually not used. There’s also possibly the format string %six containing what looks like the word “six”, perhaps meant to make you not think that the ‘s’ is used as a format character.

A lot to unpack from such little code, and plenty to learn.

Update

Some good comments at the Reddit link, including the discussion of this code that someone already did that I didn’t find in my searches.

Discuss on Hacker News or Reddit.