lundi 4 novembre 2013

Reverse Engineering ELF

                                             Reverse Engineering ELF
                                                         by Void0xCC

Hi everyone,

A while ago I came across root-me challenges , and I started reversing a 'crackme' that looks interesting.

You can download the 'crackme' from root-me , or you can find it in the attachment.

So lets get started,
First I run the binary to get a first look of his functionality :


The program print “Welcome to Root-me Challenges” and ask for password, I type “system” it print “Try again!” as Bad boy Message

While reading the title “ELF - No software breakpoints” I assume who ever code this binary has put an anti-breakpoint technique .

I find some informations about the binary by using command “file ch20.bin”


The file is “Stripped” , so after loading it into GDB we get “no debugging symbols found” in other words there is no informations about functions , variables...etc
In order to get around this I used “info file” to get the Entry point


as you can see we manage to get the entry point “0x8048080”. Next we put a breakpoint on the Entry point and launch the binary:


after this we examine the functionality in deep:




Basically this is the way we pass arguments to sys_write system call
mov eax, 0x4                    ----> 0x4 is the system call number (sys_write)

mov ecx, 0x80491a1        ----> 0x80491a1 address of the string that gets printed

int 0x80                            ----> call kernel

Taking a look at 0x80491a1, we find the first string that gets printed while running the program


Next

mov eax, 0x3                   ----> 0x3 system call number (sys_read)

mov ecx, 0x8049188       ----> 0x8049188 address where the string gets stocked

as you may already understand this is where our password gets stocked

Next there is this sequence of instructions


xor ecx, ecx                       ----> make ECX = 0x00000000

mov eax, 0x8048080         ----> if you remember this is the entry point (beginning of .text section)

mov ebx, 0x8048123        ----> this is the end of .text section

call 0x8048115                  ----> execution goes toward 0x8048115 lets see what is there


sub ebx, eax                      ----> make EBX=0xa3 = 163 decimal ,the length of .text section.
 
loop:
add cl, BYTE PTR [eax]  ----> remember EAX point to the beginning of .text section so it take the                                                 first byte and put it on ECX register.

rol ecx, 0x3                      ----> rotate ECX register to left by 0x3.

inc eax                             ----> its clear increment EAX

dec ebx                            ----> decrement EBX

jne 0x8048119 (loop)       ----> if EBX != 0x00000000 go to loop (loop )

lets explain the big picture here, this code is used to check for breakpoints. if we put a breakpoint on any instruction between 0x8048080 and 0x8048123 (.text section) the opcode of that instruction will be changed to 0xCC , and when we finish this loop we wont get the right ECX.
Remember this ECX 4 bytes is crucial in password validation routine.
Next we get to the password validation routine:


as you see the ECX we spoke about get moved into EDX.

mov ecx, 0x19           ----> its self explanatory but we see also “dec ecx” at the end so I assume this                                          is the length of the password, so length(password) = 25.

mov eax, 0x8049155 ----> copy address into EAX, lets see whats there.

by printing the first 25 bytes in hexadecimal format we get:


keep this 25 bytes in mind we will use it later

mov ebx, 0x8049188          ----> this is sere the password is stocked.

ror edx, 1                             ----> it just a rotation to right by 1 , remember EDX contain the 4 bytes                                                    gotten from breakpoint check routine.

This instructions are very important so lets see what they do :


mov al, BYTE PTR [eax+ecx*1-0x1]  -----> in this instruction we have:
                                                               EAX= 0x8049155
                                                               ECX=0x19 = 25 decimal
                                                               so “BYTE PTR [eax+ecx*1-0x1]” will point to
                                                              the 25th byte from EAX witch is 0xe0 
                                                              (remember  the 25  bytes we just saw).

mov bl, BYTE PTR [ebx+ecx*1-0x1]    -----> the same thing only takes the 25 byte of password (last                                                                   character) put it in EBX.

xor al, bl                                            ------> xor the 25th byte “0xe0” with the 25th byte of                                                                                  password store result in AL.

xor al,dl                                             ------> xor AL with the the first byte of EDX.

jne 0x80480f6                                   ------> if (al != dl) jump to “0x80480f6” lets go there


its a sys_write call with the message stored in “0x804914a” lets see what is the message


its the Bad Boy Message.

And if (AL == DL) we get the 24th elements and repeat the routine until we finish all 25 bytes and we will get to


witch print


lets summarize, in order to validate this challenge we need to get the right 4 bytes stored in ECX in other words we will not put any breakpoint and get this value, How to do that ????

