0

I am not sure if I am understanding the raw output of dds esp or its 64-bit counterpart dqs rsp properly. When I see a list of entries in the stack, I tend to assume that wherever I see return addresses, those are calls made by code that have not returned yet. IOW, stringing them together should form a nice call stack. (let's not bother with k* group of Windbg commands for now.) Is that not the case always?

Because there are some third party extensions, that operate on the esp/rsp output and strings together the entries into something that appear to look like a call stack but I can't seem to match that order with what I see in the source (well, whatever source I have.) There are even entries of functions that have returned long ago.

What am I missing?

UPDATE:

OK -- the third party extension I use does say:

Dumps (dps) from the stack limit the base only showing items that include the ! followed by +0x

So, the question then becomes what is that entry? I thought it was the return address of some function that is fixing to make a call into another function?

  • i think this may be somewhat difficult to answer without some concrete examples. remember that we can't see your screen and don't know what "proper" stack you expected to see. – Igor Skochinsky Oct 6 '17 at 22:54
  • @IgorSkochinsky blabb answered my confusion below. Basically I wasn't sure if the addresses resolving to a symbol pointing to a Win32 API were real calls to that function with their return addresses pushed on to the stack or just bogus. I guess there is no way to tell? – ForeverLearning Oct 9 '17 at 13:55
  • @ForeverLearning you can tell (how does k in windbg or bt in gdb tells it there are heuristics to tell and if symbols are correct the results will be almost always correct and in x64 the unwind information lets anyone decipher the stack correctly) only caveat is you should know that doing dds esp and blindly assuming every symbol is a return address is wrong you need to validate every symbol and discard possible bogus symbols – blabb Oct 9 '17 at 20:31
2

dds means dump dwords intrepreting the result as symbols

suppose 0x401234 contains 0x77123456 and 0x77123456 is resolved as kernel32!CreateFileA

dds 0x401234 will yield kernel32!CreateFileA

if you do dds esp it can return bogus symbols as stack can contain address that may be a constant which might resolve to a symbol

edit

dds/dqs/dps are meant to be used to look for addresses that resolve to symbol you can use it against stack register esp/rsp/va to look for symbols only keep in mind it can return bogus symbols

for example after import table is resolved you can look what imports were resolved using dps /dds

0:000> dds calc+1000 l6;dps calc+1000 l6
00461000  760b0468 SHELL32!SHGetSpecialFolderPathW
00461004  76115708 SHELL32!SHGetFolderPathW
00461008  7615a129 SHELL32!ShellAboutW
0046100c  7619dd83 SHELL32!SHCreateDirectory
00461010  760b1e46 SHELL32!ShellExecuteExW
00461014  00000000
00461000  760b0468 SHELL32!SHGetSpecialFolderPathW
00461004  76115708 SHELL32!SHGetFolderPathW
00461008  7615a129 SHELL32!ShellAboutW
0046100c  7619dd83 SHELL32!SHCreateDirectory
00461010  760b1e46 SHELL32!ShellExecuteExW
00461014  00000000

if you had used dd here it would be just a bunch of DWORDS

0:000> dd calc+1000 
00461000  760b0468 76115708 7615a129 7619dd83
00461010  760b1e46 00000000 

other dereferncing commands inlude dda / ddu / ddp / dpp

dda derefences an ascii string 
ddu derefernces an unicode string
ddp dereferences  a pointer (only 4 butes or a dword
dpp dereferences a pointer ( either 4 or 8 bytes based on arch)

suppose you have code like this if you compile with using
vc++ cl /Zi /Od /EHsc /analyze /W4 dds.cpp /link /RELEASE and execute it

#include <stdio.h>
#include <stdlib.h>
char *azz = "forever";
char *bzz = "learning";
char *czz = "for";
char *dzz = "ever";
char *ezz = "learn";
char *fzz = "ing";
char *gzz = "for";
char *hzz = "eve";
char *f[] = {azz,bzz,czz,dzz,ezz,fzz,gzz,hzz};
int main () {

    char **moo[] = { &f[0],&f[1],&f[2],&f[3],&f[4],&f[5],&f[6],&f[7] };
    char *meow[] = {  f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7] };
    for(int i =0;i <_countof(f);i++)
    {
        printf("%p %10s\n" ,moo[i],meow[i]);
    }
    return 0;
}

you will get a result like this

012158A0    forever
012158A4   learning
012158A8        for
012158AC       ever
012158B0      learn
012158B4        ing
012158B8        for
012158BC        eve

if you set a breakpoint on line 18 and do dds you can see how windbg resolves the char** to module!symbol notation

windbg -c "bp `dds!dds.cpp:18`;g" dds.exe


0:000> bl
     0 e Disable Clear  013910d0  [c:\dds.cpp @ 18]     0001 (0001)  0:**** dds!main+0x70

0:000> .lastevent
Last event: 808.1b8: Hit breakpoint 0      
0:000> rM0
dds!main+0x70:
013910d0 ff743430        push    dword ptr [esp+esi+30h] 
ss:0023:002cfa50=013cb1a0

0:000> dds esp l14    
002cfa20  013d5678 dds!__argc
002cfa24  013d40f0 dds!_iob+0x90
002cfa28  00000fa0
002cfa2c  00000000
002cfa30  013d48a0 dds!f   <---------
002cfa34  013d48a4 dds!f+0x4 <------
002cfa38  013d48a8 dds!f+0x8 <------
002cfa3c  013d48ac dds!f+0xc <-----
002cfa40  013d48b0 dds!f+0x10 <-------
002cfa44  013d48b4 dds!f+0x14 <--------
002cfa48  013d48b8 dds!f+0x18 <---------
002cfa4c  013d48bc dds!f+0x1c <---------
002cfa50  013cb1a0 dds!__xt_z+0x4
002cfa54  013cb1a8 dds!__xt_z+0xc
002cfa58  013cb1b4 dds!__xt_z+0x18
002cfa5c  013cb1b8 dds!__xt_z+0x1c
002cfa60  013cb1c0 dds!__xt_z+0x24
002cfa64  013cb1c8 dds!__xt_z+0x2c
002cfa68  013cb1cc dds!__xt_z+0x30
002cfa6c  013cb1d0 dds!__xt_z+0x34

if you do dda esp you can see the strings

0:000> dda esp l14
002cfa20  013d5678 "."
002cfa24  013d40f0 "..."
002cfa28  00000fa0
002cfa2c  00000000
002cfa30  013d48a0 
002cfa34  013d48a4 
002cfa38  013d48a8 
002cfa3c  013d48ac 
002cfa40  013d48b0 
002cfa44  013d48b4 
002cfa48  013d48b8 
002cfa4c  013d48bc 
002cfa50  013cb1a0 "forever"  <---------------
002cfa54  013cb1a8 "learning" <-----------
002cfa58  013cb1b4 "for" <--------------
002cfa5c  013cb1b8 "ever"
002cfa60  013cb1c0 "learn"
002cfa64  013cb1c8 "ing"
002cfa68  013cb1cc "for"
002cfa6c  013cb1d0 "eve"

if you happen to compile check dpp ddp etc on both 32 bit and 64 bit binary for the same stack

| improve this answer | |
  • Thanks! I understand now. Given this, in what circumstances, do you normally use dds esp? – ForeverLearning Oct 9 '17 at 13:56
  • @ForeverLearning i edited in further info take a look – blabb Oct 9 '17 at 19:51
3

The stack can be used to hold all kinds of values, including ones that look like return addresses but aren't. If stack-frames are omitted, then it becomes very difficult to trace backwards without disassembling the function to see how it stores preserved values such as registers.

| improve this answer | |
  • OK. So some of these only look like return addresses. Let's assume FP isn't omitted, then what exactly are these fake return address entries? – ForeverLearning Oct 6 '17 at 16:47
  • they might be pointers to data within the calling DLL, for example. If there are code and data mixed together, or data at the front of a section which is common in some Microsoft DLLs, then the result will be almost indistinguishable from each other. – peter ferrie Oct 14 '17 at 1:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.