Readelf – a sexy little elf



  • Get a working key/keygen.
  • Allowed are only GPLed-tools.
  • Patching/Hijacking prohibited ;)

You can get the executable at or I’ve mirrored it here.

Now, I know what you’re thinking, but no cheating and looking at the solutions without giving it a whirl or I WILL murder ball you. You DONT want to find out what that means.


objdump is pretty limited, but it does what you need. Fine for dissasembly.

$objdump -D ./cm1 > textfileiusetomakenotes

If you’re hard core you’ll only use objdump.


gdb is a debugger. You also could just use this as a disassembler since it disassembles, but that’s just not the way I roll.

Some useful commands off the top of my head:

-break <memlocation> – set breakpoints throughout your program
-run/continue – ironically these actually mean walk/stop
-disassemble <memlocation> – disasemble the code at a memory location or label
-info registers – display the values in most the registers
-x *0x1234567 – display the contents of what’s at 0x1234567. Also related is x $eax which will display eax. Useful because you can do things like x $ebp+8 to view offsets of different registers. Another thing you can do is specify a data type, like for ints it would be x/i.

Assembly Notes

Now I have written a bit of code in assembly before, but reversing is a whole new ballgame.

Some things that took me awhile to wrap my mind around:

-Interleaving instructions are everywhere, although it makes sense when considering an ia36 processor
-Weird (to me) optimizations. Some ones that are extremely common ones I noticed are:
-xor %eax, %eax (set eax to 0) this is apparently faster than mov 0x0,%eax
-The only difference between cmp and sub is that sub stores a value. They both can set the ZF
-test %eax,%eax is just short for see if eax is 0
-It’s easier to try to guess at conditionals than it is to follow the gotos


Where to begin?

cm1 is looking at a file called /home/username/.key_username for a valid key. A valid key is ascii text that has the following properties:

esi must equal the ascii value of the current letter in the username, username[i]
esi is modified by the following symbols

* means esi+=5
/ means esi-=5
– means esi–
+ means esi++

In the keyfile, you will have a series of these symbols to make esi equal to username[i], then a comma, then username[i]. Some caveats are:

a symbol repeated > 5 times makes the key invalid. ie ***** is not allowed.
after every iteration, esi = esi/username[i]

So a valid key for a user lundeen is (with no white space):


Easy as pie.

Here is my disassembly with the notes I made as I figured out these facts. My comments are hashed #

080484d4 <main>:
80484d4: 8d 4c 24 04 lea 0x4(%esp),%ecx
80484d8: 83 e4 f0 and $0xfffffff0,%esp
80484db: ff 71 fc pushl 0xfffffffc(%ecx)
80484de: 55 push %ebp
80484df: 89 e5 mov %esp,%ebp
80484e1: 57 push %edi
80484e2: 56 push %esi
80484e3: 53 push %ebx
80484e4: 51 push %ecx
80484e5: 81 ec 28 10 00 00 sub $0x1028,%esp

#Get the username, return to %eax
80484eb: e8 f0 fe ff ff call 80483e0 <getuid@plt>
80484f0: 83 ec 0c sub $0xc,%esp

#call getpwuid with %eax as an arg returns a struct <password>
80484f3: 50 push %eax
80484f4: e8 a7 fe ff ff call 80483a0 <getpwuid@plt>

80484f9: 89 85 d8 ef ff ff mov %eax,0xffffefd8(%ebp)

80484ff: 5e pop %esi
8048500: 5f pop %edi

#push what %eax is pointing to, the username
8048501: ff 30 pushl (%eax)
8048503: 31 f6 xor %esi,%esi

#Hello, %s, let’s seen what you’ve done so far
8048505: 68 50 87 04 08 push $0x8048750
804850a: e8 c1 fe ff ff call 80483d0 <printf@plt>

804850f: 8b 85 d8 ef ff ff mov 0xffffefd8(%ebp),%eax
8048515: 5b pop %ebx

#%ebx is 0x8048750

8048516: 8d 9d ee ef ff ff lea 0xffffefee(%ebp),%ebx

#%ebx is now 0xbfdcc6a6 or which is a memory address that contains a “”
#This is a negative number
#below, ebx is sort of an iterator. It’s initially set to 1 below

804851c: ff 30 pushl (%eax); the username?
804851e: ff 70 14 pushl 0x14(%eax); working directory
8048521: 68 7f 87 04 08 push $0x804877f ; %s/.key_%s
8048526: 68 01 10 00 00 push $0x1001 ;1001
804852b: 53 push %ebx

#snprintf(*str (ebx), 1001(size),/home/lundeen/.key_lundeen)
804852c: e8 bf fe ff ff call 80483f0 <snprintf@plt>

8048531: 83 c4 18 add $0x18,%esp
8048534: 6a 00 push $0x0
8048536: 53 push %ebx
8048537: e8 c4 fe ff ff call 8048400 <open@plt>

#the 0 flag indicates read only
#the open function returns a -1 if an error occurs
804853c: 83 c4 10 add $0x10,%esp

