如何获取基本堆栈指针的地址

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何获取基本堆栈指针的地址相关的知识,希望对你有一定的参考价值。

我正在将应用程序从x86移植到x64。我正在使用Visual Studio 2009;大多数代码是C ++,有些部分是纯C。在编译x64时我们不支持__asm关键字,而我们的应用程序包含一些内联汇编程序。我没有写这段代码所以我不确切知道应该做什么:

int CallStackSize() 
    DWORD Frame;
    PDWORD pFrame;
    __asm
        
            mov EAX, EBP
            mov Frame, EAX
        
    pFrame = (PDWORD)Frame;
    /*... do stuff with pFrame here*/

EBP是指向当前函数堆栈的基指针。有没有办法在不使用内联asm的情况下获取堆栈指针?我一直在关注微软提供的内在函数作为内联asm的替代品,但我找不到任何能给我带来帮助的东西。有任何想法吗?

安德烈亚斯询问用pFrame做了什么。这是完整的功能:

int CallStackSize(DWORD frameEBP = 0)

    DWORD pc;
    int tmpint = 0;
    DWORD Frame;
    PDWORD pFrame, pPrevFrame;

    if(!frameEBP) // No frame supplied. Use current.
    
        __asm
        
            mov EAX, EBP
            mov Frame, EAX
        
    
    else Frame = frameEBP;

    pFrame = (PDWORD)Frame;
    do
    
        pc = pFrame[1];
        pPrevFrame = pFrame;
        pFrame = (PDWORD)pFrame[0]; // precede to next higher frame on stack

        if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a DWORD boundary. Bail if not so.
        break;

        if (pFrame <= pPrevFrame)
        break;

        // Can two DWORDs be read from the supposed frame address?
        if(IsBadWritePtr(pFrame, sizeof(PVOID)*2))
        break;

        tmpint++;
     while (true);
    return tmpint;

不使用变量pc。看起来这个函数在堆栈中向下走,直到它失败。它假定它无法在应用程序堆栈外部读取,因此当它失败时,它会测量调用堆栈的深度。这段代码不需要在那里编译_EVERY_SINGLE编译器。只是VS2009。该应用程序不需要在EVERY_SINGLE计算机上运行。我们完全控制部署,因为我们自己安装/配置它并将整个产品交付给我们的客户。

答案

真正正确的做法是重写此函数所做的任何事情,以便它不需要访问实际的帧指针。这绝对是不好的行为。

但是,要做你想要的,你应该能够做到:

int CallStackSize() 
    __int64 Frame = 0; /* MUST be the very first thing in the function */
    PDWORD pFrame;

    Frame++; /* make sure that Frame doesn't get optimized out */

    pFrame = (PDWORD)(&Frame);
    /*... do stuff with pFrame here*/

这样做的原因是,在C中,函数通常做的第一件事是在分配局部变量之前保存基指针(ebp)的位置。通过创建一个局部变量(Frame)然后获取if的地址,我们真正得到了这个函数的堆栈帧的起始地址。

注意:某些优化可能会导致删除“Frame”变量。可能不是,但要小心。

第二个注意:当“pFrame”本身在堆栈上时,您的原始代码以及此代码将操纵“pFrame”指向的数据。有可能在这里意外覆盖pFrame然后你会有一个坏指针,并可能会得到一些奇怪的行为。当从x86移动到x64时要特别注意这一点,因为pFrame现在是8个字节而不是4个,所以如果旧的“使用pFrame做的东西”代码在占用内存之前考虑了Frame和pFrame的大小,那么你将会需要考虑新的,更大的尺寸。

另一答案

您可以使用_AddressOfReturnAddress()内在函数来确定当前帧指针中的位置,假设它尚未完全优化。我假设如果你明确地引用它,编译器将阻止该函数优化掉帧指针。或者,如果您只使用单个线程,则可以使用IMAGE_NT_HEADER.OptionalHeader.SizeOfStackReserveIMAGE_NT_HEADER.OptionalHeader.SizeOfStackCommit来确定主线程的堆栈大小。有关如何访问当前图像的this,请参阅IMAGE_NT_HEADER

我还建议不要使用IsBadWritePtr来确定堆栈的结尾。至少你可能会导致筹码增长,直到你击中预备队,因为你会去一个警卫页面。如果您确实想要找到堆栈的当前大小,请使用VirtualQuery和您正在检查的地址。

如果最初的用途是走栈,你可以使用StackWalk64

另一答案

Microsoft提供了一个库(DbgHelp)来处理stack walking,你应该使用而不是依赖于汇编技巧。例如,如果存在PDB文件,它也可以遍历优化的堆栈帧(那些不使用EBP)。

CodeProject有一篇文章解释了如何使用它:

http://www.codeproject.com/KB/threads/StackWalker.aspx

另一答案

无法保证RBP(x64相当于EBP)实际上是指向callstack中当前帧的指针。我猜微软决定尽管有几个新的通用寄存器,但它们需要另外一个被释放,因此RBP仅用作调用alloca()的函数中的framepointer,在某些其他情况下。因此,即使支持内联汇编,也不是可行的方法。

如果您只想回溯,则需要在dbghelp.dll中使用StackWalk64。它位于XP附带的dbghelp.dll中,并且XP之前没有64位支持,因此您不需要随应用程序发送dll。

对于32位版本,只需使用当前的方法。你自己的方法可能比dbghelp的导入库小,更不用说内存中的实际dll了,所以这是一个明确的优化(个人经验:我已经为x86实现了一个Glibc风格的回溯和backtrace_symbols,不到一个 - dbghelp导入库的大小的十分之一)。

此外,如果您将其用于进程内调试或发布后崩溃报告生成,我强烈建议您只使用提供给异常处理程序的CONTEXT结构。

也许有一天我会决定认真对待x64,并找出一个可以分享我可以共享的StackWalk64的廉价方式,但是由于我仍然以我的所有项目为目标,我没有打扰过。

另一答案

如果您需要精确的“基指针”,那么内联汇编是唯一的方法。

令人惊讶的是,编写使用相对较少的平台特定代码来驱动堆栈的代码是可能的,但是很难完全避免汇编(取决于您正在做什么)。

如果你要做的就是避免溢出堆栈,你可以只取任何局部变量的地址。

另一答案
.code

PUBLIC getStackFrameADDR _getStackFrameADDR
getStackFrameADDR:
    mov RAX, RBP
    ret 0

END

这样的东西对你有用。

用ml64或jwasm编译它,并在你的代码extern“C”中使用它来调用它.void getstackFrameADDR(void);

以上是关于如何获取基本堆栈指针的地址的主要内容,如果未能解决你的问题,请参考以下文章

调试器如何跟踪堆栈?

获取堆栈上控制器的指针

Qt中如何获取某个控件的指针地址

如何使用算术和掩码舍入地址?

如何从该指针获取指针指向的值的地址

C++:如何获取浮点指针的地址并将其转换为 void**