C语言实现ELF文件解析

这学期Linux的大作业第一部分是解析64位/32位的ELF文件,这里偷了个懒只解析了ELF头、程序头表和节头表;
这里先附上代码占个坑,有时间会来补详细解释;
输入格式为ELF文件名;
程序会同时将三个解析结果打印到控制台并写入同级目录下的result.txt文件中;

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

#define putline puts("-------------------------------------------------------------------------------------------------------------")

int OStype;
FILE *fp;
char *fname;
char strtable[9999];

//----------------------------------------------------------------------------------
//-------------------------------64位分析-------------------------------------------
//----------------------------------------------------------------------------------

void ELF_header_64_parse(Elf64_Ehdr* ehdr) {
    
    
    fseek(fp, 0, SEEK_SET);
    fread(ehdr, sizeof(Elf64_Ehdr), 1, fp);
    putline;
    puts("ELF头:");
    printf("Magic:\t\t\t");
    for(int i = 0; i < EI_NIDENT; ++i) printf("%02x ", ehdr->e_ident[i]);
    printf("\n类别:\t\t\t");
    switch(ehdr->e_type) {
    
    
        case 0 : printf("未知文件类型\n"); break;
        case 1 : printf("可重定位文件\n"); break;
        case 2 : printf("可执行文件\n"); break;
        case 3 : printf("动态链接库文件\n"); break;
        case 4 : printf("Core文件\n"); break;
        case 0xff00 : printf("特定处理器文件扩展下边界\n"); break;
        case 0xffff : printf("特定处理器文件扩展上边界\n"); break;
    }
    printf("处理器体系结构:\t\t");
    switch(ehdr->e_machine) {
    
    
        case 0 : printf("未知体系结构\n"); break;
        case 1 : printf("AT&T WE 32100\n"); break;
        case 2 : printf("SPARC\n"); break;
        case 3 : printf("Intel Architecture\n"); break;
        case 4 : printf("Motorola 68000\n"); break;
        case 5 : printf("Motorola 88000\n"); break;
        case 7 : printf("Intel 80860\n"); break;
        case 8 : printf("MIPS RS3000 Big-Endian\n"); break;
        case 10 : printf("MIPS RS4000 Big-Endian\n"); break;
        case 62 : printf("AMD x86-64 architecture\n"); break;
    }
    printf("version:\t\t");
    switch(ehdr->e_version) {
    
    
        case 0 : printf("非法版本号\n"); break;
        case 1 : printf("当前版本号\n"); break;
    }
    printf("入口虚拟地址:\t\t0x%016x\n", ehdr->e_entry);
    printf("程序头表偏移量:\t\t0x%08x\n", ehdr->e_phoff);
    printf("节头表偏移量:\t\t0x%08x\n", ehdr->e_shoff);
    printf("处理器标志位:\t\t%x\n", ehdr->e_flags);
    printf("ELF文件头大小:\t\t%u bytes\n", ehdr->e_ehsize);
    printf("程序头标每一表项大小:\t%u bytes\n", ehdr->e_phentsize);
    printf("程序头表表项数量:\t%u\n", ehdr->e_phnum);
    printf("节头表每一表项大小:\t%u bytes\n", ehdr->e_shentsize);
    printf("节头表表项数量:\t\t%u\n", ehdr->e_shnum);
    printf("字符串表在节头表中索引:\t%u\n", ehdr->e_shstrndx);
}

void section_header_64_parse(Elf64_Ehdr* ehdr) {
    
    
    Elf64_Shdr shdr[99];
    int count = ehdr->e_shnum;    //节头表数量
    fseek(fp, ehdr->e_shoff, SEEK_SET);
    fread(shdr, sizeof(Elf64_Shdr), count, fp);
    fseek(fp, shdr[ehdr->e_shstrndx].sh_offset, SEEK_SET);
    fread(strtable, 1, shdr[ehdr->e_shstrndx].sh_size, fp);
    putline;
    printf("There are %d section headers, starting at offset 0x%04x:\n\n", count, ehdr->e_shoff);
    puts("节头表:");
    printf("[编号]\t名称\t\t\    类型\t\t属性\t虚拟地址\t\t偏移量\t\t大小\t\t索引值\t信息\t对齐长度\t表项大小\n");
    for(int i = 0; i < count; ++i) {
    
    
        printf("[%02d]\t%s", i, &strtable[shdr[i].sh_name]);
        for(int j = 0; j < 20 - strlen(&strtable[shdr[i].sh_name]); ++j) {
    
    
            putchar(' ');
        }
        switch(shdr[i].sh_type) {
    
    
            case 0 : printf("SHT_NULL\t"); break;
            case 1 : printf("SHT_PROGBITS"); break;
            case 2 : printf("SHT_SYMTAB\t"); break;
            case 3 : printf("SHT_STRTAB\t"); break;
            case 4 : printf("SHT_RELA\t"); break;
            case 5 : printf("SHT_HASH\t"); break;
            case 6 : printf("SHT_DYNAMIC\t"); break;
            case 7 : printf("SHT_NOTE\t"); break;
            case 8 : printf("SHT_NOBITS\t"); break;
            case 9 : printf("SHT_REL\t"); break;
            case 10 : printf("SHT_SHLIB\t"); break;
            case 11 : printf("SHT_DYNSYM\t"); break;
            case 14 : printf("SHT_INIT_ARRAY"); break;
            case 15 : printf("SHT_FINI_ARRAY"); break;
            case 0x70000000 : printf("SHT_LOPROC"); break;
            case 0x7fffffff : printf("SHT_HIPROC"); break;
            case 0x80000000 : printf("SHT_LOUSER"); break;
            case 0xffffffff : printf("SHT_HIUSER"); break;
            case 0x6ffffff6 : printf("SHT_GNU_HASH"); break;
            case 0x6fffffff : printf("SHT_GNU_versym"); break;
            case 0x6ffffffe : printf("SHT_GNU_verneed"); break;
        }
        printf("\t0x%x\t", shdr[i].sh_flags);
        printf("0x%016x\t", shdr[i].sh_addr);
        printf("0x%08x\t", shdr[i].sh_offset);
        printf("%4lu bytes\t", shdr[i].sh_size);
        printf("%u\t", shdr[i].sh_link);
        printf("%u\t", shdr[i].sh_info);
        printf("%2lu bytes\t", shdr[i].sh_addralign);
        printf("%4x\n", shdr[i].sh_entsize);
    }
}

void program_header_64_parse(Elf64_Ehdr* ehdr) {
    
    
    Elf64_Phdr phdr[99];
    fseek(fp, ehdr->e_phoff, SEEK_SET);
    int count = ehdr->e_phnum;    //程序头表的数量
    fread(phdr, sizeof(Elf64_Phdr), count, fp);
    putline;
    printf("There are %d program headers, starting at offset 0x%04x:\n\n", count, ehdr->e_phoff);
    puts("程序头表:");
    puts("类型\t\t属性\t偏移量\t\t虚拟地址\t\t物理地址\t\t文件大小\t镜像大小\t对齐长度");
    for(int i = 0; i < count; ++i) {
    
    
        switch(phdr[i].p_type) {
    
    
            case 0 : printf("PT_NULL\t"); break;
            case 1 : printf("PT_LOAD\t"); break;
            case 2 : printf("PT_DYNAMIC"); break;
            case 3 : printf("PT_INTERP"); break;
            case 4 : printf("PT_NOTE\t"); break;
            case 5 : printf("PT_SHLIB"); break;
            case 6 : printf("PT_PHDR\t"); break;
            case 0x6474e550 : printf("GNU_EH_FRAME"); break;
            case 0x6474e551 : printf("GNU_STACK"); break;
            case 0x6474e552 : printf("GNU_RELRO"); break;
            case 0x70000000 : printf("PT_LOPROC"); break;
            case 0x7fffffff : printf("PT_HIPROC"); break;
        }
        putchar('\t');
        switch(phdr[i].p_flags) {
    
    
            case 0 : printf("none"); break;
            case 1 : printf("x"); break;
            case 2 : printf("w"); break;
            case 3 : printf("wx"); break;
            case 4 : printf("r"); break;
            case 5 : printf("rx"); break;
            case 6 : printf("rw"); break;
            case 7 : printf("rwx"); break;
        }
        printf("\t0x%08x", phdr[i].p_offset);
        printf("\t0x%016x", phdr[i].p_vaddr);
        printf("\t0x%016x", phdr[i].p_paddr);
        printf("\t%6u bytes", phdr[i].p_filesz);
        printf("\t%6u bytes", phdr[i].p_memsz);
        printf("\t%8u bytes", phdr[i].p_align);
        putchar('\n');
    }
}

//----------------------------------------------------------------------------------
//-----------------------------32位分析---------------------------------------------
//----------------------------------------------------------------------------------