#if there is no file or permissions are not set, this cmp is true
804853f: 83 f8 ff cmp $0xffffffff,%eax

8048542: 89 c7 mov %eax,%edi
8048544: ba 01 00 00 00 mov $0x1,%edx
8048549: c6 85 d3 ef ff ff 20 movb $0x20,0xffffefd3(%ebp)
8048550: c7 85 d4 ef ff ff 00 movl $0x0,0xffffefd4(%ebp)
8048557: 00 00 00

#if -1 != eax meaning if there is no error, jump to 804863c
804855a: 0f 85 dc 00 00 00 jne 804863c <main+0x168>
#else jump to 8048682
8048560: e9 1d 01 00 00 jmp 8048682 <main+0x1ae>

—->jmp if there is more than the end of file in the file
#From below, if there is something in the file, jump to here
#eax should be 0 to begin with

#move the last character on the file to %dl then the eax
8048565: 8a 55 ef mov 0xffffffef(%ebp),%dl
8048568: 89 d0 mov %edx,%eax
804856a: 88 95 df ef ff ff mov %dl,0xffffefdf(%ebp)
8048570: 83 e8 2a sub $0x2a,%eax

8048573: 3c 05 cmp $0x5,%al

#ja is jump if greater than (unsigned)
#804867d is the end of the program
#if(eax is * + , – . or /)
8048575: 0f 87 02 01 00 00 ja 804867d <main+0x1a9>
804857b: 0f b6 c0 movzbl %al,%eax
#this jump is hit
#%eax is * + , – . / because ja is unsighned
#jum at the 0x0487a4 + eax*4
#x/x6 *0x80487a4 =
#0x80487a4: (*)0x0804858f (+)0x08048585 (,)0x080485bc (-)0x0804859b
#0x80487b4: (.)0x0804867d (/)0x080485a5

#esi seems to be given an initial value of 0
804857e: ff 24 85 a4 87 04 08 jmp *0x80487a4(,%eax,4)
#if the char is +
8048585: 46 inc %esi
#compare to the previous byte read 0xfffefd3
8048586: 80 bd d3 ef ff ff 2b cmpb $0x2b,0xffffefd3(%ebp)
804858d: eb 20 jmp 80485af <main+0xdb>

#if the char is *
#esi +=5
804858f: 83 c6 05 add $0x5,%esi
8048592: 80 bd d3 ef ff ff 2a cmpb $0x2a,0xffffefd3(%ebp)
8048599: eb 14 jmp 80485af <main+0xdb>

#if the char is –
804859b: 4e dec %esi
804859c: 80 bd d3 ef ff ff 2d cmpb $0x2d,0xffffefd3(%ebp)
80485a3: eb 0a jmp 80485af <main+0xdb>

#if the char is /
#esi -=5
80485a5: 83 ee 05 sub $0x5,%esi
80485a8: 80 bd d3 ef ff ff 2f cmpb $0x2f,0xffffefd3(%ebp)

–>all punctuation ends up here

#if the value above is not the same as the previous byte?
#if it’s not the same jump to the start of the loop below
#AND set ebx (the counter) back to 1. This means you can’t have > 5
#of the same thing in a row
80485af: 75 7a jne 804862b <main+0x157>

80485b1: 43 inc %ebx
80485b2: 83 fb 05 cmp $0x5,%ebx

#if 5 >= ebx (ebx is incremented above and set to 1 below)
#looks like this reads another char and repeats
80485b5: 76 79 jbe 8048630 <main+0x15c>
#else if 5<ebx
#then exit
80485b7: e9 c1 00 00 00 jmp 804867d <main+0x1a9>

#if the char is a comma, we’re done manipulating edi
#break 3
80485bc: 51 push %ecx
80485bd: 8d 4d ef lea 0xffffffef(%ebp),%ecx
80485c0: 6a 01 push $0x1
80485c2: 51 push %ecx
80485c3: 57 push %edi
#read another byte from the file
80485c4: e8 47 fe ff ff call 8048410 <read@plt>
80485c9: 83 c4 10 add $0x10,%esp
80485cc: 48 dec %eax
#eax is now equal to zero if it’s not the end of the file
#if it is the end of the file then end the program
80485cd: 0f 85 aa 00 00 00 jne 804867d <main+0x1a9>

#eax should be the last char just read
80485d3: 0f b6 45 ef movzbl 0xffffffef(%ebp),%eax

#if esi is == to char just read (else exit)
80485d7: 39 f0 cmp %esi,%eax
80485d9: 0f 85 9e 00 00 00 jne 804867d <main+0x1a9>
#eax and ecx have something to do with getpuid(), probably
#pointers to the username
80485df: 8b 85 d8 ef ff ff mov 0xffffefd8(%ebp),%eax
80485e5: 8b 8d d4 ef ff ff mov 0xffffefd4(%ebp),%ecx
80485eb: 8b 10 mov (%eax),%edx
80485ed: 0f be 04 0a movsbl (%edx,%ecx,1),%eax

