栈回溯----X86

首先来看一个简单的函数调用对应的汇编代码

#include <stdio.h>
int __stdcall temp(int m , int n)
{
    m++;
    return m;
}
int main()
{
    temp(1,2);
    getchar();
    getchar();
    return 0;
}

main 函数对应汇编

push    ebp
mov     ebp, esp

push    2
push    1
call    sub_401000
call    ds:getchar
call    ds:getchar
xor     eax, eax
pop     ebp
retn

temp 函数对应汇编


push    ebp
mov     ebp, esp

mov     eax, [ebp+arg_0]
add     eax, 1
mov     [ebp+arg_0], eax
mov     eax, [ebp+arg_0]
pop     ebp
retn    8

0x00CFFB6C  b0 fb cf 00   push        ebp ,mov ebp,esp

0x00CFFB68  02 00 00 00 push        2 

0x00CFFB64  01 00 00 00 push        1 

0x00CFFB60  2c 10 91 00 00911027 E8 D4 FF FF FF       call        temp (911000h)

0x00CFFB5C  6c fb cf 00   push        ebp  ,mov ebp,esp

pop         ebp

ret         8

image

X86 函数调用有很多调用约定,这里以被调用者回收栈区为例,栈回溯的操作与函数调用中最常见的指令---- PUSH EBP,MOV EBP,ESP.SUB ESP… 相关。这两步汇编指令经常出现在函数入口。在执行call 指令,将参数压栈之后,代码将call 指令的下一个指令eip 压栈,并跳转到call 指令的目标地址,之后执行上面的两条指令,通过当前函数的ebp 既可以得到当前函数的返回地址,又可以得到上一个ebp,即上一层的调用堆栈。

据此,我们不难写出如下代码,得到本函数的调用堆栈:

#include <stdio.h>
#include <Windows.h>
PVOID    g_ImageBase;
void Show(PVOID dwAddress)
{
    printf("返回地址 EIP:%p\r\n",dwAddress);
}
void Test3()
{
    __try
    {
        __asm
        {
            mov edx,ebp
            agin:
            mov eax,edx
            mov edx,[eax]    //上一个ebp
            mov ecx,[eax+4]     //函数返回地址
            mov eax,ecx
            push edx
            push eax
            call Show
            add esp,4
            pop  edx
            jmp agin
        }
                   
    }__except(1)
    {
         printf("嘿嘿\r\n");
        getchar();
        getchar();
    }
}

void Test2(int a)
{
    Test3();
           
}
void Test1(int a)
{
    Test2(0x12345678);
}
int main()
{
    Test1(0x12345670);
    return 0;
}

返回地址 EIP:009410B8
返回地址 EIP:009410CD
返回地址 EIP:009410ED
返回地址 EIP:00941267
返回地址 EIP:759D8744
返回地址 EIP:77E9587D
返回地址 EIP:77E9584D
返回地址 EIP:00000000

  

使用windbg 简单验证如下

   

0:001> u 0x009410B8 - 5
Show!Test2+0x3 [f:\program\show\show\show.cpp @ 38]:
009410b3 e868ffffff      call    Show!Test3 (00941020)
009410b8 5d              pop     ebp

0:001> u 0x009410CD - 5
Show!Test1+0x8 [f:\program\show\show\show.cpp @ 43]:
009410c8 e8e3ffffff      call    Show!Test2 (009410b0)
009410cd 83c404          add     esp,4
009410d0 5d              pop     ebp
009410d1 c3              ret

  

0:001> u 0x77E9584D - 5
ntdll!_RtlUserThreadStart+0x16:
77e95848 e801000000      call    ntdll!__RtlUserThreadStart (77e9584e)
77e9584d cc              int     3
ntdll!__RtlUserThreadStart:
77e9584e 6a1c            push    1Ch
77e95850 682801f377      push    offset ntdll!ResCSegmentValidateHeader+0x1122 (77f30128)
77e95855 e81efd0100      call    ntdll!_SEH_prolog4_GS (77eb5578)
77e9585a 8bf9            mov     edi,ecx
77e9585c 8365fc00        and     dword ptr [ebp-4],0
77e95860 8b35f478f477    mov     esi,dword ptr [ntdll!Kernel32ThreadInitThunkFunction (77f478f4)]

如此,我们已经掌握了X86 栈回溯的基本原理及实现。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值