我将从回答一些基本问题开始,其中一些您甚至没有问过!
段寄存器在现代代码中做什么?
我们需要额外的寄存器来寻址内存区域已经有一段时间了。32 位,尤其是 64 位,已经绰绰有余。操作系统开发人员利用了那些未使用的寄存器,现在大多数现代操作系统至少使用一些寄存器来保存与操作系统相关的数据。正如评论中提到的,在 amd64 处理器上,段寄存器不能用于分段,但操作系统也在 32 位处理器上这样做。
您可以在此处阅读有关 linux 的更多信息,此处和此处有关 windows 等。
为什么我不能从以前执行的程序中恢复数据
尽管您可以控制程序执行的一些变量(参数、堆栈地址、进程加载地址和堆位置),但您仍然无法控制所有变量(特定分配的位置、从“外部”源(例如内核和作为)返回的值我们很快就会看到,反利用缓解措施也可能会干扰这类事情)。
一般来说,如果不进行必要的调整,你永远不应该期望这样的事情会起作用。更不用说像 setjmp/longjmp 这样低级和微妙的东西了。
为什么没有记录 setjmp/longjmp 实现?
首先,我们在逆向工程社区,避免文档并不能保证机密性。其次,文档在代码中:)
我想文档对于这种可能经常更改并且非常特定于体系结构的低级细节来说是相当困难的。这就引出了你的下一个问题——
为什么依赖于 setump/longjmp 架构?
显然,这是不言而喻的,但为了完整起见,我认为最好在这里明确。以下是必须在每个架构级别上完成的一些原因:
- 由于这些函数涉及 CPU 的某些 ABI(特别是调用约定),因此代码必须遵循不同的约定。
- 为特定目的按名称访问寄存器是在 C 中抽象出来的。
- C 是一种过程语言,因此其核心的 setjmp/longjmp 与 C 的本质直接矛盾,因为它打破了过程(函数)的界限。
- 其他特定于体系结构的功能(实现方式不同,影子堆栈和指针保护就是这样的例子)可能会改变 setjmp/longjmp 处理特定情况的方式。
从现在开始我将讨论 amd64。
setjump 是如何实现的
现在,虽然这不是 C(也不是最易读的程序集),但 amd64 上的 setjmp 代码可以在setjmp.S 中找到,longjmp 是__longjmp.S。它甚至得到了很好的评论,代码也很简单!
您可以清楚地看到保存在结构中的寄存器(例如,movq %r12, (JB_R12*8)(%rdi)
)。PTR_MANGLE
如果启用了 aformentinoed 指针保护功能,您可以看到被调用。
因为您的问题主要围绕查找代码而不是阅读代码,而且由于代码非常简单,所以我现在将阅读函数作为练习留给读者。稍后我会回来添加更多详细信息,因此请随时提出后续问题。
jmp_buf
结构是如何定义的
由于我们正在处理组装,因此我们没有结构。相反,有几个#define
预处理器指令来定义jmp_buf
结构。这些位于专用标题中:jmpbuf-offsets.h
这些文件在哪里?我在哪里可以找到不同的架构实现?
这些文件位于sysdep
模块中,该模块包含每个受支持的特定于体系结构的组件的子目录。aarch64
代表 arm 64 位,x86
代表 32 位英特尔 8086 兼容处理器,86_64
代表 64 位英特尔 8086 CPU,等等。