Tuuna Computer Science

[ELF] 리눅스 Debugger(디버거)만들기 본문

Debugger

[ELF] 리눅스 Debugger(디버거)만들기

GuTTe 2018. 12. 2. 23:48


elf header와 elf header의 내용을 바탕으로 섹션 헤더를 출력하는데 성공했다. 

이젠 이 섹션 헤더를 바탕으로 .symtab 섹션으로 가서 심볼들을 체크해봐야 겠다. 

int check_elf(int file)
{
    ssize_t check;
    char value[16];
    int i;
    int magic=1;
    lseek(file, 0, SEEK_SET);
    for(i=0;i<16;i++)
    {
        read(file, &value[i], 1);
    }
    if(value[0] == 0x7F && value[1] == 0x45 && value[2] == 0x4c && value[3] == 0x46)
    {
        magic = 0;
    }
    if(magic)
    {
        return 1;
    }
    return 0;
}    
check_elf.h (파일이 elf인지 아닌지 확인한다.)

/*
// 32-bit ELF base types.  
typedef __u32 Elf32_Addr;
typedef __u16 Elf32_Half;
typedef __u32 Elf32_Off;
typedef __s32 Elf32_Sword;
typedef __u32 Elf32_Word;
*/
#define EL_NIDENT 16
/*
typedef struct elf32_hdr{
    unsigned char e_ident[EL_NIDENT]; //=> 파일이 object파일임을 나타냄. 기계 독립적인 데이터 제공
    Elf32_Half    e_type;             //=> Object 파일의 타입을 표시
    Elf_Half      e_machine;          //=> 아키텍쳐 정보를 표시 (32인지 64인지)
    Elf_Word      e_version;          //=> Object 파일의 버전 정보
    Elf_Addr      e_entry;            //=> 가상주소 (시작점 ), 프로그램을 어디로
    Elf_Off       e_phoff;            //=> 프로그램 헤더 파일 옵셋을 나타냄
    Elf_Off       e_shoff;            //=> 섹션 헤더 테이블의 파일 옵셋을 나타냄
    Elf32_Word    e_flags             //=> 사용 X
    Elf32_Half    e_ehsize;           //=> ELF 헤더의 크기를 가짐
    Elf32_Half    e_phentsize;        //=> 파일의 프로그램 헤더파일에 있는 한 엔트리의 크기를 나타냄
    Elf32_Half    e_phnum;            //=> 프로그램 헤더 테이블에 들어있는 모든 엔트리의 수를 나타냄
    Elf32_Half    e_shentsize;        //=> 섹션 헤더의 크기를 나타냄.
    Elf32_Half    e_shnum;            //=> 섹션 헤더 테이블에 있는 엔트리의 수를 가짐.
    Elf32_Half    e_shstrndx;         
    //=> 섹션의 이름을 나타내는 스트링의 테이블과 관련된 엔트리의 섹션 헤더 테이블 인덱스를 가진다.
} Elf32_Ehdr;
*/
Elf32_Ehdr elf_header(int file)
{
    Elf32_Ehdr elf32;
    //Elf64_Ehdr elf64;

    ssize_t check;
    int i;
    
    lseek(file, 0, SEEK_SET);    
    read(file, &elf32, sizeof(elf32));

    printf("magic:    ");
    for(i=0;i<16;i++)
        printf("%X ", elf32.e_ident[i]);
    printf("\n");

    printf("%c[1;31m",27);
    if(elf32.e_ident[4] == 1)
        printf("Class:                    ELF32\n");
    else if(elf32.e_ident[4] == 2)
    {
        printf("Class:                    ELF64\n");
        printf("Sorry! debugger want 32bit binary!\n");
        exit(1);
        //return -1;
    }
    else
        perror("Error!");
    printf("%c[0m",27);

    if(elf32.e_ident[5] == 1)
        printf("Data:                    2's complement, little endian\n");
    else if(elf32.e_ident[5] == 2)
        printf("Data:                    1's complement, big endian\n");
    else
        perror("Error!");

    if(elf32.e_ident[6] == 1)
        printf("Version:                1 (current)\n");
    else if(elf32.e_ident[6] == 2)
        printf("Version:                setting....\n");
    else
        perror("Error!");

    printf("OS/ABI:                    UNIX - System     V\n");
    printf("ABI Version:                0\n");
    
    printf("%c[1;31m",27);
    if(elf32.e_machine ==3)
        printf("Machine:                Intel 80386\n");
    else if(elf32.e_machine == 62)
        printf("Machine:            Advance Micro Devices X86-64\n");
    else
        printf("Machine:            setting.....\n");
    printf("%c[0m",27);

    if(elf32.e_type == 0)
        printf("Type:                               NONE\n");
    else if(elf32.e_type == 1)
        printf("Type:                              REL\n");
    else if(elf32.e_type == 2)
        printf("Type:                                   EXEC\n");
    else if(elf32.e_type == 3)
        printf("Type:                               DYN\n");
    else if(elf32.e_type == 4)
        printf("Type:                              CORE\n");
    else
        printf("Type:                setting....\n");

    printf("Version:                0x1\n");
    //printf("=========================================\n");

    printf("%c[1;31m",27);
    printf("Entry Point Address(.text):        0x%X\n",elf32.e_entry);
    printf("%c[0m",27);
    printf("Flags:                    %X\n",elf32.e_flags);
    printf("Start of program headers:        0x%X\n",elf32.e_phoff);
    printf("Start of section headers:        0x%X\n",elf32.e_shoff);
    printf("Size of program headers:        %X (bytes)\n",elf32.e_phentsize);
    printf("Size of section headers:        %X (bytes)\n",elf32.e_shentsize);
    printf("Number of program headers:        %X\n",elf32.e_phnum);
    printf("Size of ELF Header:            %X (bytes)\n",elf32.e_ehsize);
    printf("Number of section headers:        %X\n",elf32.e_shnum);
    printf("Section header string table index:    %d\n",elf32.e_shstrndx);

    return elf32;
}
(elf_header.h)(ELF HEADER를 출력한다.) 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <elf.h>
#include <linux/types.h>
#include "check_elf.h"
#include "elf_header.h"

