"Use of GoBug" demonstrations
Stack tests

These tests demonstrate how to use GoBug's stack panes to view data on the stack. For example you might want to view arguments on the stack or local data. It can be quite mindbending sometimes working out what is on the stack and where it is. Hopefully with GoBug it all makes sense.

Start Testbug as the debuggee and follow the instructions below.

There are 5 tests to choose from:-
   Passing arguments (EBP)
   Passing arguments (ESP)
   Using EBP as spare register
   Local data in stack frames
   Ways of saving return address

Passing arguments (EBP)
In the first test, 3 pieces of data are put on the stack before the call to RECEIVE_ARGSEBP, and then recovered back by that function.
Using GoBug's "action, run to .. procedure" menu item, set the breakpoint to STACK_TEST1 and single step down as far as the call to RECEIVE_ARGSEBP. Watch the value of ESP as you do each push and watch the ESP stack pane. See how ESP reduces in value by 4 bytes (one dword) each time you push, and see that the data is put on the stack at [ESP-4] so that after the push ESP points the data. Now trace into RECEIVE_ARGSEBP by pressing F5 and scroll the EBP stack pane until it shows the same data as showing in the ESP stack pane. Note that after the instruction equalising EBP and ESP, the date you pushed earlier can now be addressed by EBP plus 8h, 0Ch and +10h. Trace into into CALL TO_NOTHING while watching the stack panes and note how the return address of the call is put on the stack at [EBP-4]. In other words, further use of the stack by push and calls will not interfere with your data held on the stack. The value of EBP has not changed and it is still addressable and valid. An important part of this code is the RET 0Ch in RECEIVE_ARGSEBP which restores ESP to the correct value to suit the caller. Here is the source code:-

STACK_TEST1:
PUSH ADDR SPLOPFILE
PUSH 33333333h
PUSH [hWnd]
CALL RECEIVE_ARGSEBP
RET
;
RECEIVE_ARGSEBP:
PUSH EBP                ;receiving 3 parameters from caller
MOV EBP,ESP
;now [EBP+8]=3rd param, [EBP+0Ch]=2nd param, [EBP+10h]=1st param
CALL TO_NOTHING
MOV ESP,EBP
POP EBP
RET 0Ch
;
TO_NOTHING:
NOP
NOP
NOP
RET

Passing arguments (ESP)
This test is the same as the first, but you need to concentrate more on ESP. After the call to RECEIVE_ARGSESP the data on the stack is addressable by ESP +4h, +8h and +0Ch. After the call to TO_NOTHING the data moves to ESP +8h, +0Ch, and +10h. That is, ESP has moved, not the data. The point is, the data is much more difficult to keep track of using ESP. Again an important part of this code is the RET 0Ch in RECEIVE_ARGSESP which restores ESP to the correct value to suit the caller.

RECEIVE_ARGSESP:
;now [ESP+4]=3rd param, [ESP+8]=2nd param, [ESP+0Ch]=1st param
CALL TO_NOTHING
RET 0Ch
;
STACK_TEST2:
PUSH ADDR SPLOPFILE
PUSH 33333333h
PUSH [hWnd]
CALL RECEIVE_ARGSESP
RET

Using EBP as spare register
This simple test shows the EBP stack pane view when EBP is used as a spare register.
Set breakpoint to STACK_TEST3 and single-step down this code. See how the EBP stack pane changes values as EBP changes. In this code the EBP register is of course carefully saved and restored before its value is changed. Note when viewing the EBP stack pane when EBP does not point to the stack area, the contents of the stack pane is not to be relied on!

STACK_TEST3:
PUSH EBP
MOV EBP,200h
MOV EBP,2000h
MOV EBP,666666h
MOV EBP,0FFFFFFFFh
POP EBP
RET

