Uninformed: Informative Information for the Uninformed

Vol 1» 2005.May

Stacks and Frames

The 'u' command instructs WinDBG to (u)nassemble, or translate from opcodes to mnemonics with operands, the information found at the specified address.

01002fd8 e851f7ffff     call    WinMine!DisplayGrid (0100272e)
01002fdd c20400         ret     0x4
From this, one can see that the DisplayGrid function is called and the ShowBombs function subsequently returns to the caller. But what is call actually doing? Can one tell where ret is really returning to and what does the 0x4 represent? The IA-32 Command Reference states that call "Saves procedure linking information on the stack and branches to the procedure (called procedure) specified with the destination (target) operand. The target operand specifies the address of the first instruction in the called procedure." The reader may notice that the command reference has variable behaviors for the call instruction depending on the type of call being made. To further identify what call is doing, the reader can examine the opcodes, as previously discussed, and find 0xe8. 0xe8, represents a near call. "When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) onto the stack (for use later as a return-instruction pointer)." This is the first step in building a frame. Each time a function call is made, another frame is created so that the called function can access arguments, create local variables, and provide a mechanism to return to calling function. The composition of the frame is dependant on the function calling convention. For more information on calling conventions, the reader is encouraged to read the relevant documents in the reference section[1]. To view the current call stack, or series of linked frames, use the 'k' command.
ChildEBP RetAddr
0006fd34 010034b0 winmine!ShowBombs
0006fd40 010035b0 winmine!GameOver+0x34
0006fd58 010038b6 winmine!StepSquare+0x9e
0006fd84 77d43b1f winmine!DoButton1Up+0xd5
0006fdb8 77d43a50 USER32!UserCallWinProcCheckWow+0x150
0006fde4 77d43b1f USER32!InternalCallWinProc+0x1b
0006fe4c 77d43d79 USER32!UserCallWinProcCheckWow+0x150
0006feac 77d43ddf USER32!DispatchMessageWorker+0x306
0006feb8 010023a4 USER32!DispatchMessageW+0xb
0006ff1c 01003f95 winmine!WinMain+0x1b4
0006ffc0 77e814c7 winmine!WinMainCRTStartup+0x174
0006fff0 00000000 kernel32!BaseProcessStart+0x23
With this, the reader can track the application flow in reverse order. The reader may find it easier to navigate the call stack by pressing Alt-6, which will bring up the Call Stack window. Here, the call stack information is digested a bit more and displayed in a tabular manner. With the call stack information, one can answer the second question regarding where ret is really headed. For example, once ShowBombs returns, eip will be set to 0x010034b0. Finally, the significance of 0x4 can be learned by reading the ret instruction definition, which states that this value represents "...the number of stack bytes to be released after the return address is popped; the default is none. This operand can be used to release parameters from the stack that were passed to the called procedure and are no longer needed." More specifically, when the processor encounters a ret instruction it pops the address stored at the top of the stack, where esp is pointing, and places that value in the eip register. If the ret instruction has an operand, that operand represents how many additional bytes should be removed from the top of the stack. With this information, and knowing that 32-bit addresses are four bytes long, one can determine that the ShowBombs function accepts one argument5.3.

Returning to the task at hand, the following represents the current picture of what the ShowBombs function is doing:

