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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章