Jac*_*sen 16 c windows assembly stack pointers
我正在将应用程序从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*/
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
不使用变量pc.看起来这个函数在堆栈中向下走,直到它失败.它假定它无法在应用程序堆栈外部读取,因此当它失败时,它会测量调用堆栈的深度.这段代码不需要在那里编译_EVERY_SINGLE编译器.只是VS2009.该应用程序不需要在EVERY_SINGLE计算机上运行.我们完全控制部署,因为我们自己安装/配置它并将整个产品交付给我们的客户.
Soa*_*Box 13
真正正确的做法是重写此函数所做的任何事情,以便它不需要访问实际的帧指针.这绝对是不好的行为.
但是,要做你想要的,你应该能够做到:
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*/
}
Run Code Online (Sandbox Code Playgroud)
这样做的原因是,在C中,函数通常做的第一件事是在分配局部变量之前保存基指针(ebp)的位置.通过创建一个局部变量(Frame)然后获取if的地址,我们真正得到了这个函数的堆栈帧的起始地址.
注意:某些优化可能会导致删除"Frame"变量.可能不是,但要小心.
第二个注意:当"pFrame"本身在堆栈上时,您的原始代码以及此代码将操纵"pFrame"指向的数据.有可能在这里意外覆盖pFrame然后你会有一个坏指针,并可能会得到一些奇怪的行为.当从x86移动到x64时要特别注意这一点,因为pFrame现在是8个字节而不是4个,所以如果旧的"使用pFrame做的东西"代码在占用内存之前考虑了Frame和pFrame的大小,那么你将会需要考虑新的,更大的尺寸.
您可以使用_AddressOfReturnAddress()内在函数来确定当前帧指针中的位置,假设它尚未完全优化掉.我假设如果你明确地引用它,编译器将阻止该函数优化掉帧指针.或者,如果您只使用单个线程,则可以使用IMAGE_NT_HEADER.OptionalHeader.SizeOfStackReserve和IMAGE_NT_HEADER.OptionalHeader.SizeOfStackCommit来确定主线程的堆栈大小.见这对如何访问IMAGE_NT_HEADER当前图像.
我还建议不要IsBadWritePtr用来确定堆栈的结尾.至少你可能会导致筹码增长,直到你击中预备队,因为你会去一个警卫页面.如果您确实想要查找堆栈的当前大小,请使用VirtualQuery您正在检查的地址.
如果最初的用途是走栈,你可以使用StackWalk64它.