ELFを解析してみる(1)

世の中には色々な実行形式があります。ELF, Coff, PE, MachO, …そのなかでも一番広く用いられている ELF に目を向け、その構造を解析しようとしています。今の時代なので 64bit 版をやる予定です。まだうまくいって無いですがね。。。

(今のところ)参考にしている文献等

お金はなるべく出したくないので書籍は見ていません。オライリーのタダ PDF 等があればそれを見たいとは思いますが、まだ探してもないです。
という訳で以下を参考にしています。

使うツール・環境

Arch Linux

Parallels Desktop に入れて、Mac 側から ssh 接続をして使います。

Vim

単純にアセンブリコードを記述するテキストエディタとして使用します。

NASM

愛用のアセンブラです。Vim でアセンブリコードを書いたら以下のコマンドでバイナリ生成をします。

$ nasm -f bin asm.asm

xxd

できたバイナリファイルを見るために。一方で hexdump は2バイトごとに勝手にひっくり返してくるので嫌いです。

とりあえず分かってきたこと

  • ELF ヘッダの 0x7F, "ELF" だけ記述すればとりあえず file コマンドで ELF だと認識される。
  • 続いて 2, 1, 1 とすると ELF 64-bit LSB と出る。
  • 続いて 0, 0 とすると ELF 64-bit LSB (SYSV) と出る。
  • 続いてゼロ埋めをし、2, 62 とすると ELF 64-bit LSB executable, x86-64, (SYSV) とでる。
    • 楽しい。
  • nasm で $ は現在のアドレス、$$ はこのセクションの先頭アドレスを指す。

  • 64bit では dw: 2byte, dd: 4byte, dq: 8byte である。
  • その他はまだわからない。一応それっぽいファイルを作っても Segmentation Fault になる。
    • 実行形式エラーにはならないから、一応はそれっぽい ELF ができている?
    • org のアドレスが分からん。

ということで、暇を見つけて ELF の調査を行っていきます。目指せ、バイナリアン。

ちなみに今のところ適当に書き上げた ELF の実行ファイルはこちらです。上手く動きませんけど。

bits 64
org 0x400000
; e_ident[]
elf_header:
db 0x7F,"ELF" ; magic numbers
db 2, 1, 1 ; 64bit, little endian, current ELF version
db 0, 0 ; No ABI, ABI version 0
db 0, 0, 0, 0, 0, 0, 0 ; zero-padding
dw 2, 62 ; executable file, x64 machine
dd 1 ; current object version
dq _start
dq program_header - $$ ; program header offset
dq 0 ; section header offset
dd 0 ; flags(zero)
dw elf_header_size
dw program_header_size
dw 1 ; progra header number
dw 0, 0, 0 ; section header(none)

elf_header_size equ $ - elf_header

program_header:
dd 1 ; loadable segment(what!?)
dd 0 ; segment offset(what!?)
dq $$ ; first segment virtual address
dq 0 ; reserved
dq filesize, filesize
dq (1 << 2) | (1 << 0) ; readable & executable segment
dq 0x1000 ; align(what!?)

program_header_size equ $ - program_header

_start:
    mov ax, 60
    mov di, 42
    syscall

filesize equ $ - $$