2019年3月28日木曜日

ELF弄りメモ Part.1 ~各セクションの分割~

Part0に書いたようにELFファイルはセクションごとに役割が決まってるので、それごとに分けてあげた方が弄りやすいのではと思ったので分割してみることにしました。分割するためにこのようなプログラムを書きました。これを書いたときに躓いたことを記しておきます。

0. 型

次項から構造体を書いていきますが、それに使われている型は以下の通りです。
/* 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;

/* 64-bit ELF base types. */
typedef __u64 Elf64_Addr;
typedef __u16 Elf64_Half;
typedef __s16 Elf64_SHalf;
typedef __u64 Elf64_Off;
typedef __s32 Elf64_Sword;
typedef __u32 Elf64_Word;
typedef __u64 Elf64_Xword;
typedef __s64 Elf64_Sxword;

1. ELFヘッダ

elf.hによって以下のように定義されています。
typedef struct elf32_hdr{
  unsigned char e_ident[EI_NIDENT];
  Elf32_Half e_type;
  Elf32_Half e_machine;
  Elf32_Word e_version;
  Elf32_Addr e_entry;  /* Entry point */
  Elf32_Off e_phoff;
  Elf32_Off e_shoff;
  Elf32_Word e_flags;
  Elf32_Half e_ehsize;
  Elf32_Half e_phentsize;
  Elf32_Half e_phnum;
  Elf32_Half e_shentsize;
  Elf32_Half e_shnum;
  Elf32_Half e_shstrndx;
} Elf32_Ehdr;

typedef struct elf64_hdr {
  unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
  Elf64_Half e_type;
  Elf64_Half e_machine;
  Elf64_Word e_version;
  Elf64_Addr e_entry;  /* Entry point virtual address */
  Elf64_Off e_phoff;  /* Program header table file offset */
  Elf64_Off e_shoff;  /* Section header table file offset */
  Elf64_Word e_flags;
  Elf64_Half e_ehsize;
  Elf64_Half e_phentsize;
  Elf64_Half e_phnum;
  Elf64_Half e_shentsize;
  Elf64_Half e_shnum;
  Elf64_Half e_shstrndx;
} Elf64_Ehdr;
ELFのマジックは0x7fELFで、次に32bitなら0x01、64bitなら0x02が来ます。私は5バイト分読み込んでELFファイルであるか、32ビットか64ビットであるかを判定しました。
他のメンバはPart.0の参考サイトが役にたちます。
個人的に記しておきたいのはe_shstrndxで、これはセクションヘッダの文字列セクションがセクションの何番目かを示しています。したがって後述するセクションヘッダテーブルをセクションヘッダの配列として扱い、これを使うことで簡単にアクセスできます。

2. セクションヘッダ

以下のように定義されています。
typedef struct elf32_shdr {
  Elf32_Word sh_name;
  Elf32_Word sh_type;
  Elf32_Word sh_flags;
  Elf32_Addr sh_addr;
  Elf32_Off sh_offset;
  Elf32_Word sh_size;
  Elf32_Word sh_link;
  Elf32_Word sh_info;
  Elf32_Word sh_addralign;
  Elf32_Word sh_entsize;
} Elf32_Shdr;

typedef struct elf64_shdr {
  Elf64_Word sh_name;  /* Section name, index in string tbl */
  Elf64_Word sh_type;  /* Type of section */
  Elf64_Xword sh_flags;  /* Miscellaneous section attributes */
  Elf64_Addr sh_addr;  /* Section virtual addr at execution */
  Elf64_Off sh_offset;  /* Section file offset */
  Elf64_Xword sh_size;  /* Size of section in bytes */
  Elf64_Word sh_link;  /* Index of another section */
  Elf64_Word sh_info;  /* Additional section information */
  Elf64_Xword sh_addralign; /* Section alignment */
  Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
ここでつまずいたのは各セクション名の取得です。sh_nameはインデックス値を保持していますが、このインデックス値は.shstrtab先頭から目的の文字列の先頭までのバイト数で、文字列は0x00までです。この項がわかりやすいです。
コードで書くならe_shstrndx番目のセクションヘッダを読み込み、そのsh_offset + 任意のセクションヘッダのsh_nameで名前が取得できます。

いかがでしょうか

自分メモ感が強いので読みにくくてすいません。分割で結構重かったのでやる気が失せかけていますが、次は.dynamicや.sym、.rel/.relaの構造を見ていきたいと思っています。その後セクションを弄るPythonコードをそれぞれ書き、ヘッダ類調整して固めて動作確認、Pythonコードリファクタリングを行いたいと思っています。先が長い…

0 件のコメント:

コメントを投稿