int elf_section_header(Elf32_Ehdr elf, int file);

int main(int argc, char** argv)
{
    Elf32_Ehdr elf;
    int return_check;
    int file;
    if(argc!=2)
    {
        perror("Error: ");
        exit(1);
    }
    file = open(argv[1], O_RDONLY);
    if(file == -1)
    {
        perror("Error: ");
        exit(1);
    }
    return_check=check_elf(file); //elf인지 확인한다.
    //printf("%d\n", return_check);
    if(return_check)
    {
        printf("This is not ELF\n");
        close(file);
        exit(1);
    }
    elf = elf_header(file); //elf의 header를 출력한다.
    elf_section_header(elf, file); //elf의 섹션헤더를 출력한다.
    close(file);
    return 0;
}

int elf_section_header(Elf32_Ehdr elf, int file)
{
    char sym[29][30];
    char name;
    //const char *const name;
    Elf32_Shdr section_header[30]; //일단 30개 해놓자  
    int i;
    int j=0;
    lseek(file, elf.e_shoff, SEEK_SET);
    for(i=0;i<elf.e_shnum;i++)
    {
        read(file, &section_header[i], sizeof(section_header[1]));
    }
    printf("\nThere are %d section headers, starting at offset %X\n", elf.e_shnum, elf.e_shoff);

    printf("\nSection Headers:\n");
    printf("[Nr] Name\t\t\tAddr\tOff\tSize\n");
    
    //lseek(file, section_header[28].sh_offset, SEEK_SET);

    for(i=0;i<elf.e_shnum;i++)
    {
        lseek(file, section_header[28].sh_offset+section_header[i].sh_name,SEEK_SET);
        while(read(file, &name, 1))
        {
            if(name == '\0')
            {
                break;
            }
            sym[i][j] = name;
            j++;    
        }
        sym[i][j] = '\0';
        j=0;
    }

    for(i=0;i<elf.e_shnum;i++)
    {
        printf("[%-2d] %s\t\t\t0x%X\t0x%X\t%X\n",i, sym[i], section_header[i].sh_addr, section_header[i].sh_offset, section_header[i].sh_size);
    }
}

//이름은 처음 파일 옵셋에서 sh_name을 더하고, sh_offset을 더한건가.
elf_parser.c ( 여기서 하나를 헤더파일화 안했는데 elf_section_header 함수는 섹션 헤더를 출력하는 함수이다.) 

섹션헤더를 출력하면서 좀 힘들었던점이 sh_name의 값이 파일 옵세이 아니라 shstrtab의 옵셋이였던 것이다;; 

그래서 name을 구할려면 shstrtabl의 오프셋에서 sh_name을 더해야 했던것 

stack overflow에서 왜 이부분을 mmap 시스템콜을 사용해서 하는지 알겠다.. 

그냥 파일로 읽을려하니 널까지 읽어야하고 등등 처리할게 많아지네 

내일은 .symtab을 바탕으로 사용자가 원하는 함수에 대한 명령코드를 출력하는 것을 목표로하자

그리고 이 명령코드를 바탕으로 디스 어셈...ㅎ


Comments