#if eax isequal to esi else exit
#eax is the current username letter
80485f1: 39 c6 cmp %eax,%esi
80485f3: 0f 85 84 00 00 00 jne 804867d <main+0x1a9>

80485f9: 41 inc %ecx
80485fa: 89 8d d4 ef ff ff mov %ecx,0xffffefd4(%ebp)

#if there are no more letters in the username
8048600: 80 3c 0a 00 cmpb $0x0,(%edx,%ecx,1)
8048604: 75 14 jne 804861a <main+0x146>
8048606: 52 push %edx
8048607: 8d 45 ef lea 0xffffffef(%ebp),%eax
804860a: 6a 01 push $0x1
804860c: 50 push %eax
804860d: 57 push %edi
804860e: e8 fd fd ff ff call 8048410 <read@plt>
#break 5
8048613: 83 c4 10 add $0x10,%esp
#break 4
8048616: 85 c0 test %eax,%eax
#break 2
#if eax is not equal to zero then end program
#if there are no more stuff in the file
8048618: 75 63 jne 804867d <main+0x1a9>

804861a: ba 0a 00 00 00 mov $0xa,%edx
#break 3
804861f: 89 f0 mov %esi,%eax
8048621: 89 d1 mov %edx,%ecx
8048623: 31 d2 xor %edx,%edx
8048625: f7 f1 div %ecx
8048627: 89 c6 mov %eax,%esi
#edx ->0; ecx-> 10; eax ->esi
#esi = esi/10
8048629: eb 05 jmp 8048630 <main+0x15c>
#this may needs to be hit at least once every five times so the loop can repeat
804862b: bb 01 00 00 00 mov $0x1,%ebx
8048630: 8a 85 df ef ff ff mov 0xffffefdf(%ebp),%al
8048636: 88 85 d3 ef ff ff mov %al,0xffffefd3(%ebp)

#jump here if the file exists
804863c: 50 push %eax
804863d: 8d 45 ef lea 0xffffffef(%ebp),%eax
#%eax is 0xbfdcd6a7 which is the buffer where the previous byte will be stored

#the read function
8048640: 6a 01 push $0x1
8048642: 50 push %eax

#eax is 0xbfa20767
8048643: 57 push %edi
8048644: e8 c7 fd ff ff call 8048410 <read@plt>
#read(file discriptor(6),0xbfa20767 or whatever(buffer),size(1 byte))
#number of bytes read is returned, 0 indicates eof

#eax is now the size of the number of bytes returned
#the value read is stored in (hopefully 0xbfdcd6a7)

8048649: 83 c4 10 add $0x10,%esp
804864c: 48 dec %eax
#numbytes read–, it should probably be zero at this point, and ZF is set

#if the file was empty or an eof marker was received continue
#else je is true, so jump on up
804864d: 0f 84 12 ff ff ff je 8048565 <main+0x91>

8048653: 8b 95 d8 ef ff ff mov 0xffffefd8(%ebp),%edx
8048659: 8b 8d d4 ef ff ff mov 0xffffefd4(%ebp),%ecx
804865f: 8b 02 mov (%edx),%eax
8048661: 31 d2 xor %edx,%edx
8048663: 80 3c 08 00 cmpb $0x0,(%eax,%ecx,1)
8048667: 75 19 jne 8048682 <main+0x1ae>
8048669: 83 ec 0c sub $0xc,%esp
804866c: 68 8a 87 04 08 push $0x804878a
8048671: e8 3a fd ff ff call 80483b0 <puts@plt>
8048676: 31 d2 xor %edx,%edx
8048678: 83 c4 10 add $0x10,%esp
804867b: eb 05 jmp 8048682 <main+0x1ae>
804867d: ba 02 00 00 00 mov $0x2,%edx

8048682: 8d 65 f0 lea 0xfffffff0(%ebp),%esp
8048685: 89 d0 mov %edx,%eax
8048687: 59 pop %ecx
8048688: 5b pop %ebx
8048689: 5e pop %esi
804868a: 5f pop %edi
804868b: c9 leave
804868c: 8d 61 fc lea 0xfffffffc(%ecx),%esp
804868f: c3 ret

After figuring out how it works, it really is easy to make a keygen. I wrote this in C++

#include <fstream>
#include <iostream>
#include <string>
using namespace std;

#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>

int main()
string username = getlogin();
string key = “”;

int esi = 0;
for(int i=0; i<username.length(); i++)
int num = 0;
num += esi;
for(;num < username[i]-20; num+=20){
key += “****+-“;}
for(;num < username[i]-5; num+=5){
key += “*”;}
for(;num < username[i]; num++){
key += “+”;}

key = key + “,” + username[i];

esi = username[i]/10;

string writefile = “/home/” + username + “/.key_” + username;
ofstream fout;;

return 0;


This was a lot of fun! I think I’ll try to do one of these from more often. A little hard though. This was rated at 1 which is the easiest, and I think 1 should be easier. cm1 definitely took me a long time to figure out. I’d say this is more like a 3 (the ranking is out of 10).

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: