Debug Fundamentals #4 : Spoiler


We’re posting the answers to Debug Fundamentals #4 in this blog. Additionally we posted all of your answers that trickled into the original blog. We deferred the posting of your answers to prevent spoiling it for the others but you should find them up there now. Stay tuned for Debug Fundamentals #5!


 


 


Part One – Debugging


 


1.       What caused the access violation?


Code tried to execute with an invalid EIP address.


0:000> !address @eip


 ProcessParameters 00280f20 in range 00280000 00284000


 Environment 00280808 in range 00280000 00284000


    00ac5000 : 00ac5000 – 7513b000


                    Type     00000000


                    Protect  00000001 PAGE_NOACCESS


                    State    00010000 MEM_FREE


                    Usage    RegionUsageFree


 


2.       Examine the registers at the time of the crash.  Is there anything interesting about the contents?


There are ASCII values in 3 of the registers.


0:000> .formats @eax


  Chars:   SEGP  (little endian would be PGES)


0:000> .formats @eip


Evaluate expression:


  Chars:   GAME


0:000> .formats @ebp


  Chars:   OVER


 


3.       How did the registers get into this state?


The saved EIP and EBP values on the stack were corrupt and the boom() function executed a leave and ret instruction, loading the corrupt values into the registers.


4.       Find the offending data structure which caused this state to occur. What are its contents?


“GAMEOVER Happy debugging courtesy of Platforms GES!” backwards


0:000> dc @esp-38 l34/4


001bfdc8  45532180 6d732047 74666f72 20506c61  .!SEG smroftalP


001bfdd8  79206f66 72746573 20636f75 67696e67  fo ysetruoc gnig


001bfde8  65627567 70792064 20486170 4f564552  gubed yppaH REVO


001bfdf8  47414d45                             EMAG



 


5.       Are there security concerns with this access violation? Why?


A malicious user could potentially manipulate EIP to execute arbitrary code and compromise the system.


6.        Why is this class of crashes not seen in the wild much anymore?


This exercise was explicitly compiled with the /GS- flag.  The Microsoft compiler appropriately enables buffer overrun security checks by default using the /GS compiler switch. This is a randomized “cookie” value placed on the stack during the function prolog right before the saved EIP and EBP. During the function epilog this value is asserted before executing the ret instruction. If it is not correct the program is terminated.


More info : http://msdn.microsoft.com/en-us/library/8dbf701c.aspx


Part Two – Reverse Engineering


 


Examine the functions main(), snap(), crack(), pop(), and boom().


1.       Describe with a sentence or two what each one is doing.  (There is no need to comment on every assembly instruction or re-write the code in C here unless you really feel you need to.)


main() – Allocates a buffer on the stack and makes a call to snap() passing it the buffer.


//


// Allocate a buffer on the stack


//


int


main()


{


      char buf[sizeof(string) + 13];           


      return snap ((char *)buf);


}


snap() – Does a strcpy() like operation from a global buffer into the buffer parameter and passes it to crack().


//


// Copy static string to stack


//


int


snap(char * buf)


{


      for (int i = 0; string[i]; i++)


      {


            buf[i] = string[i];


      }


     


      return crack (buf);


}


 


crack() – Does a string reverse operation on the buffer parameter then counts the number of characters before finding a special delimiting character (0xC3) and passes the buffer and character count to pop().


//


// Reverse the string


//


int


crack(char * buf)


{


      char * s = buf;


      char * p = s;


     


      while (*p) p++;


      p–;


 


      while (p > s)


      {


            char t = *s;


            *s++ = *p;


            *p– = t;


      }


 


      // strlen of the secret


      char * len = buf;


      unsigned i = 0;


           


      while (*len != ‘\xc3’)


      {


            len++;


            i++;


      }


     


      i++;


           


      return pop (buf, i);


}



 


pop() – Does a memmove() like operation removing the character count parameter from the start of the string buffer and passes it to boom().


//


// Do a memory move on the string, hiding the secret


//


int pop(char * buf, unsigned size)


{


      volatile char * a = buf;


      char * b = buf + size;


     


      while (*b)


            *a++ = *b++;


     


      *a = ‘\0’;


     


      return boom (buf);     


}


 


boom() – Allocates a stack buffer and does a strcpy() operation from the parameter into it. Takes each char of the new stack buffer and swaps the nibbles of each character so that 0y1101 1001 is 0y1001 1101. Then returns ‘SEGP’; or PGES as a DWORD.


//


// Copy string to our local buf, decrypt and boom!!!


//


int


boom(char * buf)


{


      char new_buf[sizeof(string) – 0x14];


      register unsigned int i;     


     


      for (i = 0; buf[i]; i++)


      {


            new_buf[i+1] = buf[i];


      }


     


            while (*buf)


            *buf ++ = *buf << 4 | *buf >> 4;


 


      return (int)’SEGP’;


}



Bonus:


 


1.       If this access violation occurred while the program was running without a debugger present, would there be anything different about the crashing register state or the data structure which caused it? If so what is it and why?


When a debugger is present, an unhandled exception is immediately trapped and the pristine state of the process can be examined. When a debugger is not present, an unhandled exception handler is executed in the context of the original exception before the JIT debug handling takes effect and a debugger is attached. Since the offending corruption was in stack memory which has been popped off the stack before the access violation occurred, the unhandled exception handler reused and overwrote the offending stack memory.


More Info on debugging and unhandled exception filters:


Debugging a custom unhandled exception filter  


Don’t perform complicated tasks in your unhandled exception filter


 


2.       The functions from “Part Two – Reverse Engineering” perform operations on a set of data. During this manipulation some data is lost. Find this data and decode its “secret” message.


                “<DBGNINJA>”


 














Share this post :

Comments (2)

  1. Thank you for submitting this cool story – Trackback from DotNetShoutout

  2. pulsoid says:

    Nice and fun!

    When there will be #5? 🙂

    Just a little note regarding the reversing of the function boom.

    By looking a the disassembly:

    .text:010011E8                 lea     eax, [ebp+new_buf]

    .text:010011EB                 jmp     short loc_1001204

    .text:010011ED ; —————————————————————————

    .text:010011ED

    .text:010011ED loc_10011ED:                            ; CODE XREF: boom(char *)+41j

    .text:010011ED                 mov     cl, [eax]

    .text:010011EF                 mov     dl, [eax]

    It seems it should be more:

    while(*new_buf)

    *new_buf++ = *new_buf >> 4 | *new_buf << 4;

    rather than:

               while (*buf)

               *buf ++ = *buf << 4 | *buf >> 4;

     <DIV class=commentowner>[Very Soon:) ^ronsto.]</DIV>