Local data in stack frames
These two tests demonstrates how a stack frame can be made to hold local data on the stack, it shows how much easier it is to use EBP to address such data, and it also helps to shows the inter-action between GoBug's ESP and EBP stack panes. The first test uses ESP to address the data, and the second uses EBP.
Set a breakpoint at STACK_TEST4 and go to the test. Make a note of the eip at that address. Press F5 to trace into ST41. Now at this point the return address is held on the stack at [ESP]. Scroll the EBP pane a little until you get to the same stack address (you will see a line comes into view - this is at the current value of ESP). Now press F5 so that the instruction SUB ESP,64 (or SUB ESP,40 in hex as shown in the codepane) is carried out. Note that the ESP pane changes to the current position of ESP which is now higher up the stack. The EBP stack pane has not changed and is still showing the return address noted earlier. ESP now points to the top of the stack frame which will hold local data and the REP STOSB instruction fills this with zeroes. Watch as the zeroes reach the return address in the EBP stack pane but do not rub it out (just!). Now we are going to put some local data on the stack with the instruction MOV D[ESP+8],22222222h. Have a look at that in the ESP stack pane. Now comes the interesting part. Press F5 again to call GET_LOCDATA1. See that the return address of that call is even higher up the stack. The local data is not written over. However, ESP is now a lower value than before (higher up the stack) and in order to recover the data you need MOV EAX,[ESP+0Ch] and not MOV EAX,[ESP+8h] which would read from the wrong place. Finally after the return, ESP is restored to the correct stack position with ADD ESP,64 (ADD ESP,40 in hex) and the call to ST51 is finished.
Now press F5 again to trace into ST52. This time ESP is given to EBP in the traditional way then the stack frame is made. Scroll back the EBP pane as before to show the top of the stack frame. This time the data is inserted at [EBP-38h], but this time since EBP does not change it can still be addressed at [EBP-38h] even in the call to GET_LOCALDATA2.
I also show here that using eax to address the stack also works perfectly well. You don't have to use EBP, however it is traditional to do so, mainly because in 16-bit assembler [ESP] and [EBP] always read from the stack segment, whereas to use other register would require the SS segment override. In 32-bits this is not necessary, but it is a good idea to stick to the tradition since it helps to make your code more understandable to others.
Therefore it can be seen that by addressing all local data using EBP you are less likely to make errors arising from the changes to ESP.
You can make any number of stack frames you like subject to memory. For example there could be another stack frame made in GET_LOCALDATA2. If you did that and addressed [EBP-38h] you would be addressing the 2nd stack frame and not the 1st one. It's local data after all.

STACK_TEST4:
CALL ST41
CALL ST42
RET
;
ST41:
SUB ESP,64              ;make space for 16 dwords
MOV EDI,ESP
MOV ECX,64 
XOR EAX,EAX
REP STOSB               ;fill stack space with zeroes
MOV D[ESP+8],22222222h  ;insert some data locally
CALL GET_LOCADATA1
ADD ESP,64
RET
;
GET_LOCADATA1:
MOV EAX,[ESP+0Ch]       ;get local data back
RET
;
ST42:
PUSH EBP
MOV EBP,ESP
SUB ESP,64              ;make space for 16 dwords
MOV EDI,ESP
MOV ECX,64 
XOR EAX,EAX
REP STOSB               ;fill stack space with zeroes
MOV D[EBP-38h],22222222h
CALL GET_LOCADATA2
MOV ESP,EBP
POP EBP
RET
;
GET_LOCADATA2:
MOV EAX,[EBP-38h]       ;get local data back
MOV EAX,EBP             ;ready to use eax this time
MOV EAX,[EAX-38h]       ;get local data back
RET

Ways of saving return address
These four tests demonstrate various ways of saving and restoring the return address either side of a stack frame made by reducing the value of ESP by the number of dwords required.
In each case ESP is itself restored by using RET with the correct number of bytes to suit the size of the stack frame.
In ST51 the return address is moved to the top of the stack frame before the RET which is rather neat! In ST52 the return address is kept in a register, but you have to make sure the value in the register is not written over.
ST54 is a variation on ST51 and can be used in cases where eax has to be kept and restored.

In ST53 the value is kept in the user pointer in the Thread Environment Block (TEB) which is a thread-specific area accessible using the FS segment override. In Windows NT, 2000 and XP, if you use the FS segment register to address memory, the system assumes you are intending to address the TEB. In Windows 9x and ME, the value of FS changed from thread to thread because it held a 16-bit selector value. This does not happen in later Windows versions and FS remains the same value. Useful TEB values are:-
FS:[0] = exception list
FS:[4] = StackBase
FS:[8] = StackLimit
FS:[14h] = a dword for private thread use
FS:[18h] = linear address of the TEB
In ST53 use is made of FS [14h] which is a unique dword for each thread.

STACK_TEST5:
CALL ST51
CALL ST52
CALL ST53
CALL ST54
RET
;
ST51:
MOV EAX,[ESP]
SUB ESP,64              ;make space for 16 dwords
MOV [ESP],EAX           ;move return address
RET 40h
;
ST52:
POP EAX                 ;save caller in eax
SUB ESP,64              ;make space for 16 dwords
PUSH EAX                ;put caller back
RET 40h
;
ST53:
FS POP [14h]            ;put caller into user pointer
SUB ESP,64              ;make space for 16 dwords
FS PUSH [14h]           ;put caller back
RET 40h
;
ST54:
XCHG EAX,[ESP]          ;swap caller with eax
SUB ESP,64              ;make space for 16 dwords
MOV [ESP],EAX           ;put caller back on stack
MOV EAX,[ESP+64]        ;restore eax
RET 40h