I'm sure there is a lot of answers but This is mine:

First I extract the opcode of .text section:
using the following command :

“for i in $(objdump -d ch20.bin -M intel |grep "^ " |cut -f2); do echo -n '0x'$i','; done;echo”


and i rotate every byte of this opcode by 0x3 and store result in ECX.

In the other hand , the password get verified by this equation:
(password[25] XOR sting[25]) == ROL EDX, 0x1
than: password[25] = string[25] XOR (ROL EDX, 0x1)

now we have all the ingredient I wrote a C code to do all that in the attachment
 #include <stdio.h>  
 int main(int argc, char const *argv[])  
 {  
      /* code */  
      int i,j;  
   unsigned int out = 0x00000000;  
   unsigned int tmp;  
   char password[25] = "";  
      unsigned int bytes[25] = {  
           0x1e,0xcd,0x2a,0xd5,0x34,0x87,0xfc,0x78,      
     0x64,0x35,0x9d,0xec,0xde,0x15,0xac,0x97,  
     0x99,0xaf,0x96,0xda,0x79,0x26,0x4f,0x32,  
     0xe0  
      };  
      unsigned char opcode[163] = {  
           0xb8,0x04,0x00,0x00,0x00,0xbb,0x01,0x00,0x00,0x00,        
           0xb9,0xa1,0x91,0x04,0x08,0xba,0x26,0x00,0x00,0x00,  
           0xcd,0x80,0xb8,0x03,0x00,0x00,0x00,0x31,0xdb,0xb9,  
           0x88,0x91,0x04,0x08,0xba,0x33,0x00,0x00,0x00,0xcd,  
           0x80,0x31,0xc9,0xb8,0x80,0x80,0x04,0x08,0xbb,0x23,  
           0x81,0x04,0x08,0xe8,0x5b,0x00,0x00,0x00,0x89,0xca,  
           0xb9,0x19,0x00,0x00,0x00,0xb8,0x55,0x91,0x04,0x08,  
           0xbb,0x88,0x91,0x04,0x08,0xd1,0xca,0x8a,0x44,0x08,  
           0xff,0x8a,0x5c,0x0b,0xff,0x30,0xd8,0x30,0xd0,0x75,  
           0x1b,0x49,0x75,0xe3,0xb8,0x04,0x00,0x00,0x00,0xbb,  
           0x01,0x00,0x00,0x00,0xb9,0x24,0x91,0x04,0x08,0xba,  
           0x26,0x00,0x00,0x00,0xcd,0x80,0xeb,0x16,0xb8,0x04,  
           0x00,0x00,0x00,0xbb,0x01,0x00,0x00,0x00,0xb9,0x4a,  
           0x91,0x04,0x08,0xba,0x0b,0x00,0x00,0x00,0xcd,0x80,  
           0xb8,0x01,0x00,0x00,0x00,0x31,0xdb,0xcd,0x80,0x29,  
           0xc3,0x31,0xc9,0x02,0x08,0xc1,0xc1,0x03,0x40,0x4b,  
           0x75,0xf7,0xc3  
      };  
      printf("#########################################\n");  
      printf("--------KeyGen Coded By Void0xCC---------\n");  
      printf("------------Root-Me Challenge------------\n");  
      printf("#########################################\n");  
      for (j = 0; j < 163; j++)          
      {  
           __asm__(  
                "mov ecx, %2;"  
                "add cl, %1;"          // function to get the right ECX   
                "rol ecx, 0x3;"  
                "mov %0, ecx;"  
                :"=r" (tmp)  
                :"r" (opcode[j]), "r" (out)  
                );  
           out = tmp;  
      }  
      printf("XOR hexadecimal ---> 0x%x\n", tmp);  
      printf("Key Generating ... 99.8%% \n");  
      sleep(2);  
      for(i=0; i < 25 ; i++)  
      {  
           __asm__(  
           "mov ebx, %1;"  
           "ror ebx, 0x1;"          //get the password by solving the equation :  
           "mov %1, ebx;"          //password[25] = string[25] XOR (ROL EDX, 0x1)  
           "mov %0, ebx;"  
           : "=r" (out)  
           : "r" (tmp)  
           );  
      tmp = out;  
      printf("%x  XOR  %x -----> %c\n", out, out ^ bytes[24 - i], out ^ bytes[24 - i]);  
      password[24 - i] = out ^ bytes[24 - i];  
      }  
      printf("Owned \n");  
      printf("Serial -----> %s\n", password);  
      printf("\n");  
      return 0;  
 }  


after the executing C program :

2 commentaires: