You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何读取存储列表指针的x86_64 %gs段寄存器并访问列表?

问题:通过LD_PRELOAD结合FSGSBASE存储并访问数组指针

背景与成功验证案例

我研究x86_64的FSGSBASE特性,希望通过LD_PRELOAD在程序加载阶段给%gs段寄存器赋值,运行时从该寄存器读取数据。

先编写了两段代码验证基础思路:

  • load.c 编译命令:gcc -Wall -fPIC -shared -o load.so load.c -ldl -mfsgsbase
#include <sys/auxv.h>
#include <elf.h>
#include <immintrin.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
/* Will be eventually in asm/hwcap.h */
#ifndef HWCAP2_FSGSBASE
#define HWCAP2_FSGSBASE        (1 << 1)
#endif
#define _GNU_SOURCE
void __attribute__((constructor)) load()
{
    int a = 4;
    unsigned val = getauxval(AT_HWCAP2);
    if (val & HWCAP2_FSGSBASE)
        printf("FSGSBASE enabled\n");

    int *addr_a = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);     
    if (addr_a == MAP_FAILED) {     
        fprintf(stderr, "mmap() failed\n");     
        exit(EXIT_FAILURE);
    } 
    *addr_a = a;
    _writegsbase_u64(addr_a);
}
  • main.c 编译命令:gcc -S main.c -o main.s && sed -i 's/-4(%rbp)/%gs:0x0/g' main.s && gcc main.s -o main
#include <stdio.h>

int main() {
    int a;  
    printf("From main: %d\n", a);
    printf("Hello World!\n");
    return 0;
}

执行命令:LD_PRELOAD=$PWD/load.so ./main,输出如下:

FSGSBASE enabled
From main: 4
Hello World!

此时已成功通过运行时读取%gs寄存器获取加载时分配的值(sed用于修改汇编代码,按要求读取%gs)。


遇到的问题:存储数组指针后无法正确访问

接下来尝试将数组的基地址存储到%gs段寄存器,运行时访问该数组。

修改后的load_updated.c,编译命令:gcc -Wall -fPIC -shared -o load_updated.so load_updated.c -ldl -mfsgsbase

void __attribute__((constructor)) load()
{
    int a = 4;
    unsigned val = getauxval(AT_HWCAP2);
    if (val & HWCAP2_FSGSBASE)
        printf("FSGSBASE enabled\n");

    int *addr_a = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);     
    if (addr_a == MAP_FAILED) {     
        fprintf(stderr, "mmap() failed\n");     
        exit(EXIT_FAILURE);
    } 
    *addr_a = a;
    int *table[] = {addr_a};
    printf("Table base address: %p\n", &table);
    _writegsbase_u64(&table);
}

但运行时无法正确读取该指针并访问数组。通过gdb调试:

gdb ./main
pwndbg> set environment LD_PRELOAD ./load_updated.so
pwndbg> b main
Breakpoint 1 at 0x1171
pwndbg> start
FSGSBASE enabled
Table base address: 0x7fffffffd880 0x7ffff7fb5000
pwndbg> i r $gs_base
gs_base        0x7fffffffd880      140737488345216

可以确认%gs寄存器已加载正确的数组基地址,但执行到如下汇编指令时:

0x555555555171 <main+8>     sub    rsp, 0x10
 ► 0x555555555175 <main+12>    mov    eax, dword ptr gs:[0]
   0x55555555517d <main+20>    mov    esi, eax

尝试将%gs指向的地址解引用到%eax寄存器时,%eax的值为0:

pwndbg> i r eax
rax            0x0                 0

尝试直接将%gs寄存器的值移动到%eax(不解引用),修改汇编代码将movl %gs:0, %eax改为movl %gs, %eax,仍未解决问题。


进一步尝试后的问题

按照提示添加汇编指令:

rdgsbase %rax
    movq    0x0(%rax), %rax

虽然能通过这种方式读取%gs寄存器,但寄存器中存储的地址并未包含预期的数组,而是垃圾值。例如在gdb中:

pwndbg> set environment LD_PRELOAD ./load.so
pwndbg> b main
Breakpoint 1 at 0x1171
pwndbg> start
Temporary breakpoint 2 at 0x1171
Table addr: (base address) 0x7fffffffd870 (*table[0]) 0x7ffff7ffa000

汇编执行时:

*RAX  0x7fffffffd870 ◂— 0x86d11c8e53b3e43
──────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────
   0x555555555171 <main+8>     sub    rsp, 0x10
   0x555555555175 <main+12>    rdgsbase rax
 ► 0x55555555517a <main+17>    mov    rax, qword ptr [rax + 0x40]

可见0x7fffffffd870并未包含预期的0x7ffff7ffa000,而是垃圾值0x86d11c8e53b3e43。

我的操作系统及架构信息:

> uname -a
Linux pop-os 6.2.6-76060206-generic #202303130630~1685473338~22.04~995127e SMP PREEMPT_DYNAMIC Tue M x86_64 x86_64 x86_64 GNU/Linux

现寻求技术指导,解决如何正确读取存储数组指针的%gs段寄存器并访问数组的问题。

内容的提问来源于stack exchange,提问作者Jay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 20:24:58