如何在不触发保护错误的情况下检查我是否已移出堆栈?

逆向工程 调试器 linux x86 调用栈 分割
2021-06-29 10:15:54

我正在向我的 Linux 调试器添加一个功能(我使用 Ptrace 来操作被跟踪的进程以及 libbfd/libopcodes)来展开堆栈并确定每个 CALL 分配的堆栈空间和静态派生的局部变量大小之间是否存在差异,沿途打印每个帧的地址和本地堆栈大小。

我的一般方法是获取基指针 (EBP/RBP) 中的地址,将指针增加到应该包含存储的帧指针,取消引用该地址,使用 PTRACE_PEEKDATA 检查它并重复,直到我取消引用占用外部区域的地址堆。

我知道如何检查代码/数据段寄存器,但理想情况下我想要一种方法来检查我是否仍在调用堆栈中,即使分段已被 W^X 内存页或其他不可执行的堆栈更改。

简而言之,如何在不触发一般保护错误的情况下检查(在一般情况下)何时移出堆栈?

(顺便说一句,我意识到我正在假设检查地址的页段是这里的理想方法——也许存在另一种更简单的方法来确定地址是否在当前进程的堆栈空间内)

1个回答

所以,它完全未经测试,但这是一些互联网浏览的结果。

首先堆栈基地址存在于 中/proc/<pid>/maps,然后它必须在某个时刻可以从用户空间访问。

我查看了pstack命令的代码,该命令打印正在运行的进程的堆栈内容。此代码从link_map结构中获取基地址并将其存储在字段中l_addr该字段在函数内部设置readLinkMap()

static void readLinkMap(int pid, ElfN_Addr base, struct link_map *lm, 
                        char *name, unsigned int namelen)
{
  /* base address */
  lm->l_addr = (ElfN_Addr) ptrace(PTRACE_PEEKDATA, pid,
                                  base + offsetof(struct link_map,l_addr), 0);
  /* next element of link map chain */
  if (-1 != (long) lm->l_addr || !errno)
    lm->l_next = (struct link_map *) ptrace(PTRACE_PEEKDATA, pid,
                                            base + offsetof(struct link_map, l_next), 0);
  if ((-1 == (long) lm->l_addr || -1 == (long) lm->l_next) && errno) {
    perror("ptrace");
    quit("can't read target.");
  }

  loadString(pid, base + offsetof(struct link_map, l_name), name, namelen);
}

我想这是正确的方法。因此,我建议您查看pstack命令的代码(文件不是很长)并从中获得灵感,因为它执行的操作与您想要的非常相似(至少如果我理解您说的正确的话) )。

希望这篇简短的笔记对您有所帮助。