3——Linux二进制分析——学习——简易ELF解析器

在第二章的结尾,作者贴上了一段ELF解析器的代码,我进行了反复的尝试,加深了对ELF格式的理解。这个其实就是一个简单的readelf,打印所有的section header名称和在可执行程序中的虚拟地址,program header的地址。

为了便于理解,在某些地方我加入了自己的注释。例如mmap()处的注释,line92之前的3行对e_shstrndx的解释,还有开头对3个结构体的解释。

之前在看《深入理解Linux内核》的时候看到过mmap(),这次再次遇到才真正查了一下mmap()的具体功能,也知识有了肤浅的认识。我没有想到的是共享内存居然使用的就是mmap()技术实现的,大家对同一块内存进行映射(map),大家就能操作相同一块内存,可读也可以写,这样也就实现了进程间通信。除了共享内存外还有一个更广的应用场景,就是划拨一块硬盘作为内存使用,也是这个思想。

下面我把代码贴出来,就是一个简单的main()函数,一点也不复杂。按照第一行的注释进行编译即可。代码里用到的结构体都在elf.h系统头文件中定义。任何一个完整的linux系统中都存在,在linux内核代码中也存在。如果使用window编辑的话,建议拷贝一份elf.h到自己的工程中。

/* elfparse.c - gcc elfparse.c -o elfparse */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <elf.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    printf("XBter!\n");

    int fd, i;
    uint8_t *mem;
    struct stat st;
    char *StringTable, *interp;

    Elf32_Ehdr *ehdr;//ELF file header
    Elf32_Phdr *phdr;//program header
    Elf32_Shdr *shdr;//section header

    if(argc < 2){
        printf("Usage: %s <executable>\n", argv[0]);
        exit(0);
    }

    if((fd = open(argv[1], O_RDONLY)) < 0){
        perror("open");
        exit(-1);
    }

    if(fstat(fd, &st) < 0){
        perror("fstat");
        exit(-1);
    }

    /* Map the executable into memory */
    mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if(mem == MAP_FAILED){/*mmap may have many fuctions. I read*/
        perror("mmap");/*a blog, share memory, Changing disk to*/
        exit(-1);/*memory when memory is not enough may all use*/
    }/*mmap(). This is real mapping. This is the blog address: */
    /*https://blog.csdn.net/yanerhao/article/details/77829191  */

    /**
     * The initial ELF Header starts at offset 0
     * of our mapped memory
     */
    ehdr = (Elf32_Ehdr*)mem;

    /**
     * The shdr table and phdr table offsets are
     * given by e_shoff and e_phoff members of the
     * ELF32_Ehdr
     */
    phdr = (Elf32_Phdr*)&mem[ehdr->e_phoff];
    shdr = (Elf32_Shdr*)&mem[ehdr->e_shoff];

    /**
     * Check to see if the ELF magic (The first 4 bytes)
     * match up as 0x7f E L F (0x7f 45 4c 46)
     */
    if(mem[0] != 0x7f && strcmp(&mem[1], "ELF")){
       fprintf(stderr, "%s is not an ELF file\n", argv[1]);
       exit(-1);
    }

    /**
     * We are only parsing executables with this code
     * so ET_EXEC marks an executables.
     */
    if(ehdr->e_type != ET_EXEC){
        fprintf(stderr, "%s is not an executable\n", argv[1]);
        exit(-1);
    }

    printf("Program Entry point: 0x%x\n", ehdr->e_entry);

    /**
     * We find the string table for the section header
     * names with e_shstrndx which gives the index of
     * which section holds the string table.
     * e_shstrndx is the index of section .shstrtab
     * .shstrtab holds all sections's name, just a strings
     * ending with /0.
     */
     StringTable = &mem[shdr[ehdr->e_shstrndx].sh_offset];
     /**
      * Print each section header name and address.
      * Notice we get the index into the string table
      * that contains each section header name with
      * the shdr.sh_name member.
      */
     printf("Section header total:%d list:\n\n", ehdr->e_shnum);
     for(i = 0; i < ehdr->e_shnum; i++)
         printf("[%d]%s: 0x%x\n", i, &StringTable[shdr[i].sh_name], shdr[i].sh_addr);

     /**
      * Print out each segment name, and address.
      * Except for PT_INTERP we print the path to
      * the dynamic linker (Interpreter).
      */
     printf("\nProgram header total:%d list:\n\n", ehdr->e_phnum);
     for(i = 0; i < ehdr->e_phnum; i++){
//         printf("phdr[%d].p_type=%d\n", i, phdr[i].p_type);
         switch(phdr[i].p_type){
             case PT_LOAD:
                 /**
                  * We know that text segment starts
                  * at offset 0. And only one other
                  * possible loadable segment exists
                  * which is the data segment.
                  */
                 if(phdr[i].p_offset == 0)
                     printf("Text segment: 0x%x\n", phdr[i].p_vaddr);
                 else
                     printf("Data segment: 0x%x\n",phdr[i].p_vaddr);
             break;
             case PT_INTERP:
                 interp = strdup((char*)&mem[phdr[i].p_offset]);
                 printf("Interpreter: %s\n", interp);
             break;
             case PT_NOTE:
                 printf("Note segment: 0x%x\n", phdr[i].p_vaddr);
             break;
             case PT_DYNAMIC:
                 printf("Dynamic segment: 0x%x\n", phdr[i].p_vaddr);
             break;
             case PT_PHDR:
                 printf("Phdr segment: 0x%x\n", phdr[i].p_vaddr);
             break;
         }
     }

    exit(0);
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值