void ELF_header_32_parse(Elf32_Ehdr* ehdr) {
    
    
    fseek(fp, 0, SEEK_SET);
    fread(ehdr, sizeof(Elf32_Ehdr), 1, fp);
    putline;
    puts("ELF头:");
    printf("Magic:\t\t\t");
    for(int i = 0; i < EI_NIDENT; ++i) printf("%02x ", ehdr->e_ident[i]);
    printf("\n类别:\t\t\t");
    switch(ehdr->e_type) {
    
    
        case 0 : printf("未知文件类型\n"); break;
        case 1 : printf("可重定位文件\n"); break;
        case 2 : printf("可执行文件\n"); break;
        case 3 : printf("动态链接库文件\n"); break;
        case 4 : printf("Core文件\n"); break;
        case 0xff00 : printf("特定处理器文件扩展下边界\n"); break;
        case 0xffff : printf("特定处理器文件扩展上边界\n"); break;
    }
    printf("处理器体系结构:\t\t");
    switch(ehdr->e_machine) {
    
    
        case 0 : printf("未知体系结构\n"); break;
        case 1 : printf("AT&T WE 32100\n"); break;
        case 2 : printf("SPARC\n"); break;
        case 3 : printf("Intel Architecture\n"); break;
        case 4 : printf("Motorola 68000\n"); break;
        case 5 : printf("Motorola 88000\n"); break;
        case 7 : printf("Intel 80860\n"); break;
        case 8 : printf("MIPS RS3000 Big-Endian\n"); break;
        case 10 : printf("MIPS RS4000 Big-Endian\n"); break;
        case 62 : printf("AMD x86-64 architecture\n"); break;
    }
    printf("version:\t\t");
    switch(ehdr->e_version) {
    
    
        case 0 : printf("非法版本号\n"); break;
        case 1 : printf("当前版本号\n"); break;
    }
    printf("入口虚拟地址:\t\t0x%08x\n", ehdr->e_entry);
    printf("程序头表偏移量:\t\t0x%04x\n", ehdr->e_phoff);
    printf("节头表偏移量:\t\t0x%04x\n", ehdr->e_shoff);
    printf("处理器标志位:\t\t%x\n", ehdr->e_flags);
    printf("ELF文件头大小:\t\t%u bytes\n", ehdr->e_ehsize);
    printf("程序头标每一表项大小:\t%u bytes\n", ehdr->e_phentsize);
    printf("程序头表表项数量:\t%u\n", ehdr->e_phnum);
    printf("节头表每一表项大小:\t%u bytes\n", ehdr->e_shentsize);
    printf("节头表表项数量:\t\t%u\n", ehdr->e_shnum);
    printf("字符串表在节头表中索引:\t%u\n", ehdr->e_shstrndx);
}

void section_header_32_parse(Elf32_Ehdr* ehdr) {
    
    
    Elf32_Shdr shdr[99];
    int count = ehdr->e_shnum;    //节头表数量
    fseek(fp, ehdr->e_shoff, SEEK_SET);
    fread(shdr, sizeof(Elf32_Shdr), count, fp);
    fseek(fp, shdr[ehdr->e_shstrndx].sh_offset, SEEK_SET);
    fread(strtable, 1, shdr[ehdr->e_shstrndx].sh_size, fp);
    putline;
    printf("There are %d section headers, starting at offset 0x%04x:\n\n", count, ehdr->e_shoff);
    puts("节头表:");
    printf("[编号]\t名称\t\t\    类型\t\t属性\t虚拟地址\t偏移量\t大小\t\t索引值\t信息\t对齐长度\t表项大小\n");
    for(int i = 0; i < count; ++i) {
    
    
        printf("[%02d]\t%s", i, &strtable[shdr[i].sh_name]);
        for(int j = 0; j < 20 - strlen(&strtable[shdr[i].sh_name]); ++j) {
    
    
            putchar(' ');
        }
        switch(shdr[i].sh_type) {
    
    
            case 0 : printf("SHT_NULL\t"); break;
            case 1 : printf("SHT_PROGBITS"); break;
            case 2 : printf("SHT_SYMTAB\t"); break;
            case 3 : printf("SHT_STRTAB\t"); break;
            case 4 : printf("SHT_RELA\t"); break;
            case 5 : printf("SHT_HASH\t"); break;
            case 6 : printf("SHT_DYNAMIC\t"); break;
            case 7 : printf("SHT_NOTE\t"); break;
            case 8 : printf("SHT_NOBITS\t"); break;
            case 9 : printf("SHT_REL\t"); break;
            case 10 : printf("SHT_SHLIB\t"); break;
            case 11 : printf("SHT_DYNSYM\t"); break;
            case 14 : printf("SHT_INIT_ARRAY"); break;
            case 15 : printf("SHT_FINI_ARRAY"); break;
            case 0x70000000 : printf("SHT_LOPROC"); break;
            case 0x7fffffff : printf("SHT_HIPROC"); break;
            case 0x80000000 : printf("SHT_LOUSER"); break;
            case 0xffffffff : printf("SHT_HIUSER"); break;
            case 0x6ffffff6 : printf("SHT_GNU_HASH"); break;
            case 0x6fffffff : printf("SHT_GNU_versym"); break;
            case 0x6ffffffe : printf("SHT_GNU_verneed"); break;
        }
        printf("\t0x%x\t", shdr[i].sh_flags);
        printf("0x%08x\t", shdr[i].sh_addr);
        printf("0x%04x\t", shdr[i].sh_offset);
        printf("%4lu bytes\t", shdr[i].sh_size);
        printf("%u\t", shdr[i].sh_link);
        printf("%u\t", shdr[i].sh_info);
        printf("%2lu bytes\t", shdr[i].sh_addralign);
        printf("%4x\n", shdr[i].sh_entsize);
    }
}

