迄今我最喜欢的一段代码-栈回溯

查看被调试进程指定线程的栈回溯信息的方法,除了直接从TEB中找相关结构获取信息外,还可以利用栈帧关系来回溯父辈函数.

原理是大部分编译器生成的win32汇编代码在新的call的开始使用如下方式保护栈帧:

push ebp
mov  ebp,esp

执行过这两条指令后,ebp实际上指向的是被push的ebp的位置.

栈回溯

利用这个技巧,我们就可以遍历所有栈帧的ebp.又因为上图的关系,我们可以利用Ebp的位置计算出上个函数的返回地址.进而完成栈回溯.

当然还有一个问题,什么时候停止?用户模块的第一个栈桢中ebp的值是0.所以当我们发现ebp的值为0时即可认为栈回溯完成.

//栈回溯递归搜寻
void kRecur(DWORD* dwEbp)
{
    if(nullptr==dwEbp)
    {return;}

    DWORD dwNewEbp=0;

    if( false==ReadDebuggedMemory((PVOID)dwEbp,4,_Out_ (BYTE*)(&dwNewEbp)))
    {
        return ;
    }

    DWORD dwFunReturnPath=0;

    if (false==ReadDebuggedMemory((PVOID)(dwEbp+1),4,_Out_ (BYTE*)(&dwFunReturnPath)))
    {

        return;
    }
    //printf("函数返回地址:%p\r\n",(DWORD)dwFunReturnPath);

    if (0!=(DWORD)dwNewEbp)
    {
        kRecur((DWORD*)dwNewEbp);
    }


    return ;

}

值得注意的是上面的ReadDebuggedMemory()是我们自己对系统API进行的封装:
其中g_hProc是被调试进程的句柄.

/*
//用途:
通用读被调试者内存函数.读失败不做改变权限尝试.
//参数:

//起始读地址
LPVOID  lpAddress   
//希望获得目标进程指定地址内存内容字节的数量
DWORD   dwGetNumber 
//用于存放获取内容的地方.例如:BYTE byA[20]中的byA
_Out_   BYTE* wcGetValue

//返回值   
成功   true
读失败 false
*/

bool ReadDebuggedMemory(LPVOID lpAddress,DWORD dwGetNumber,_Out_ BYTE* wcGetValue)
{

    DWORD   dwRetN      = 0;

    if(!ReadProcessMemory(g_hProc,lpAddress,wcGetValue,dwGetNumber,&dwRetN))
    {
        return false;
    }
    return true;
}

2015年11月16日 14:05:55

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值