使用windbg观察栈

7 篇文章 0 订阅
5 篇文章 0 订阅

清明时节没出门,在B站上发现一个windbg教程,学习了堆栈一段

https://www.bilibili.com/video/BV1j5411P7Tp?t=150

1.B站内容

Introduction to Windbg Series 1 Part 8 - Commands k for callstack or stackback
Basic commands for windbg - k
Concept of Callstack
-Callstack is the most important information for any kind of debugging.
-Stack of functions.  -- 栈属于函数
-A callstack is there for each thread in the OS. -- 每个线程都有
-k is the command for callstack
-Many variants - kb,kvn,kM   --几个常用变种
-Take 3 inputs optional which is esp,eip and ebp which is useful in some case like stack corruption. ---和参数相关的几个重要寄存器
-Correct symbols are needed to show the stack correctly. --符号很重要
-Works in both kernel mode and user mode.
然后是一个DEMO,kCmd演示:

2 什么是栈

为加强理解,又拿出张银奎老师的巨著《软件调试》读了第22章节 栈和函数调用

什么是栈?

从数据结构角度,栈是用来存储数据的容器,放入数据称为push,取数据称为pop,存取数据的一条基本规则是后进先出LIFO

对基于栈的计算机系统而言,栈是存储局部变量和进行函数调用所比不可少的连续内存区域。x86系统,栈是朝低地址生长的。

ESP指向栈顶

一个普通的用户线程都有两个栈:内核态栈和用户态栈

挖雷小程序演示,需要使用windbg 6.12  x86,用windbg 10.x x86不行,找不到IP counter
open winmine.exe
ntdll!LdrpDoDebuggerBreak+0x2c:
772a10a6 cc              int     3
进入第一个断点
0:000> k
ChildEBP RetAddr  
000cfb34 77280ff3 ntdll!LdrpDoDebuggerBreak+0x2c
000cfcb0 77249f31 ntdll!LdrpInitializeProcess+0x12ce
000cfd00 77239799 ntdll!_LdrpInitialize+0x78
000cfd10 00000000 ntdll!LdrInitializeThunk+0x10
0:000> !teb
TEB at 7efdd000
    ExceptionList:        000cfb24
    StackBase:            000d0000
    StackLimit:           000cc000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7efdd000
    EnvironmentPointer:   00000000
    ClientId:             00002dec . 00002c28
    RpcHandle:            00000000
    Tls Storage:          7efdd02c
    PEB Address:          7efde000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0
显示线程栈的结构
0:000> dd 000cc000-10
000cbff0  ???????? ???????? ???????? ????????
000cc000  00000000 00000000 00000000 00000000
000cc010  00000000 00000000 00000000 00000000
000cc020  00000000 00000000 00000000 00000000
000cc030  00000000 00000000 00000000 00000000
000cc040  00000000 00000000 00000000 00000000
000cc050  00000000 00000000 00000000 00000000
000cc060  00000000 00000000 00000000 00000000
StackLimit 更低的地址是没有分配的,所以是?号,不可读
0:000> !address 000cbff0

                                     
Failed to map Heaps (error 80004005)
Usage:                  Stack
Allocation Base:        00090000
Base Address:           000ca000
End Address:            000cc000
Region Size:            00002000
Type:                   00020000    MEM_PRIVATE
State:                  00001000    MEM_COMMIT
Protect:                00000104    PAGE_READWRITE|PAGE_GUARD
More info:              ~0k
可以看到有PAGE_GUARD保护
 

3.观察函数调用和返回过程

int __stdcall Proc(int n)
{
    int a=n;
    printf("A test to inspect stack, n=%d,a=%d.",n,a);
    return n*a;
}
int main()
{
    return Proc(122);
}
0:000> bp HiStack!main
0:000> g
Breakpoint 0 hit
HiStack!main:
00391040 6a7a            push    7Ah
0:000> r eip,esp
eip=00391040 esp=004dfb5c
0:000> p
eip=00391042 esp=004dfb58
0:000> dd 004dfb58 l1   -- esp的值就是压入堆栈的7a
004dfb58  0000007a
0:000> t
光标移到了子函数
HiStack!printf:
00391010 55              push    ebp
eip=00391010 esp=004dfb4c
eip就是当前子函数的位置
esp的内容应该是返回地址,可以查一下
0:000> dd 004dfb4c l1
004dfb4c  0039104e
0039104e 通过反汇编窗口查一下:
00391049 e8c2ffffff      call    HiStack!printf (00391010)
0039104e 83c40c          add     esp,0Ch
正是调用子函数后面的地址位置。说明call命令把EIP压栈了。
从函数ret后
eip=0039104e esp=004dfb50
esp的值把最初的7Ah也给弹出去了004dfb5c。

4. 局部变量和栈帧

LocalVar.cpp

int main()
{
    FuncA();
    FuncB("Dbg");
    FuncC("Dbg");
    return 0;
}
#pragma optimize( "", on )


