理解 Windbg 的 `dds esp` 的输出

逆向工程 调试 风袋 调用栈
2021-07-01 09:59:37

我不确定我是否正确理解了原始输出dds esp或其 64 位对应物dqs rsp当我看到堆栈中的条目列表时,我倾向于假设无论我在哪里看到返回地址,都是由尚未返回的代码进行的调用。IOW,将它们串在一起应该形成一个很好的调用堆栈。(让我们暂时不要理会一k*组 Windbg 命令。)情况不总是这样吗?

因为有一些第三方扩展,它们对esp/rsp输出进行操作并将条目串在一起,形成看起来像调用堆栈的东西,但我似乎无法将该顺序与我在源中看到的匹配(好吧,无论源我有。)甚至还有很久以前返回的函数条目。

我错过了什么?

更新:

好的——我使用的第三方扩展确实说:

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

那么,问题就变成了什么该条目?我以为是某个函数的返回地址正在修复以调用另一个函数?

2个回答

堆栈可用于保存各种值,包括那些看起来像返回地址但实际上不是的值。如果省略了堆栈帧,那么在不分解函数以查看它如何存储诸如寄存器之类的保留值的情况下,就很难向后追溯。

dds 表示转储双字,将结果解释为符号

假设 0x401234 包含 0x77123456 并且 0x77123456 被解析为 kernel32!CreateFileA

dds 0x401234 将产生 kernel32!CreateFileA

如果你做 dds esp 它可以返回虚假符号,因为堆栈可以包含可能是一个常量的地址,该常量可能会解析为一个符号

编辑

dds/dqs/dps 用于查找解析为符号的地址,您可以将其用于堆栈寄存器 esp/rsp/va 来查找符号,请记住它可以返回虚假符号

例如,在解析导入表后,您可以查看使用 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

如果你在这里使用了 dd 它只是一堆 DWORDS

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

其他解引用命令包括 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)

假设你有这样的代码,如果你使用
vc++ cl /Zi /Od /EHsc /analyze /W4 dds.cpp /link /RELEASE 编译 并执行它

#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;
}

你会得到这样的结果

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

如果您在第 18 行设置断点并执行 dds,您可以看到 windbg 如何将 char** 解析为 module!symbol 符号

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

如果你做 dda esp 你可以看到字符串

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"

如果您碰巧在同一堆栈的 32 位和 64 位二进制文​​件上编译检查 dpp ddp 等