开发者

Need help explain an obfuscated C++ code?

This code snippet drove me crazy, anyone could help me explain this?

#include <stdio.h>
char*_="XxTIHRCXCxTIHRXRCxTIHXHRCxTIXIHRCxTXTIHRCxXxTIHRCX";
int main(int l){for(l+=7;l!=putchar(010);++l);if(*(++_))main
    (*_!=88?(putcha开发者_开发百科r(*_^073)|putchar(33))&1:0xffff2a8b);}

Thanks,

Chan Nguyen


In order to understand how this code works, start rewriting it in a readable way:

#include <stdio.h>

char*_="XxTIHRCXCxTIHRXRCxTIHXHRCxTIXIHRCxTXTIHRCxXxTIHRCX";

int main(int l)
{
    for( l += 7; l != putchar(010); ++l ) {
    }

    if( *(++_) ) {
        main( ( *_ != 88 ) ? ( putchar(*_^073) | putchar(33) )&1 : 0xffff2a8b );
    }

    return 0;
}

Now let's understand it:

  • its parameter l (which will be 1, if you run this program without parameters) gets incremented by 7 (it becomes 8)

  • the loop will print 010 (octal for 8: ascii backspace) until l==8 (thus it won't do anything when you run the program

  • if the next character pointed by _ (it's x now) is different than 0 (this will probably mean "until we reached the end of _"), main is called, but lets see what happens while we're evaluating its parameters:

    • the character currently pointed by _ is different from 88 (88 is x in ascii), thus the parameter for main will be the result of expression ( putchar(*_^073) | putchar(33) )&1:

      while evaluating main's parameter two characters will be printed

      • first one is: *_^073, that's it, 120^59 (since x is 120, in ascii, and 073 in octal is 59 in decimal), which is 67: 120(0b1000011) XOR 59(0b111011) = 67 0b1000011

      • second one is 33 (!)

      main parameter will then be the result of (67|33)&1, which is 1

If you really want to understand what happens in the details you'll have to go on with this work, but you'll be able to see what happens by running the program (maybe put an usleep(10000) somewhere, so that you can actually see the output). It will write a roteating string "Corsix!".

Writing a program like this is pretty easy: once you decide how your algorithm works, it's easy to generate a string, like _, that makes the algorithm generate what you want, but to reverse engineer it is a lot more difficult.


While this piece of code is not standard compliant, gcc will compile it and the output is consistent with my analysis of the code. Sadly, I can't really interpret the output without a bit more context. If I ignore the backspaces, the output looks something like this:

C!o!r!s!i!...

To analyze the code, we'll start by formatting it a bit :

#include <stdio.h>

char* _ ="XxTIHRCXCxTIHRXRCxTIHXHRCxTIXIHRCxTXTIHRCxXxTIHRCX";

int main(int l){
    for(l+=7; l != putchar(010); ++l); // 010 = 8 -> backspace char
    if(*(++_))
        main(
            *_ != 88 ? // *_ != 'X'
                ( putchar(*_ ^ 073) | putchar(33) ) & 1 : // 33 = '!'
                0xffff2a8b);
}

Here's a few things worth noting before we go any further:

  1. If putchar succeeds, it returns the char it was passed.
  2. In C, numbers starting with 0 are actually octals and not decimals. So 010 is really the decimal number 8.

Now notice that whenever the _ pointer is outputed, it's XORed with the octal value 073. If we apply this on the entire string we get:

cCorsixcxCorsicixCorscsixCorcrsixCocorsixCcCorsixc

This is starting to resemble the output that we saw earlier. Let's continue by analysing a few of the more interesting lines:

for(l+=7; l != putchar(010); ++l); // 010 = 8 -> backspace char

The point of this line is to output a series of backspaces. If l is equal to 1 it will output only one backspace. But if it's equal to something else, it goes berserk on dumps a truck load of chars. The behaviour depends on how main is called. On startup, it appears to always be called with the value 1 (don't know why).

Now let's look at the components of the recursive main call.

( putchar(*_ ^ 073) | putchar(33) ) & 1 : // 33 = '!'

This is the first possible branch. First it outputs, one of the XORed characters and then it outputs a '!' char. If you look at the bit pattern of 33, you'll notice that (x | 33) & 1 will always evaluate to 1. So in this case we're only outputting a single backspace character in the for loop.

The second branch on the other hand is a little trickier because the value passed to main is not 1. If you look carefully at the output of the program you will notice that it does output a truck load of backspaces at a certain place in the string. Without context, I can't really say what the goal is.

Now that we have all the pieces, let's rewrite the code:

#include <stdio.h>

#define BIG_CONSTANT 42 // Not the actual value.

int main () {
    char* str = "cCorsixcxCorsicixCorscsixCorcrsixCocorsixCcCorsixc";

    putchar(8);

    char* c = str;
    while (*c != '\0') {

        if (*c != 'c') { // 'X' ^ 073 = 'c'
            putchar(*c);
            putchar('!');
            putchar(8);
        }
        else {
            for (int i = 0; i < BIG_CONSTANT; ++i)
                putchar(8);
        }
        c++;
    }
}

My C is a little rusty so this might not compile/run. It should still give you a good idea of what's going on.

EDIT: Well I was a little late in posting my answer and I just realised that my console was printing the backspaces a little to literaly instead of just removing chars. So that's why I somewhat misinterpreted the output. So like the accepted answer says, if you actually process the backspaces correctly, it prints Corsix!.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