【问题标题】:where are the memory segments stored in x86 assemblyx86 程序集中存储的内存段在哪里
【发布时间】:2020-12-26 07:15:18
【问题描述】:

我有以下来自 gdb 的 x86 汇编命令:

mov eax, gs:0x14

当我输入(gdb) info registers 时,gs 的值变成了 0x63。 根据我读到的内容,要获得地址本身,我必须将 gs 乘以 0x10 并添加偏移量 (0x14)。

正如预期的那样,该地址无法从内存中读取,因为这是一个相对地址。我试图objdump 文件试图找到任何有意义的起点,我应该添加 0x644 以获取实际内存地址,但没有弹出任何内容。 当我在 gdb 中运行文件时,地址始终为 0x056555XXX,但在代码中间添加 0x644 到 0x56555000。

这个内存段实际位于哪里?

编辑: 我在 64 位 kali linux VM 上运行它,但该文件来自一些 CTF,并且是 32 位 i386 elf 文件。不知道是保护模式还是实模式……

【问题讨论】:

  • gs 是一个寄存器。这一切如何工作取决于您运行它的模式,在哪里,什么处理器(不仅仅是x86,还有32位和64位等),这是386的模拟等等,等等。然后有虚拟地址以及您的代码或文件将看到的物理地址。这导致操作系统在所有其余部分之上,以了解它们如何分配内存,甚至可能是工具链及其链接器脚本。请编辑问题。
  • 既然您提到了eaxgdb,我假设您在Linux 中处于32 位保护模式。您使用的计算仅适用于实模式分段。您需要从操作系统中查询gs 段基值。
  • 您描述的计算仅适用于实模式。假设您处于保护模式,您必须通过其他方式获取 GS Base 的值。在 32 位模式下,处理器从 GDT 获取段基地址。 (此外,在任何情况下,它都不是相对地址。)
  • 是保护模式。
  • 如果您的程序已与libpthread 链接,则gdb 命令info threads 将显示TLS 基地址作为线程ID,例如* 1 Thread 0xf7dc3700 (LWP 24305) 的地址为 0xf7dc3700。这是获取地址的最简单方法,但不能保证有效。

标签: assembly x86


【解决方案1】:

在保护模式下,段寄存器基本上只包含描述符表的索引,而不是段的实际地址。描述符表将包含段的基地址和大小。实际分解为 13 位索引、1 位本地/全局选择器和 2 位权限级别。

在您的情况下,gs 值 0x63 分解为权限级别 3(底部两位),这实际上是最低(用户)权限级别;选择全局描述符的 0 位,以及 GDT(全局描述符表)中的索引 12。不幸的是,没有简单的方法可以从 gdb 中读取 GDT,但是用于该指令的实际地址将根据 GDT 中索引 12 处的基地址计算,并添加偏移量 0x14。

在长(64 位)模式下,段寄存器中的值被完全忽略——而是基地址来自 GSBASE 寄存器。在 gdb 中,您可以使用寄存器名称 $gs_base 进行检查:

(gdb) p /x $gs_base

烦人的是,info registers(甚至info registers all不显示,所以你必须知道要专门打印它。

【讨论】:

  • 请注意,GS 基础不会通常与 GDT 中的匹配。操作系统将使用wrmsr 直接为该线程修改 GS 基础,而不是为系统中的每个线程为每个不同的 GS 基础创建一个 LDT 条目。例如,请参阅Detail about MSR_GS_BASE in linux x86 64。实际的传统 GDT / LDT 分段机制非常笨拙,以至于现代 x86 提供了更好的方法来设置线程本地存储的 GS 或 FS 基础。
  • @PeterCordes:在长(64 位)模式下是这种情况,但在受保护(32 位)模式下则不然。
  • 64位内核下的32位用户空间是兼容模式,是长模式的子模式。 en.wikipedia.org/wiki/X86-64#Operating_modes。根据 OP 的编辑“我在 64 位 kali linux VM 上运行它”。另外,您确定 32 位内核不使用wrmsrMSR_GS_BASE 吗?我认为它有效,并且比使用 LDT 和重新加载段 reg 更有效。或者你是在谈论我链接的那个问题的具体情况,内核对 fs 和 gs 使用空选择器值?对,纯 32 位内核会留下一些非零段选择器。
  • 由于 64 位之前的 CPU 上不存在 MSR_GS_BASE,因此尝试使用它的 32 位内核将无法在这样的机器上运行,但我想这可能是可能的。在 64 位内核上运行时,每次我查看段寄存器时,gdb 都会显示为 0,因此看到非零值表明正在发生一些奇怪的事情(并且像 0x63 这样的值看起来是合法的价值)。如果 OP 在可能的情况下运行,我添加了一条关于直接在 gdb 中检查 $gs_base 的注释。但是,不确定这是否适用于兼容模式。
  • 我一直猜测 Linux 会出于某些向后兼容的原因将非空 GS 用于 32 位用户空间,即使它仍然单独设置 gs 基数。但你是对的,我的 Linux 5.7 内核运行 FS=GS=0 的 32 位用户空间。 (在静态可执行文件中,IDK 与 TLS 有关)。我猜想现代 32 位 Linux 内核会在启动时检查 MSR 的可用性,并将其用于设置 GS 基础。但是他们可能仍然有一个非零的 GS 来加载段描述符的其他部分? IDK,也许OP毕竟有一个32位的VM。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
  • 1970-01-01
  • 2019-04-21
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多