素槿
Published on 2025-08-22 / 3 Visits
0

C语言读取ELF文件内容

根据ELF文件的结构进行解析

  1. 打开并判断是不是ELF64位二进制文件

读取后记得重置文件指针到开头。

int is_elf_64(FILE* fp)
{
    char buf[16];
    int  nread = fread(buf, 1, 16, fp);
    fseek(fp, 0, SEEK_SET);
    if (nread < 16) {
        return 1;
    }

    if (strncmp(buf, ELFMAG, SELFMAG)) {
        return 1;
    }

    if (buf[EI_CLASS] != ELFCLASS64) {
        return 1;
    }
    return 0;
}

FILE* felf = fopen(argv[1], "r");
if (!felf) {
    printf("open %s failed.\n", argv[1]);
    return 1;
}

if (is_elf_64(felf)) {
    printf("file type mismatch.\n");
    return 1;
}
  1. 读取ELF文件头
Elf64_Ehdr m_elf;
fread(&m_elf, 1, sizeof(m_elf), felf);
  1. 读取所有段结构
Elf64_Shdr arSection[m_elf.e_shnum];
fseek(felf, m_elf.e_shoff, SEEK_SET);
fread(&arSection[0], 1, (m_elf.e_shnum * m_elf.e_shentsize), felf);
  1. 读取段名字索引
char arSectionNames[arSection[m_elf.e_shstrndx].sh_size];
fseek(felf, arSection[m_elf.e_shstrndx].sh_offset, SEEK_SET);
fread(&arSectionNames, 1, sizeof(arSectionNames), felf);
  1. 读取段结构和段名字
SectionMap m_mpSections[m_elf.e_shnum];
for (Elf64_Half i = 0; i < m_elf.e_shnum; i++) {
    m_mpSections[i].name = &arSectionNames[0] + arSection[i].sh_name;
    m_mpSections[i].shdr = arSection[i];
}
  1. 输出相关信息
const char findSectionName[] = ".dynstr";
// 遍历每一个段
for (Elf64_Half i = 0; i < m_elf.e_shnum; i++) {
    // 输出每个段的名字
    printf("section name :%s\n", m_mpSections[i].name);

    // 输出“.dynstr”段的内容
    if (!strcmp(m_mpSections[i].name, findSectionName)) {
        unsigned char content[m_mpSections[i].shdr.sh_size];
        fseek(felf, m_mpSections[i].shdr.sh_offset, SEEK_SET);
        fread(content, 1, m_mpSections[i].shdr.sh_size, felf);
        for (Elf64_Xword j = 0; j < m_mpSections[i].shdr.sh_size; ++j)
            printf("%c", content[j]);
        // printf("%02x", content[i]);对于非字符内容,应该输出十六机制。
    }
}

完整代码

#include <elf.h>
#include <stdio.h>
#include <string.h>

typedef struct {
    char*      name;
    Elf64_Shdr shdr;
} SectionMap;

/**
 * 判断是否是ELF64文件。
 */
int is_elf_64(FILE* fp)
{
    char buf[16];
    int  nread = fread(buf, 1, 16, fp);
    fseek(fp, 0, SEEK_SET);
    if (nread < 16) {
        return 1;
    }

    if (strncmp(buf, ELFMAG, SELFMAG)) {
        return 1;
    }

    if (buf[EI_CLASS] != ELFCLASS64) {
        return 1;
    }
    return 0;
}

int main(int argc, char const* argv[])
{
    if (argc != 2) {
        printf("usage : %s elf_file\n", argv[0]);
        return 1;
    }

    FILE* felf = fopen(argv[1], "r");
    if (!felf) {
        printf("open %s failed.\n", argv[1]);
        return 1;
    }

    if (is_elf_64(felf)) {
        printf("file type mismatch.\n");
        return 1;
    }

    // 1 读取elf头文件
    Elf64_Ehdr m_elf;
    fread(&m_elf, 1, sizeof(m_elf), felf);

    // 2 读取所有段结构
    Elf64_Shdr arSection[m_elf.e_shnum];
    fseek(felf, m_elf.e_shoff, SEEK_SET);
    fread(&arSection[0], 1, (m_elf.e_shnum * m_elf.e_shentsize), felf);

    // 3 读取段名字索引
    char arSectionNames[arSection[m_elf.e_shstrndx].sh_size];
    fseek(felf, arSection[m_elf.e_shstrndx].sh_offset, SEEK_SET);
    fread(&arSectionNames, 1, sizeof(arSectionNames), felf);

    // 4 读取段结构和段名字
    SectionMap m_mpSections[m_elf.e_shnum];
    for (Elf64_Half i = 0; i < m_elf.e_shnum; i++) {
        m_mpSections[i].name = &arSectionNames[0] + arSection[i].sh_name;
        m_mpSections[i].shdr = arSection[i];
    }

    const char findSectionName[] = ".dynstr";
    // 遍历每一个段
    for (Elf64_Half i = 0; i < m_elf.e_shnum; i++) {
        // 输出每个段的名字
        printf("section name :%s\n", m_mpSections[i].name);

        // 输出“.dynstr”段的内容
        if (!strcmp(m_mpSections[i].name, findSectionName)) {
            unsigned char content[m_mpSections[i].shdr.sh_size];
            fseek(felf, m_mpSections[i].shdr.sh_offset, SEEK_SET);
            fread(content, 1, m_mpSections[i].shdr.sh_size, felf);
            for (Elf64_Xword j = 0; j < m_mpSections[i].shdr.sh_size; ++j)
                printf("%c", content[j]);
            // printf("%02x", content[i]);对于非字符内容,应该输出十六机制。
        }
    }

    printf("\n");
    fclose(felf);
    return 0;
}

运行

$ g++ main.cc 
$ ./a.out ./a.out 
section name :
section name :.interp
section name :.note.gnu.property
section name :.note.gnu.build-id
section name :.note.ABI-tag
section name :.gnu.hash
section name :.dynsym
section name :.dynstr
libc.so.6fopenstrncmpputs__stack_chk_failputcharprintffseekfclosefread__cxa_finalizestrcmp__libc_start_mainGLIBC_2.4GLIBC_2.2.5_ITM_deregisterTMCloneTable__gmon_start___ITM_registerTMCloneTablesection name :.gnu.version
section name :.gnu.version_r
section name :.rela.dyn
section name :.rela.plt
section name :.init
section name :.plt
section name :.plt.got
section name :.plt.sec
section name :.text
section name :.fini
section name :.rodata
section name :.eh_frame_hdr
section name :.eh_frame
section name :.init_array
section name :.fini_array
section name :.dynamic
section name :.got
section name :.data
section name :.bss
section name :.comment
section name :.symtab
section name :.strtab
section name :.shstrtab