int FuncA()
{
    int l,m,n;
    char sz[]="Advanced SW Debugging";
    l=sz[0];
    m=sz[4];
    n=sz[8];
    return l*m*n;
}
-----
FuncA
00391040 55              push    ebp
00391041 8bec            mov     ebp,esp
00391043 0f100d08213900  movups  xmm1,xmmword ptr [LocalVar!`string' (00392108)]
0039104a 83ec18          sub     esp,18h   ---esp寄存器被递减24字节,也就是分配了24字节的栈空间。这个空间为字符串变量分配的, 尽管字符串需要22个字节,但因为内存对齐的需要,所以编译器会实际分配24个字节。三个整数变量未分配栈空间,看来编译器想使用寄存器存储三个整数变量。
0039104d 0f28c1          movaps  xmm0,xmm1
00391050 660f7ec8        movd    eax,xmm1
00391054 660f73d804      psrldq  xmm0,4
00391059 660f7ec1        movd    ecx,xmm0
0039105d 0fbec0          movsx   eax,al
00391060 660f73d908      psrldq  xmm1,8
00391065 0fbec9          movsx   ecx,cl
00391068 0fafc1          imul    eax,ecx
0039106b 660f7ec9        movd    ecx,xmm1
0039106f 0fbec9          movsx   ecx,cl
00391072 0fafc1          imul    eax,ecx
00391075 8be5            mov     esp,ebp
00391077 5d              pop     ebp
00391078 c3              ret
00391079 cc              int     3
0039107a cc              int     3
0039107b cc              int     3
-------------------------------------
如何引用局部变量?
void FuncB(char * szPara)
{
    char szTemp[5];
    strncpy(szTemp,szPara,sizeof(szTemp)-1);
    printf("%s;Len=%d.\n",szTemp,strlen(szTemp));
}

LocalVar!FuncB:
00391080 55              push    ebp
00391081 8bec            mov     ebp,esp
00391083 83ec0c          sub     esp,0Ch   --- szPara,szTemp预留12个字节
00391086 a104303900      mov     eax,dword ptr [LocalVar!__security_cookie (00393004)]
0039108b 33c5            xor     eax,ebp
0039108d 8945fc          mov     dword ptr [ebp-4],eax
00391090 6a04            push    4                                -- 压入参数4,准备调用strncpy
00391092 51              push    ecx                              --压入szPara
00391093 8d45f4          lea     eax,[ebp-0Ch] 
00391096 50              push    eax                              -- 压入szTemp
00391097 ff15c4203900    call    dword ptr [LocalVar!_imp__strncpy (003920c4)]
0039109d 8d45f4          lea     eax,[ebp-0Ch]
003910a0 83c40c          add     esp,0Ch
003910a3 8d5001          lea     edx,[eax+1]
003910a6 8a08            mov     cl,byte ptr [eax]
003910a8 40              inc     eax
----------------------------------------------
#pragma optimize( "", off )
void FuncC(char * szPara)
{
    char szTemp[5];
    strncpy(szTemp,szPara,sizeof(szTemp)-1);
    printf("%s;Len=%d.\n",szTemp,strlen(szTemp));
}

LocalVar!FuncC:
003910d0 55              push    ebp          --压入EBP寄存器当前值
003910d1 8bec            mov     ebp,esp --ESP的当前值(栈顶)赋给EBP
003910d3 83ec1c          sub     esp,1Ch --为变量szTemp分配空间。应该是8,怎么分了24?
003910d6 a104303900      mov     eax,dword ptr [LocalVar!__security_cookie (00393004)]
003910db 33c5            xor     eax,ebp
003910dd 8945fc          mov     dword ptr [ebp-4],eax
003910e0 6a04            push    4            --准备调用strncpy,压入参数4
003910e2 8b4508          mov     eax,dword ptr [ebp+8] -- 将szPara赋给EAX
003910e5 50              push    eax  --压入szPara
003910e6 8d4df4          lea     ecx,[ebp-0Ch] -- 将szTemp的有效地址放入ECX寄存器
003910e9 51              push    ecx  --压szTemp
003910ea ff15c4203900    call    dword ptr [LocalVar!_imp__strncpy (003920c4)]  --调用strncpy
003910f0 83c40c          add     esp,0Ch   -- 调整栈指针,释放前面3次压入的参数
003910f3 8d55f4          lea     edx,[ebp-0Ch]      --将szTemp放入edx
003910f6 8955ec          mov     dword ptr [ebp-14h],edx   -- 
003910f9 8b45ec          mov     eax,dword ptr [ebp-14h]
003910fc 83c001          add     eax,1
003910ff 8945e8          mov     dword ptr [ebp-18h],eax
00391102 8b4dec          mov     ecx,dword ptr [ebp-14h]
00391105 8a11            mov     dl,byte ptr [ecx]
00391107 8855f3          mov     byte ptr [ebp-0Dh],dl
0039110a 8345ec01        add     dword ptr [ebp-14h],1
0039110e 807df300        cmp     byte ptr [ebp-0Dh],0
00391112 75ee            jne     LocalVar!FuncC+0x32 (00391102)
00391114 8b45ec          mov     eax,dword ptr [ebp-14h]
00391117 2b45e8          sub     eax,dword ptr [ebp-18h]
0039111a 8945e4          mov     dword ptr [ebp-1Ch],eax
0039111d 8b4de4          mov     ecx,dword ptr [ebp-1Ch]
00391120 51              push    ecx                             --压入ECX即strlen(szTemp)
00391121 8d55f4          lea     edx,[ebp-0Ch]          --将szTemp的有效地址放入EDX寄存器
00391124 52              push    edx                             --压入EDX,即szTemp
00391125 6820213900      push    offset LocalVar!`string' (00392120)   --压入常量字符串 "%s;Len=%d.\n"
0039112a e8e1feffff      call    LocalVar!printf (00391010)
0039112f 83c40c          add     esp,0Ch
00391132 8b4dfc          mov     ecx,dword ptr [ebp-4]
00391135 33cd            xor     ecx,ebp
00391137 e827000000      call    LocalVar!__security_check_cookie (00391163)
0039113c 8be5            mov     esp,ebp
0039113e 5d              pop     ebp
0039113f c3              ret
 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值