How to work out the password in 'rogue'

Rogue is text-based dungeon crawler game for linux. It's loved around the world by special people in a particular niche who may fondly remember the 1980's.

There is a password that can give you unlimited powers. This password changes between version and author.
I'm currently running Linux Mint 17 (MATE edition, incidentally, which is better imo than Cinnamon).

There are some websites that offer discussion forums on rogue. Some suggested passwords have been "cute,huh" "cute,huh?" "wizard" "rodney" and others. But rogue is an open-source game, so it's simple to deduce the password for your version for yourself. It takes some maths, some C programming skills, and some linux shell knowledge.

Given the source code, I worked out that the password for my version was "bathtub".

But how did I work that out ? This webpage gives you the details. Seasoned C programmers will nod sagely; there's no mystery in it.
If you want to play rogue, then this is an excellent site for tips.

Get the source code

I installed rogue from the ubuntu packages. When I check the version (press 'v' once in the game) I get my version as :
rogue-clone: Version III. (Tim Stoehr was here), tektronix!zeus!tims

Looking at this website, I downloaded the appropriate code and started reading it.
An obvious place to start is to see if the word "password" is anywhere in the code; and, sure enough :

sheldon@minty ~/Downloads/ROGUE/rogue $ ls
CHANGES   init.c       machdep.c  message.c  obj       pathnames.h  ring.c   room.c   spec_hit.c  trap.c
curses.c  inventory.c  main.c     monster.c  object.c  play.c       rogue.6  save.c   tags        use.c
hit.c     level.c      Makefile   move.c     pack.c    random.c     rogue.h  score.c  throw.c     zap.c
sheldon@minty ~/Downloads/ROGUE/rogue $ grep -i password *
zap.c:		if (get_input_line("wizard's password:", "", buf, "", 0, 0)) {
sheldon@minty ~/Downloads/ROGUE/rogue $ 

The above shows that the file zap.c contains the words "wizard's password", so that seems like a good place to start looking.

Using a text editor (I use vi), the complete function in zap.c that contains this text is :

wizardize()
{
        char buf[100];

        if (wizard) {
                wizard = 0;
                message("not wizard anymore", 0);
        } else {
                if (get_input_line("wizard's password:", "", buf, "", 0, 0)) {
                        (void) xxx(1);
                        xxxx(buf, strlen(buf));
                        if (!strncmp(buf, "\247\104\126\272\115\243\027", 7)) {
                                wizard = 1;
                                score_only = 1;
                                message("Welcome, mighty wizard!", 0);
                        } else {
                                message("sorry", 0);
                        }
                }
        }
}

The above shows that :
- buf is declared to be a character array of 100 chars long.
- wizard is a true/false variable; 1 is normally defined as logic TRUE and 0 as logic FALSE.
-- if wizard is already set TRUE (ie. you're already in superwizard mode) then it's set to FALSE and the function exits.
- the variable buf is populated with user entry (using function get_input_line)
- function xxx is called with parameter 1 (ie TRUE)
- function xxxx is called with the parameter buf (ie what you typed in)
- String comparison is made for the first 7 characters of buf (strncmp).
- If the user has entered "\247\104\126\272\115\243\027" then the password is accepted as correct.


Lets start at the end:

The password that you enter has to somehow translate as "\247\104\126\272\115\243\027". In linux, the \ means an escape character ; ie take the following number as the value of a character IN OCTAL, instead of the character itself.
So \247 means character number OCTAL247 on the ASCII chart.
Since OCTAL247 is part of the extended ASCII character set, not frequently entered on a keyboard, we are likely looking at an encrypted password.

Because the user-entered password is sent to two other functions, xxx and xxxx, it's evident that the password must be encrypted in these functions, and we'll head there next.

Incidentally, this is an excellent (basic) method for hiding passwords within computer programs. In case you don't know: strings that are defined in source code appear exactly the same, as a string, when compiled into executable code. They're usually bunched together towards the end of the compiled code. If you want to know what strings have been defined in (eg) Microsoft Word, then using any hexdump program will give them all to you. This is why passwords should be first encrypted and then checked character by character. It makes the compiled code harder to read. Not impossible, though. More advanced methods would not have the encryption technique given in the compiled code either.

So what's in those functions xxx and xxxx ?


A quick grep shows that both functions are ferreted away in source code file score.c
Again, a text editor shows us :
long xxx(st)
boolean st;
{
        static long f, s;
        long r;

        if (st) {
                f = 37;
                s = 7;
                return(0L);
        }
        r = ((f * s) + 9337) % 8887;
        f = s;
        s = r;
        return(r);
}
This is nice and simple: If you pass the parameter TRUE, then the seed values are hardcoded as 37 and 7. If you pass the parameter FALSE, then the return value comes from an easy mathematical formula and the seed values are changed.

 xxxx(buf, n)
char *buf;
short n;
{
        short i;
        unsigned char c;

        for (i = 0; i < n; i++) {

                /* It does not matter if accuracy is lost during this assignment */
                c = (unsigned char) xxx(0);

                buf[i] ^= c;
        }
}
Again, this is a nice and easy way to encrypt a password and the encryption technique is given to us.
Note: The line "buf[i] ^= c" is a quick method of writing buf[i] = buf[i] XOR c

So : for each character in the entered password, the function xxx is called and the password is encrypted using the return value and an XOR operation.
The 'for loop' is self-evident; the encryption is done using bitwise XOR. XOR is a very common (very old!) method of changing a value.
To understand it, we need binary, the XOR table, a calculator and an example. Linux comes with its own built in calculator that includes basic maths, scientific, financial and programming modes. We'll use the programming mode.

XOR Example:
Octal247 is Decimal 167, Hex A7 and binary 1010 0111.
The XOR rules state that if the values are the same, return 0 else return 1.
If we XOR Octal247 with (eg) 255, we do this :
       : 1010 0111 (Decimal 167, = Octal 247)
XOR    : 1111 1111 (Decimal 255)
EQUALS : 0101 1000 (Decimal 88)
So the encrypted character would be 88.
But the code is not encrypting using XOR 255, it's encrypting using XOR and the value returned from function xxx

So what is returned from function xxx ?

Function xxx returns a value based on the straightforward maths equation
((f * s) + 9337) % 8887
We know that f starts as 37; s starts as 7; the values are changed each time the function is called.
Put in a table, the results from calling this 7 times look like this :

Loop |    f     s      f*s   +9337   mod 8887  Return Value
1    |   37     7      259    9596        709           709 
2    |    7   709     4963   14300       5413          5413
3    |  709  5413  3837817 3847154       7970          7970
4    | 5413  7970   -- do these maths yourself ! ---   4562
5    | 7970  4562                                      2873
6    | 4562  2873                                      7638
7    | 2873  7638                                      2421
Now, rather cleverly, the function xxxx assigns the return value above into an unsigned char. That means that only 8 bits (one byte, between a decimal value of 0 and 255) are used. Anything else above decimal 255 is ignored.

Now that we know how it's encrypted, we can decrypt it.

Decrypting the first character

The first character in the encrypted password is \247 (Octal 247, Decimal 167, Binary 1010 0111).
To get to that value, we need to XOR our original character with 709. (The value returned from the first iteration of function xxx)
The beauty of XOR is that it's reversible :
167 XOR 709 = decimal 610; that's binary 0010 0110 0010.
That binary is then cast into an 8-bit character.
So the last 8 bits are 0110 0010.
Binary 0110 0010 is Decimal 98.
On the ASCII chart, Decimal 98 is keyboard letter 'b'.
So our password starts with 'b'.

Decrypting the second character

The 2nd character in the encrypted password is \104 (Octal 104, Decimal 68, Binary 0100 0100).
Using the same XOR technique with the second value from xxx, 5413:
68 XOR 5413 = decimal 5473; that's binary 0101 0110 0001.
Casting that binary into an 8-bit character gives us the last 8 bits; 0110 0001.
That's decimal 97, which on the ASCII chart is keyboard letter 'a'.

Decrypt the rest

We can then take each octal value from the encrypted string "\247\104\126\272\115\243\027", and XOR each in turn using the appropriate value returned from function xxx. The remaining characters come out at t, h, t, u and b giving us the original password : bathtub.

Of course, now that you know how to do it manually, you can write a quick program to do the maths for you.
#include <stdio.h>

void main()
{
char buf[100];

sprintf(buf, "%s", "\247\104\126\272\115\243\027");

 printf("%s", buf); 
printf("\n\n");

buf[0] = buf[0] ^ 709L;
buf[1] = buf[1] ^ 5413L;
buf[2] = buf[2] ^ 7970L;
buf[3] = buf[3] ^ 4562L;
buf[4] = buf[4] ^ 2873L;
buf[5] = buf[5] ^ 7638L;
buf[6] = buf[6] ^ 2421L;

 printf("%s", buf); 
}
And the results are :
sheldon@minty ~ $ cc paul.c
sheldon@minty ~ $ ./a.out
�DV�M�

bathtub

sheldon@minty ~ $ 
2196