查找fs:28h的内存地址

3
我用gdb调试程序,纯粹是为了好玩。这个程序有堆栈保护功能,会把canary写入fs:28h指向的堆栈地址中。出于好奇,我试图找出fs:28h指向的内存地址。
我遇到了两个问题。首先,由于gdb不能在ring zero中运行,所以无法显示gdtr/ldtr中的值。其次,在使用gdb读取fs时,它显示为0,这怎么解释呢?我知道gdt/ldt在第一个索引处包含0,用于返回无效地址,并且RPL已关闭,那么我错过了什么吗?如果有人知道如何找到段寄存器fs指向的地址,我想了解一下。
file: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked

段寄存器的是空选择器。段基地址通过MSR或64位模式下的wrfsbase单独设置。相关链接:读取包含指向列表的段寄存器(%gs) - Peter Cordes
1个回答

0
你似乎正在使用x86_64-linux-gnu,也就是x86-64 CPU、Linux内核和GNU C库的组合。在这个硬件和系统软件的组合下,FS段被设置为使得内存操作数%fs:0指向当前执行线程的线程控制块的开头。(线程控制块是一个描述运行线程的内部C库数据结构。你可以将其视为由pthread_t线程句柄引用的对象。)
由于 x86 历史上并没有让非特权代码轻松查找由 `%fs:xxx` 或 `%gs:xxx` 内存操作数所引用的 "实际" 地址(更准确地说是 "线性虚拟地址"),因此,在 x86_64-linux-gnu 上,线程控制块的第一个字段是指向线程控制块本身的指针。因此,
void *read_fs(void) {
    void *rv;
    asm("movq %%fs:0, %0" : "=r" (rv));
    return rv;
}

将返回您想要的地址。请记住,这仅在 x86_64-linux-gnu 上是保证成立的。如果您切换到不同的 Unix 内核(例如 FreeBSD)或不同的 C 库(例如 musl libc),FS 段仍然可能指向线程控制块,但该空间中的数据很可能会有所不同。

1
不,"有效地址"是seg:off逻辑地址的偏移部分。段覆盖对LEA没有影响。(请记住,最初的8086用例是为了执行地址运算以生成可以使用现有段基址进行解引用的指针(近指针=偏移量)。如果LEA添加段基址,首先,它将成为一个20位的线性地址,可能无法容纳在16位的8086寄存器中,而且对指针进行解引用时又会再次添加段基址。) - Peter Cordes
1
从用户空间读取段基址,需要在支持它的 CPU + 内核上使用 rdfsbase。(Linux 内核 5.9 及更高版本,以及 Ivy Bridge 或更新的 Intel 处理器 IIRC。)参见读取包含指向列表的段寄存器(%gs) / 如何在不链接 libc.so 的情况下访问段寄存器? - Peter Cordes
1
@ PeterCordes 既然我现在用的是一台真正的计算机,我已经重新编写了我的答案,以便无论rdfsbase是否可用,都能准确适用于x86-64+Linux+glibc。 - zwol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接