void program_header_32_parse(Elf32_Ehdr* ehdr) {
    
    
    Elf32_Phdr phdr[99];
    fseek(fp, ehdr->e_phoff, SEEK_SET);
    int count = ehdr->e_phnum;    //程序头表的数量
    fread(phdr, sizeof(Elf32_Phdr), count, fp);
    putline;
    printf("There are %d program headers, starting at offset 0x%04x:\n\n", count, ehdr->e_phoff);
    puts("程序头表:");
    puts("类型\t\t属性\t偏移量\t虚拟地址\t物理地址\t文件大小\t镜像大小\t对齐长度");
    for(int i = 0; i < count; ++i) {
    
    
        switch(phdr[i].p_type) {
    
    
            case 0 : printf("PT_NULL\t"); break;
            case 1 : printf("PT_LOAD\t"); break;
            case 2 : printf("PT_DYNAMIC"); break;
            case 3 : printf("PT_INTERP"); break;
            case 4 : printf("PT_NOTE\t"); break;
            case 5 : printf("PT_SHLIB"); break;
            case 6 : printf("PT_PHDR\t"); break;
            case 0x6474e550 : printf("GNU_EH_FRAME"); break;
            case 0x6474e551 : printf("GNU_STACK"); break;
            case 0x6474e552 : printf("GNU_RELRO"); break;
            case 0x6474e553 : printf("GNU_PROPERTY"); break;
            case 0x70000000 : printf("PT_LOPROC"); break;
            case 0x7fffffff : printf("PT_HIPROC"); break;
        }
        putchar('\t');
        switch(phdr[i].p_flags) {
    
    
            case 0 : printf("none"); break;
            case 1 : printf("x"); break;
            case 2 : printf("w"); break;
            case 3 : printf("wx"); break;
            case 4 : printf("r"); break;
            case 5 : printf("rx"); break;
            case 6 : printf("rw"); break;
            case 7 : printf("rwx"); break;
        }
        printf("\t0x%04x", phdr[i].p_offset);
        printf("\t0x%08x", phdr[i].p_vaddr);
        printf("\t0x%08x", phdr[i].p_paddr);
        printf("\t%4u bytes", phdr[i].p_filesz);
        printf("\t%4u bytes", phdr[i].p_memsz);
        printf("\t%4u bytes", phdr[i].p_align);
        putchar('\n');
    }
}

//----------------------------------------------------------------------------------
//-----------------------------main函数---------------------------------------------
//----------------------------------------------------------------------------------

void print64(void (*fun)(Elf64_Ehdr* ehdr)) {
    
         //打印到控制台和txt文件
    static Elf64_Ehdr ehdr[1];
    freopen("CON", "w", stdout);
    fun(ehdr);
    freopen("result.txt", "a+", stdout);
    fun(ehdr);
    freopen("CON", "w", stdout);
}

void print32(void (*fun)(Elf32_Ehdr* ehdr)) {
    
         //打印到控制台和txt文件
    static Elf32_Ehdr ehdr[1];
    freopen("CON", "w", stdout);
    fun(ehdr);
    freopen("result.txt", "a+", stdout);
    fun(ehdr);
    freopen("CON", "w", stdout);
}

int main() {
    
    
    char str[20];
    printf("请输入ELF文件名:\n");
    scanf("%s", str);
    fname = &str[0];
    fp = fopen(fname, "rb");
    if(fp == NULL) {
    
    
        printf("%s not exit\n", fname);
        exit(1);
    }
    memset(str, 0, sizeof(str));
    fread(str, 1, 5, fp);
    if(str[0] != 0x7f || str[1] != 'E' || str[2] != 'L' || str[3] != 'F') {
    
    
        printf("%s is not an ELF file\n", fname);
        exit(1);
    }
    OStype = str[4] == 1 ? 32 : 64;     //判断elf文件为32位还是64位
    printf("魔数检验通过,该文件为%d位ELF文件\n", OStype);

    freopen("result.txt", "w", stdout);
    printf("");    //清空文件内容

    if(OStype == 64) {
    
    
        print64(ELF_header_64_parse);
        print64(section_header_64_parse);
        print64(program_header_64_parse);
    } else {
    
    
        print32(ELF_header_32_parse);
        print32(section_header_32_parse);
        print32(program_header_32_parse);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45228537/article/details/107020572