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

如何在已构建的ELF可执行文件中追加键值表并在运行时读取?

解决方案:在ELF可执行文件中追加键值表并运行时读取

一、核心结论

你的需求完全可以实现,核心思路是在构建后的ELF文件中添加自定义段存储秘密键值对,或直接追加数据到文件末尾,再通过程序自身逻辑读取对应内容,同时可以轻松处理段/数据不存在的情况。

二、具体实现方案

1. 部署时注入自定义ELF段(推荐)

使用objcopy工具即可给已编译好的ELF文件添加自定义段,无需修改原程序的构建流程:

  • 先准备存储键值对的文本文件secrets.txt:
    DB_PASSWORD=mysecretpass
    API_KEY=abc123xyz
    
  • 执行命令注入段:
    objcopy --add-section .secrets=secrets.txt --set-section-flags .secrets=noload,readonly your_executable
    
    参数说明:
    • --add-section:创建名为.secrets的段,内容来自secrets.txt
    • --set-section-flags:设置段属性为noload(不自动加载到内存,需手动读取文件)或load(自动加载到内存,可直接通过地址访问),按需选择。

2. 程序运行时读取自定义段

方案A:直接访问内存中的段(适用于load属性)

如果注入时设置段为load,可以通过链接器生成的边界符号直接获取段的起始和结束地址:

#include <stdio.h>

// 声明链接器自动生成的段边界符号
extern char __start__.secrets[];
extern char __stop__.secrets[];

int main() {
    // 判断段是否存在
    if (__start__.secrets == __stop__.secrets) {
        printf("No secrets section found\n");
        return 1;
    }

    // 读取并打印段内容
    printf("Secrets:\n");
    for (char *p = __start__.secrets; p < __stop__.secrets; p++) {
        putchar(*p);
    }
    putchar('\n');

    // 自行实现键值对解析逻辑(如按行分割、字符串截取)
    return 0;
}

编译时无需额外参数,链接器会自动处理这些符号。若段不存在,两个符号会指向同一地址,以此判断即可。

方案B:解析自身ELF文件读取段(适用于noload属性)

如果不想让秘密被加载到进程内存(更安全),可以让程序打开自身二进制文件,解析ELF结构找到目标段:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
#include <string.h>

int main() {
    int fd = open("/proc/self/exe", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open self");
        return 1;
    }

    Elf64_Ehdr ehdr;
    if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
        perror("Failed to read ELF header");
        close(fd);
        return 1;
    }

    // 读取段表
    Elf64_Shdr *shdr = malloc(ehdr.e_shentsize * ehdr.e_shnum);
    lseek(fd, ehdr.e_shoff, SEEK_SET);
    read(fd, shdr, ehdr.e_shentsize * ehdr.e_shnum);

    // 读取段名字符串表
    char *strtab = malloc(shdr[ehdr.e_shstrndx].sh_size);
    lseek(fd, shdr[ehdr.e_shstrndx].sh_offset, SEEK_SET);
    read(fd, strtab, shdr[ehdr.e_shstrndx].sh_size);

    // 查找.secrets段
    Elf64_Shdr *secret_shdr = NULL;
    for (int i = 0; i < ehdr.e_shnum; i++) {
        if (strcmp(strtab + shdr[i].sh_name, ".secrets") == 0) {
            secret_shdr = &shdr[i];
            break;
        }
    }

    if (!secret_shdr) {
        printf("No secrets section found\n");
        free(strtab);
        free(shdr);
        close(fd);
        return 1;
    }

    // 读取段内容
    char *secrets = malloc(secret_shdr->sh_size + 1);
    lseek(fd, secret_shdr->sh_offset, SEEK_SET);
    read(fd, secrets, secret_shdr->sh_size);
    secrets[secret_shdr->sh_size] = '\0';

    printf("Secrets:\n%s\n", secrets);

    // 清理资源
    free(secrets);
    free(strtab);
    free(shdr);
    close(fd);
    return 0;
}

注:以上是64位ELF代码,32位需替换Elf64_*为Elf32_*。

3. 更简便的替代:直接追加数据到文件末尾

如果不想修改ELF结构,可直接把键值对追加到文件末尾:

# 部署时追加秘密(用特殊分隔符标记起始,方便程序定位)
echo -n "===SECRET_START===" >> your_executable
echo "DB_PASSWORD=mysecretpass" >> your_executable
echo "API_KEY=abc123xyz" >> your_executable

程序读取时,从文件末尾反向查找分隔符,读取后续内容:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>

#define SECRET_MARK "===SECRET_START==="

int main() {
    int fd = open("/proc/self/exe", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open self");
        return 1;
    }

    struct stat st;
    fstat(fd, &st);
    off_t file_size = st.st_size;
    size_t mark_len = strlen(SECRET_MARK);

    // 从文件末尾反向查找标记
    char *buf = malloc(file_size);
    lseek(fd, 0, SEEK_SET);
    read(fd, buf, file_size);

    char *secret_start = strstr(buf, SECRET_MARK);
    if (!secret_start) {
        printf("No secret found\n");
        free(buf);
        close(fd);
        return 1;
    }
    secret_start += mark_len;

    printf("Secrets:\n%s", secret_start);

    free(buf);
    close(fd);
    return 0;
}

这种方法无需修改ELF结构,但要注意后续更新程序时需重新追加秘密。

三、安全提示

  • 注入秘密时确保部署环境安全,避免秘密泄露
  • 读取秘密后,建议立即用memset清除内存中的敏感数据
  • noload段或文件末尾追加的方式,秘密不会被自动加载到进程内存,比load段更难被内存dump工具获取

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:42:44