Site in American English
Password
User name
 
 
« There are some defeats more triumphant than victories »
Michel de Montaigne
MyDoom Klingonic ROT13 Encryption

MyDoom encrypts a lot of its strings using the famous ROT13 method... but in a very strange way!

Pure K&R Style

Did you plan to debug MyDoom? Very often, you'll have to fight with a basic encryption process: ROT13. Fight/basic? You guess it's a little antinomic? It's not! Of course, ROT13 is a very simple method (it's the basic part), but the way the guy has implemented it inside MyDoom shows us that Kernighan and Ritchie's obfuscated style of coding is not dead! If you are a UNIX/LINUX source code freak (it's the fighting part), you're at the right place...

Believe me, you'll quickly resign with a debugger when tracing the Ping-Pong ROT13 loops! Just have a look at the C source code:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
char *xstrchr(const char *str, char ch)
{
  while ( *str && *str != ch ) str++;

  return ( *str == ch ) ? (char *)str : NULL;
}

char rot13c(char c)
{
  char u[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  char l[] = "abcdefghijklmnopqrstuvwxyz";
  char *p;

  if ( ( p = xstrchr(u, c) ) != NULL )

    return u[ ((p-u) + 13) % 26 ];

  else if ( ( p = xstrchr(l, c) ) != NULL )

         return l[ ((p-l) + 13) % 26 ];

       else return c;
}

void rot13(char *buf, char *in)
{
  while ( *in ) *buf++ = rot13c(*in++);

  *buf = 0;
}
 

Figure 1. MyDoom ROT13 mess.

You think it's really ugly? I agree. Let's turn this into a more readable form (I mean, let's add some indentation, some test values, etc.)...

rot13()

Not that much horrible. Two parameters are used: in is a pointer to the string to be ROT13ed1, buf is a pointer to the buffer to receive the ROT13ed string. The purpose of the function is obvious: every character of in is read one by one, ROT13ed, then "appended" to buf. Finally, an inevitable null character ends the output string, buf. A more airy code could be:

01
02
03
04
05
06
07
08
09
10
11
12
13
void rot13(char* buf,char* in)
{
  while ( 0 != *in )
  {
    *buf = rot13c(*in);

    buf++;

    in++;
  }

  *buf = '\0';
}
 

Figure 2. rot13() academic version.

xstrchr()

Let's show the "clean" code first:

01
02
03
04
05
06
07
08
09
char* xstrchr(const char* str,char ch)
{
  while ( ( 0 != *str ) && ( *str != ch ) )
  {
    str++;
  }

  return ( *str == ch ) ? (char*)str : NULL;
}
 

Figure 3. xstrchr() academic version.

This function finds the first occurence of ch in str. Every character of str is compared to ch (line 3): if ch is not found in str, the NULL value is returned; if ch is found, the pointer to its first occurrence in str is returned (line 8). Note that I've written "pointer", not "index"...

rot13c()

Look at this nice overindented code :-):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
char rot13c(char c)
{
  char u[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  char l[] = "abcdefghijklmnopqrstuvwxyz";
  char *p;

  if ( ( p = xstrchr(u,c) ) != NULL )
  {
    return u[ ( (p-u) + 13 ) % 26 ];
  }
  else
  {
    if ( ( p = xstrchr(l,c) ) != NULL )
    {
      return l[ ( (p-l) + 13 ) % 26 ];
    }
    else
    {
      return c;
    }
  }
}
 

Figure 4. rot13c() academic version.

The only parameter sent to the function is the character to be ROT13ed. At line 7, we check if this character is an alphabetic upper case. If you don't remember the xstrchr() comments, the work done is a kind of "if character c is found in string u, p will hold the pointer of the corresponding letter in u, or NULL otherwise". At line 13, same sort of job: we look if c is an alphabetic lower case.

If c is not an alphabetic upper case or lower case, it's not ROT13ed (that's the rule).

If c is an alphabetic upper case or lower case, we have to ROT13 it. It's done at lines 9 and 15. There's a last trick to mention anyway; as p holds a pointer, we need to compute (p-u) and (p-l) to get the position (or index) in u and l strings of the character to be ROT13ed.

So What?

Well, it's a real pain in the neck to debug this "multifunction" code. Why the hell didn't he use such a stuff like:

01
02
03
04
05
06
07
08
09
10
11
void rot13(char* buf,char* in)
{
  while ( 0 != *in )
  {
    *buf++=((*in>='A'&&*in<='Z')?'A'+(*in-'A'+13)%26:((*in>='a'&&*in<='z')?'a'+(*in-'a'+13)%26:*in));

    in++;
  }

  *buf = '\0';
}
 

Figure 5. rot13() simplified.

It's shorter, faster and as much unmistakable. But now, the disassembled code is much more easy to grasp. Please, spare reverse engineers tired neurons. By the way, we can shorten the line 5 above :

05
*buf++=((*in>64&&*in<91)?65+(*in-52)%26:((*in>96&&*in<123)?97+(*in-84)%26:*in));
 

Figure 6. Obfuscated version of the ROT13 process.

Nice, uh? :-).

Footers

1. Notice the invention of a brand new verb: to ROT13...

Credits

Discharge - A Hell on Earth (musical inspiration).

Enjoy, cya!

(Written 03/11/2004, revised 10/11/2009)

Click here to go to the Malwares page.
Link to this page
Page #7200002, generated in 35.16 ms
 
Copyright © 2003-2017 Arnold McDonald. All rights reserved.
W3C HTML conformity
W3C CSS conformity