我在Linux调试器中添加了一个功能(我正在使用Ptrace来操纵跟踪的进程以及libbfd / libopcodes)以展开堆栈,并确定每个CALL分配的堆栈空间与静态派生的局部变量大小之间是否存在差异,沿途打印每个帧的地址和本地堆栈大小。帧指针,取消引用该地址,使用PTRACE_PEEKDATA对其进行检查,并重复执行直到我取消引用占用堆栈外部区域的地址。一种检查我是否仍在调用堆栈中的方法,即使该段已被W ^ X内存页或其他无法执行的堆栈更改了。

总之,我该如何检查情况)当我移出堆栈而不触发一般保护时n错误?当前进程的堆栈空间)

评论

我猜您在Linux下工作。在这里显然需要指定OS,因为它确实在开始进程之前设置了堆栈基地址(是否带有ASLR偏移)。

谢谢,我已经编辑了上面指定的答案-确实是linux。

强烈建议您阅读有关放卷的内容:第1部分,第2部分(面向OS X,但仍然适用许多内容)。

既然您知道intel / amd环境中的堆栈使用情况,就知道ebp值对于每次连续回滚(意味着,访问下一个父级)必定会增加值。另外,如果您正在研究Linux环境,则可以(有些安全)假定8MB(每个线程)的堆栈大小限制。此外,鉴于正在使用(最有可能的)4KB页面,您可以安全地读取ebp值,该ebp值的地址比当前到达一个不同(显然更高)的页面条目的UNTIL大。此时,您可以使用mmap()来查看页面是否存在...

另外,由于ESP / RSP和EBP / RSP值是4字节或2字节,因此您可以通过检查低2位(32位)或低3位(64位)来检测“损坏”的EBP / RBP值的某些形式分别对齐8字节。

#1 楼

因此,它完全未经测试,但是这是一些Internet浏览的结果。 />
我查看了pstack命令的代码,该命令正在打印正在运行的进程的堆栈内容。此代码从/proc/<pid>/maps结构获取基地址并将其存储在字段link_map中。此字段在函数l_addr中设置:

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);
}


我想这是正确的方法。因此,我建议您看一下readLinkMap()命令的代码(文件不是很长),并从中获得启发,因为它的功能与您想要的非常相似(至少在我理解您说的内容的情况下)正确)。

希望这篇简短的说明对您有所帮助。

评论


在用户问题的范围内,这似乎是足够的。但是,如果要实现回溯功能,则可能需要矮化cfi(如果要使其可靠)。

–百老汇
2013年6月5日16:11

@broadway:但是,矮化并不总是出现在所有可执行文件上。您可以从中获得一些帮助,但是,以我的拙见,完全依靠它是一个错误,而没有其他选择。

–恐怖
2013年6月5日17:46

是的,在这种情况下,您只需要接受破损即可。框架通常只是不在那里。不过,在某些常见情况下,您可以尝试启发式推断寄存器。

–百老汇
2013年6月5日18:03