I want you to listen

 

"The whole is more than the sum of the parts."

 Aristotle (384-322 BC)

BabelFish Anyone?

We now had the means to decode the decoder and we were going to use it, but first we had to sit down with the age old method of etching bleached wood pulp with carbon graphite (paper and pencil). We knew from the few forays into debugging that the following was the most likely function prototype for the decoder:

void decode(int length, char *decBuf, char *encBuf);

Basically, we found that the decoder was working through the buffer passed as encBuf from the last character backwards and copying it via a temporary string into the decBuf. We were slightly baffled by the %al movements but we'll come to that later. So, to the first part of the decoder. The snippet below from the wtfu4_dumps/main.rec shows the section in question, we'll go through what we thought in stages and then show the final decoder.

/*	Procedure: 0x0804A1E8 - 0x0804A2A4
 *	Argument size: 12
 *	Local size: 4
 *	Save regs size: 12
 */

L0804A1E8(A8, Ac, A10)
/* unknown */ void  A8;
/* unknown */ void  Ac;
/* unknown */ void  A10;
{
	/* unknown */ void  ebx;
	/* unknown */ void  esi;
	/* unknown */ void  Vfffffffc;



    ebx = A8 - 1;
    al = A8 + 3 & 252;
    esp = esp - eax;
    Vfffffffc = esp;
    *A10 = *L080675E5;
    if(ebx >= 0) {
        do {
            edx = ebx - 1;
            if(ebx == 0) {
                eax = *Ac & 255;
            } else {
                esi = Ac;
                eax = *(ebx + esi) & 255;
                eax = eax - ( *(edx + esi) & 255);
            }
            ecx = eax - 23;
            if(ecx < 0) {
                do {
                } while(ecx = ecx + 256);
            }
            edx = 0;
            if(0 < A8) {
                do {
                    al = *(edx + A10);
                    *(edx + Vfffffffc) = al;
                    edx = edx + 1;
                } while(edx < A8);
            }
            *A10 = cl;
            edx = 1;
            if(1 < A8) {
                do {
                    al = *(edx + Vfffffffc - 1);
                    *(edx + A10) = al;
                    edx = edx + 1;
                } while(edx < A8);
            }
            eax = L0804F808(A10, "%c%s", ecx, Vfffffffc);
        } while(ebx = ebx - 1);
    }
    esp = ebp - 16;
}

Right, we'll start at the beginning. The function starts by setting some local variable. In particular,

ebp = a local counter, initialised to length - 1.
*A10 = *L080675E5; simply sets decBuf[0] = '\0';

We then enter a while loop that would be written something like this:

do {

...

} while (count--);

So we have the beginnings of our decoder. The next stage was puzzling for a while, but turns out to be quite simple. It assigns a second variable counter (say j) and sets it to the current value of the counter - 1. If we are at the beginning of the buffer we just copy value of the first character. If we aren't then we set a variable to the difference between the count-th character and the j-th character (if there are such terms). We can write this like so:

j = count -1;
if (count == 0)
{
    char = encBuf[0];
}
else
{
    char = encBuf[count] - encBuf[j];
}

We told you it was simple. Looks much more complicated in the reverse engineered code, but this is essentially what it is. We next subtract 23 from that value, and if it is less than 0 (ie. less than the ascii range) add 256 to it until it is within the ascii range. Shown below:

while (char < 0)
      char += 256;

The next section of the decoder is a little puzzling, and we are still not 100% sure of it, only we know our decoder works. We think there may be some strange optimisations or just some casting going on with the buffers. The decoder takes the destination string and copies it into a temporary buffer. It then copies them back into its destination buffer one character to the right. This is where the %al register and the &255 comes into play because the decoder is forcing a cast as a char (1 byte). The strange thing is that the decoder then uses sprintf to apparently do exactly the same. There must be something subtle going on here that we don't fully understand, but the final lines of the ddecoder look like this:

		k=0;
		while (k<len)
		{
			tempBuf[k] = dest[k];
			k++;
		}
		
		dest[0] = (char)diff;
		k=1;
		while(k<len)
		{
			dest[k] = tempBuf[k-1];
			k++;
		}
		
		copied = sprintf(dest, "%c%s", int2, tempBuf);

So, we have our decoder. To test it works we attempted to create an encoder that worked the same as the hackers one must have worked, but we couldn't initially get it right. What we decided though, at this point, was that the decoder should end up with exactly the same decoded buffer given the same input. So we tried it. We encoded a really dodgy string of characters, and now we had worked out what the significance of the "0x2" character was. It was the data signature for the hacker. If the buffer came on protocol 0xb AND had data character "0x2 as the first entry then it was from the hacker. So we were excited by now to see what we could tease from the wtfu4 binary given that we thought we had a decoder.

The code above is an explanation of what we eventually got to, but we weren't far off when we originally worked it out, thankfully. The main problem we had was with the casting of (int) to (char) which makes a whole lot of difference depending on where it is being done. It only took 2 or 3 attempts to refine the decoder to give the same results as the wtfu4 binary. We have also included an encoder in the files to demonstrate it's simplicity. We managed to refine both these using the kdbg techniques employed to work out the decoder. We were ready to rock now, we could wake this sleeping beast and marvel at it's beauty.