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 : https://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 :