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

如何在无库依赖的x86-64 Linux下通过汇编/链接脚本访问特定内存区域

解决方案:无库依赖下访问Linux外部内存

核心思路:直接调用Linux系统调用

因为不能链接任何库,无法使用标准C库的mmap(),必须直接通过x86-64系统调用指令触发mmap系统调用。x86-64 Linux下用syscall指令触发系统调用,mmap的系统调用号为9。

手动实现mmap的代码示例

纯汇编实现(syscall.S)

.global _start

_start:
    ; 准备mmap参数:rax存系统调用号,参数依次入rdi、rsi、rdx、r10、r8、r9
    mov rax, 9          ; mmap系统调用号
    xor rdi, rdi        ; addr=NULL,让内核自动分配地址
    mov rsi, 0x1000     ; 映射长度4KB(页面大小)
    mov rdx, 3          ; prot=PROT_READ|PROT_WRITE
    mov r10, 0x22       ; flags=MAP_PRIVATE|MAP_ANONYMOUS(匿名映射)
    mov r8, -1          ; fd=-1(匿名映射无需文件)
    xor r9, r9          ; offset=0
    syscall             ; 触发系统调用

    ; 检查返回值:rax >= -4095则为错误
    cmp rax, -4095
    jl mmap_success

    ; 错误处理:调用exit退出
    mov rax, 60         ; exit系统调用号
    mov rdi, 1          ; 错误码1
    syscall

mmap_success:
    ; 读写映射的内存
    mov qword [rax], 0x123456789abcdef0  ; 写入测试值
    mov rbx, qword [rax]                 ; 读取测试值

    ; 正常退出
    mov rax, 60
    xor rdi, rdi
    syscall

C内嵌汇编实现

#define MMAP_SYSCALL_NUM 9
#define EXIT_SYSCALL_NUM 60

int main(void) {
    void *addr;
    int prot = 3;          // PROT_READ | PROT_WRITE
    int flags = 0x22;      // MAP_PRIVATE | MAP_ANONYMOUS
    int fd = -1;
    off_t offset = 0;

    __asm__ volatile (
        "syscall"
        : "=a"(addr)
        : "a"(MMAP_SYSCALL_NUM),
          "D"(0),          // addr=NULL
          "S"(0x1000),     // 映射长度4KB
          "d"(prot),
          "r10"(flags),
          "r8"(fd),
          "r9"(offset)
        : "memory", "rcx", "r11"
    );

    // 错误判断
    if ((long)addr >= -4095) {
        __asm__ volatile (
            "syscall"
            :
            : "a"(EXIT_SYSCALL_NUM), "D"(1)
            : "rcx", "r11"
        );
    }

    // 使用映射内存
    *(unsigned long*)addr = 0x123456789abcdef0;
    unsigned long val = *(unsigned long*)addr;

    // 正常退出
    __asm__ volatile (
        "syscall"
        :
        : "a"(EXIT_SYSCALL_NUM), "D"(0)
        : "rcx", "r11"
    );

    return 0;
}

链接脚本的正确配置

之前配置MEMORY和SECTIONs失败,是因为链接器只能处理编译时已知的段,无法分配运行时动态内存。链接脚本只需确保基础段正确布局即可,示例极简脚本(linker.ld):

ENTRY(_start)

SECTIONS {
    . = 0x400000;  // x86-64 Linux默认加载地址
    .text : { *(.text) }
    .data : { *(.data) }
    .bss : { *(.bss) }
}

编译运行命令

# 汇编代码编译
gcc -nostdlib -static -T linker.ld syscall.S -o memtool

# C代码编译
gcc -nostdlib -static -T linker.ld memtool.c -o memtool

关键注意事项

  • x86-64系统调用规则:rax存系统调用号,参数依次入rdi、rsi、rdx、r10、r8、r9,返回值存在rax。
  • 若需映射特定物理内存,可打开/dev/mem后调整flags和fd参数。
  • 错误判断:系统调用返回错误时rax为负数(绝对值是错误码),通过(long)addr >= -4095识别。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 09:35:15