找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 31|回复: 1

【汇编/调试】简介x64上MSVC系ABI的栈回溯

[复制链接]
发表于 前天 10:39 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 tangptr@126.com 于 2025-1-14 10:42 编辑

前言

栈回溯称得上是调试时的神器。调试器若是没有栈回溯的能力,那么接到异常时就很难得知这个调用源是从哪里来的。

x86的原始栈回溯方案

在32位x86上,MSVC编译出来的程序通常会用ebp寄存器来保存栈帧,从而以链表的形式实现栈回溯。于是乎,就有了最经典的55 8B EC三字节序列(即push ebp-mov ebp,esp两条指令)。
当调试器接到异常(如断点)时,会读取线程ebp的值。这个值就是该栈帧的位置。其中:

  • 读取[ebp+4]可以获得调用者call指令的下一条指令的地址。
  • 读取[ebp]可以获得上一个栈帧的地址。
    以此循环,就可以获得整个调用链。如果每个指令地址都能查询到符号,那就能很直观的看明白调用链了。

x64的栈回溯

而到了64位时代,MSVC的ABI选择弃用rbp寄存器进行栈帧的保存,而是在PE映像中存放栈回溯的信息。这个操作叫做FPO(Frame Pointer Omission)。其实在32位时代也有这个操作,给编译器加上/Oy即可,但是64位上这个参数就没了。
这么做虽然会增加程序的大小,而且还会使得shellcode中的函数调用无法回溯,但是可以同时去掉保存与删除栈帧所带来的时间与栈空间的开销,为了性能其实还是值得的。

.pdata

.pdata节中保存着一个RUNTIME_FUNCTION结构体数组。如果链接时把.pdata节合并到其他节里,则可以通过PE可选头中的IMAGE_DIRECTORY_ENTRY_EXCEPTION数据目录定位。这个结构体的定义是:

typedef struct _FPO_DATA
{
    ULONG StartAddress;
    ULONG EndAddress;
    ULONG UnwindInfo;
}RUNTIME_FUNCTION;

以上值皆为相对于映像基址的32位RVA。其中StartAddress表示函数的起始地址,EndAddress表示函数的终止地址,UnwindInfo表示回溯信息。该数组按函数地址排序,因此可以直接用二分查找的方式快速定位回溯信息。
UnwindInfo指向的是UNWIND_INFO结构体,限于篇幅不做介绍,详情见MSDN
不做介绍的更根本的原因是:dbghelp.dll导出的StackWalk函数可以帮你实现栈回溯,详见MSDN

回溯纯汇编写的函数

在OS开发时,往往会用纯汇编实现一些特殊操作(如切换栈)。但是,汇编器可没有高级语言编译器那么聪明,并不能自动推断汇编所写的函数如何回溯。
因此,MASM汇编器提供了一系列的特殊语句。这些语句不会生成可执行的机器指令,但是可以帮助汇编器构造回溯信息。

  • .allocstack语句用于标记分配了多少空间的栈。这个语句通常搭配sub rsp,xxx指令使用。
  • .pushreg语句用于标记保存了哪些寄存器。这个语句通常搭配push reg指令使用。
  • .pushframe语句用于标记这个函数被中断/异常调用,栈上有栈帧。这个语句用于标记这个函数栈上有中断/异常栈帧。
  • .setframe语句用于标记这个函数有保存栈帧的寄存器。
  • .endprolog语句用于标记函数的prologue行为(即所有的分配栈空间的行为)结束。
    因此当使用纯汇编的时候,强烈建议正确结合上述语句构造栈回溯信息从而方便调试。
    以上除了.pushframe以外,使用方式应该都是比较直观的。

回溯被切换的栈


游客,如果您要查看本帖隐藏内容请回复

总结

本文简短的讲了一下32位时代的栈回溯方案,然后对比一下64位的栈回溯特点,最后重点讲解写汇编时如何生成栈回溯的信息,并以切换栈进行举例。

回复

使用道具 举报

发表于 前天 11:21 | 显示全部楼层
x64时代,趋势是以占用空间来换取其他便利性。SEH和栈信息密不可分,理应同时保存在文件中。再则vc++的异常信息在pe32时就保存在文件中了,那么和其很相关的信息保存在文件中就更合理了。
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2025-1-16 01:13 , Processed in 0.037070 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表