if(iGridHeight < 1) {
} else {
        //do stuff
Continuing on, one can see the following in the Disassembly window, which will take the place of "//do stuff".
01002f8a 53               push    ebx
01002f8b 56               push    esi
01002f8c 8b3534530001     mov     esi,[winmine!xBoxMac (01005334)]
01002f92 57               push    edi
01002f93 bf60530001       mov     edi,0x1005360
Begin by stepping WinDBG twice (by pressing 'p' twice) so that eip is set to 0x1002f8a. The next two instructions are storing the ebx and esi registers on the stack. This can be demonstrated by first viewing the memory referenced by esp, identifying the value stored in ebx, pressing 'p' to execute push ebx, and revisiting the value stored at esp. The reader will find the value of ebx stored at esp.
0:000> dd esp
0006fd38  010034b0 0000000a 00000002 010035b0
0006fd48  00000000 00000000 00000200 0006fdb8
0:000> r ebx
0:000> p
eax=00000018 ebx=00000001 ecx=0006fd14 edx=7ffe0304 esi=00000000 edi=00000000
eip=01002f8b esp=0006fd34 ebp=0006fdb8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000206
01002f8b 56               push    esi
0:000> dd esp
0006fd34  00000001 010034b0 0000000a 00000002
0006fd44  010035b0 00000000 00000000 00000200
Notice, that esp has been decremented by four (the size of a 32-bit pointer) and the value of ebx is at that location. The behavior can be observed again by stepping to execute push esi. Again, the reader will notice the value of esp decrement by four and the value within esi is at this new location. This is the basic principal of how the stack works. The stack pointer is decremented and the value being push'd onto the stack is placed at the new address esp is pointing at. It is also important to note that the stack grows down. That is, as values are placed on the stack, the stack pointer decreases to make room. Which begs the question, what are the upper and lower limits of the stack? It can't keep on growing for ever, can it? The short answer is no, the stack has a floor and ceiling. Which can be identified by examining the Thread Environment Block or TEB. Luckily, WinDBG comes with an extension command to accomplish this, !teb.
0:000> !teb
TEB at 7ffde000
    ExceptionList:        0006fe3c
    StackBase:            00070000
    StackLimit:           0006c000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7ffde000
    EnvironmentPointer:   00000000
    ClientId:             00000ff4 . 00000ff8
    RpcHandle:            00000000
    Tls Storage:          00000000
    PEB Address:          7ffdf000
    LastErrorValue:       183
    LastStatusValue:      c0000008
    Count Owned Locks:    0
    HardErrorMode:        0
Note the values for StackBase and StackLimit, which refer to the stack's ceiling and floor, respectively. For more information on the TEB, the reader is encouraged to read the related documents in the reference section [11]. That was an exciting tangent. Circling back, the reader is found at the following instruction:
01002f8c 8b3534530001     mov     esi,[winmine!xBoxMac (01005334)]
This, if convention holds true, will store the width of the playing grid in esi. By single stepping ('p'), the reader will notice the esi register is denoted in red within the Registers window and now contains the value 0x1e. 0x1e is 30 in decimal, which, if the reader recalls, is the width of the current playing grid. Hence, one can make the educated determination that xBoxMac represents the width of the playing grid. The next instruction, push edi, is saving the value in the edi register on the stack in preparation for the subsequent instruction; mov edi,0x1005360. This is were things get a bit more interesting, as this instruction begs the question; what is the significance of 0x1005360? Considering the previous instructions gathered prerequisite information about the playing grid, perhaps this address is indeed the playing grid itself! To determine this, the reader should examine some aspects of this memory address. The aforementioned !vprot extension command will provide information regarding the type of access permitted to this memory address, which is PAGE_READWRITE. This information isn't overly valuable but is favorable in the sense that this address does not reside within an executable portion of the application space and is therefore likely a variable allocation. If this area is truly the playing grid one should be able to identify a pattern while viewing the memory. To accomplish this, type 0x1005360 in to the Memory window. The following should appear:
01005360 10 42 cc 8f 8f 8f 8f 0f 8f 8f 8f 8f 0f 0f 8f 0f  .B..............
01005370 0f 8f 8f 8f 8f 8f 8f 0f 0f 0f 8f 0f 0f 8f 8f 10  ................
01005380 10 8f 0f 0f 8f 8f 0f 0f 0f 0f 0f 8f 0f 0f 8f 8f  ................
01005390 0f 8f 0f 0f 0f 8f 8f 8f 0f 0f 8f 8f 8f 8f 8f 10  ................
010053a0 10 0f 0f 8f 0f 0f 8f 0f 0f 0f 0f 0f 8f 0f 0f 0f  ................
010053b0 8f 0f 0f 0f 8f 8f 0f 0f 8f 0f 8f 0f 8f 8f 0f 10  ................
010053c0 10 0f 0f 8f 0f 0f 8f 0f 0f 0f 8f 0f 0f 8f 0f 0f  ................
010053d0 8f 0f 0f 8f 0f 0f 0f 8f 0f 0f 0f 8f 0f 0f 0f 10  ................
010053e0 10 0f 0f 8f 0f 8f 8f 0f 0f 8f 8f 0f 0f 8f 0f 0f  ................
010053f0 0f 0f 0f 0f 8f 0f 0f 0f 0f 0f 0f 0f 8f 0f 0f 10  ................
01005400 10 8f 0f 0f 0f 0f 0f 0f 8f 8f 0f 8f 8f 0f 0f 8f  ................
01005410 0f 8f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 8f 8f 0f 10  ................
01005420 10 8f 0f 8f 8f 0f 8f 8f 0f 0f 0f 8f 8f 0f 8f 0f  ................