Hello World を作った話
小山高専 3年 mitsu
お前は誰だ?
• mitsu (@zunzun_meow)
• CTF と 乱数が好き
• 好きな動物は猫
• プログラミング初心者
宣伝
• EE 専ブースでコンパイラを展示してます!良ければ来てね
• 明日はシューティングも展示します~ (今日サブ機を忘れてしまっ
た)
Hello World !!!!
世界にあいさつした
色んな言語で書けるけど…
||環境構築の壁 ||
結局こうなった
7f 45 4c 46
手打ちバイナリ
せっかくの 20 分枠なので簡単に解説
ELF #とは
• Executable and Linkable Format の略
• Linux 等で用いられる実行ファイルの形式の一つ(Windows でいう
hoge.exe)
実行ファイルが実行されるまでの流れ
1. ローダというプログラムが動きだす
2. 実行部分等をファイルの中身に応じてメモリに読み込む
3. 実行
ELF ファイルの構造
ELF ヘッダ
プログラムヘッダテーブル
セクション 1
セクション 2
…
セクション n
セクションヘッダテーブル
セグメント 1
セグメント n
ELF ヘッダ
• 自分が ELF ファイルであることを示す文字列
• これがないとただのテキストファイルと見分けがつかない
• あとは互換性を考えたり、後述のプログラムヘッダの位置を記述し
たり、とにかく雑多なデータ
セクション
• 種類ごとに細かく分けられたデータの単位
• セクション全体はセクションヘッダテーブルで管理される
• .data や .text など予約された特別なセクションが存在
• リンカのための構造で, 無くても動く. 手打ちバイナリにおいて色々
なことをしようとするときセクションの情報があったほうが便利
セクションの例
• .text
• 機械語のコードが格納されているセクション
• .data
• 初期化される変数を格納するセクション
• .bss
• 初期化されない変数を格納するセクション
セクション名
• セクションはそれぞれ名前を持ち, それは .shstrtab セクションに格
納されている
• .shstrtab にはヌル終端の文字列が複数個格納され, n 番目の文字
列が n 番目のセクション名に対応する. 当然自分自身のセクション
名も含む
セグメント
• ELF ヘッダを除くファイル全体を属性で分けたもの
• セグメント全体はプログラムヘッダテーブルで管理される
• OS のローダはセグメントの情報をもとにメモリマップを行う
• セクションを纏めてセグメントと仕立て上げることで, セクションを纏
めてメモリ上に展開することができる
Let’s バイナリ!
方針
ELFヘッダを
書く
プログラム
ヘッダを書く
実行部分を
書く
何もしない
プログラム
セクションを
書く
セクション
ヘッダを書く
Hello World !
まずは何もしないファイルを作ってみる
目標
これだけ. セクションはなし
機械語コードは exit(1) のみ
ELF ヘッダ
プログラムヘッダテーブル
機械語コード
方針
ELFヘッダを
書く
プログラム
ヘッダを書く
実行部分を
書く
何もしない
プログラム
セクションを
書く
セクション
ヘッダを書く
Hello World !
ELF ヘッダを書いていく
• 全体で 64 byte
• ファイルの先頭に、仕様通りの順番で書けばいいだけ
• 項目 1 から 7 は全部合わせて e_ident というまとまり
1. マジックナンバー (e_ident::EI_MAG)
• 全体で 4 byte
• 0x7f 0x45 0x4c 0x46
‘E’‘L’‘F’
2. クラス (e_ident::EI_CLASS)
• 1 byte
• 何ビットの ELF ファイルかを表す
• 64bit なので 0x2
3. データ (e_ident::EI_DATA)
• 1 byte
• データフォーマットを表す
• 2 の補数表現でリトルエンディアンなので 0x1
4. バージョン (e_ident::EI_VERSION)
• 1 byte
• 現行のバージョンということで必ず 0x1
5. ABI (e_ident::EI_OSABI)
• 1 byte
• System V ABI がいいので 0x0
6. ABI のバージョン (e_ident::EI_ABIVERSION)
• 1 byte
• EI_OSABI が System V ABI なのでここは 0x0 でいい
7. パディング (e_ident::EI_PAD)
• 7 byte
• e_ident 全体が 16 byte で終わると奇麗なので余った部分を 0x0 で
埋める
8. e_type
• オブジェクトファイルタイプ
• 2 byte
• 実行可能なら問題ないため 0x2
9. e_machine
• アーキテクチャ
• 2 byte
• X86_64 で動かしたいため 0x3e
10. e_version
• ファイルバージョン
• 4 byte
• 0x1 じゃないとだめ
11. e_entry
• エントリポイントを決めます
• 8 byte
• とりあえず 0x400078 くらいでいいや
12. e_phoff
• プログラムヘッダのファイル先頭からのオフセット
• 8 byte
• ELF ヘッダの直後に置きたいので ELF ヘッダのサイズにします ->
0x40
13. e_shoff
• セクションヘッダテーブルのファイル先頭からのオフセット
• 8 byte
• 最初はセクションを使わないので 0x0 にしておく
14. e_flags
• 4 byte
• 今のところ使われてないので 0x0
15. e_ehsize
• ELF header のサイズ
• 2 byte
• 0x40
16. e_phentsize
• プログラムヘッダテーブルのエントリのサイズ
• 2 byte
• 0x38 でいい
17. e_phnum
• プログラムヘッダテーブルのエントリの数
• 2 byte
• 何もしないプログラムではとりあえず 0x1
18. e_shentsize
• セクションヘッダテーブルのエントリのサイズ
• 2 byte
• とりあえず使わないので 0x0
19. shnum
• セクションヘッダテーブルのエントリの数
• 2 byte
• セクションは使わないので 0x0 にしておく
20. e_shstrndx
• 何番目のセクションが .shstrtab セクションか
• 2 byte
• セクションは使わないので 0x0
入力した
readelf でヘッダの情報を見てみる
ヘッダがちゃんと入力できてる!
プログラムヘッダをまだ入力してないのでエラーは吐くし実行もできないが…
方針
ELFヘッダを
書く
プログラム
ヘッダを書く
実行部分を
書く
何もしない
プログラム
セクションを
書く
セクション
ヘッダを書く
Hello World !
Program header を書いていく
• 全体で 56 byte
• これも ELF ヘッダ直後から、仕様通りの順番で書けばいいだけ
• 実際に実行されるコードが格納されているセグメントの情報を書き
足していく
1. p_type
• セグメントの種類
• 4 byte
• ちゃんとメモリにマップされロードされたいので 0x1
2. p_flags
• セグメントのフラグ
• 4 byte
• 実行可能かつ読み込み可能であってほしいので 0b101
3. p_offset
• ファイル先頭からのセグメントのオフセット
• 8 byte
• セグメントの情報はプログラムヘッダの直後に書きたい
-> この値は ELF ヘッダのサイズとプログラムヘッダのサイズの和
0x40 + 0x38 = 0x78
4. p_vaddr
• セグメントの先頭バイトのメモリの仮想アドレス
• 8 byte
• 実行部分のセグメントなのでエントリポイントで大丈夫
• 0x400078
5. p_paddr
• セグメントの物理アドレス
• 8 byte
• 普通の実行ファイルなので仮想アドレスと同じで大丈夫
• 0x400078
6. p_filesz
• セグメントのイメージサイズ
• 8 byte
• このセグメントには実際に実行されるコードが格納されるため, この
サイズはそのコードのサイズ
• コードは後述. 0x12
7. p_memsz
• セグメントが実際にメモリにマップされるときのサイズ
• 8 byte
• メモリにマップしたいのは実行部分だけなので 0x12
• ファイルには書かれていないものがメモリ領域を確保したいものがある場
合, p_filesz より値が大きくなる(.bss セクションを含むなど)
8. p_align
• アラインメント
• 8 byte
• 0x1000 でいい
入力した
readelf でセグメントの情報を見てみる
この調子で実行部分も書こう!
方針
ELFヘッダを
書く
プログラム
ヘッダを書く
実行部分を
書く
何もしない
プログラム
セクションを
書く
セクション
ヘッダを書く
Hello World !
• 実行部分はプログラムヘッダの直後に書く
• exit(1) をするコードのバイト文字列は
“¥xb8¥x01¥x00¥x00¥x00¥xbb¥x01¥x00¥x00¥x00¥xcd¥x80”
入力した
実行してみる
動いたよ!
"¥x31¥xc0¥x48¥xbb¥xd1¥x9d¥x96¥x91¥xd0¥x8c¥x97¥xff¥x48¥xf7
¥xdb¥x53¥x54¥x5f¥x99¥x52¥x57¥x54¥x5e¥xb0¥x3b¥x0f¥x05"
Hello World へ!
目標
セクションが増えた
ELF ヘッダ
プログラムヘッダテーブル
.text (機械語コード)
.rodata
.shstrtab
セクションヘッダテーブル
文字列リテラルの扱い
• “Hello World !” という文字列リテラルは実行ファイルに組み込ま
れ, .rodata セクションに格納される.
• 実際, 実行ファイルをテキストエディタで開くと見れる
.rodata
• その名の通り Read only data
• 定数や文字列リテラルが格納される
このセクションを作ってあげればいい!
Hello World 用に実行部分を書きなおす
Hello World 表示用
• この後追加する .rodata のアドレスによって “Hello World” の位置
が変わる
• 今回は 0x40009f の位置に置かれるため
“¥x48¥xc7¥xc2¥x0d¥x00¥x00¥x00¥x48¥x8d¥x35¥x19¥x00¥x00¥x00¥x4
8¥xc7¥xc0¥x01¥x00¥x00¥x00¥x48¥xc7¥xc7¥x00¥x00¥x00¥x00¥x0f¥x05
¥x48¥xc7¥xc0¥x3c¥x00¥x00¥x00¥x0f¥x05”
mov rdx, 0xd
lea rsi, [0x40009f]
mov rax, 1
mov rdi, 0
syscall
mov rax, 0x3c
syscall
方針
ELFヘッダを
書く
プログラム
ヘッダを書く
実行部分を
書く
何もしない
プログラム
セクションを
書く
セクション
ヘッダを書く
Hello World !
各セクションを実行部分の直後に書き連ねていく
.rodata セクション
“Hello World¥n¥x0”
-> “¥x48¥x65¥x6c¥x6c¥x6f¥x20¥x57¥x6f¥x72¥x6c¥x64¥x0a¥x00”
.shstrtab セクション
“¥x00.shstrtab¥x00.text¥x00.rodata¥x00”
->
”¥x00¥x2e¥x73¥x68¥x73¥x74¥x72¥x74¥x61¥x62¥x00¥x2e¥x74¥x65¥x78¥x7
4¥x00¥x2e¥x72¥x6f¥x64¥x61¥x74¥x61¥x00”
入力した
ELF ヘッダの修正
e_shoff
• .rodata の直後にセクションヘッダテーブルを書きたい
• e_shoff の値を ELF ヘッダのサイズ + プログラムヘッダテーブルのサイズ
+ セクションの合計サイズ にすればいい
0x40 + 0x38 + 0x27 + 0x19 + 0xd = 0xc5
e_shentsize
• セクションヘッダテーブルのサイズは 0x40
よってここも 0x40
e_shnum
• .text, .shstrtab, .rodata に加え NULL セクションというものが必要
よって 0x4
e_shstrndx
• NULL セクションの次のセクションを .shstrtab にする
よって 0x1
セクションの順番は配置によらない
入力した
最後にセクションヘッダテーブル
セクションヘッダテーブル
• 各セクションに一つずつ存在
• 10 個の項目の集まりで構成されている
• 0x40 byte
1. sh_name
• .shstrtab 内の何文字目からがこのセクションの名前かを示す
• 4 byte
2. sh_type
• セクションの内容を示す
• 文字列テーブルであることを示したり, プログラム部分を示したり
• 4 byte
3. sh_flags
• 書き込みやメモリの占有の権限
• 8 byte
4. sh_addr
• メモリイメージ内でのこのヘッダが示すセクションのアドレス
• 8 byte
5. sh_offset
• このヘッダが示すセクションのファイル先頭からのオフセット
• 8 byte
6. sh_size
• このヘッダが示すセクションの大きさ
• 8 byte
7. sh_link
• セクションの種類によって変わるパラメータ
• このヘッダが示すセクションが関連するテーブルとのインデックスの
関係を表す
• 今回は使わない
• 4 byte
8. sh_info
• セクションの種類によって変わる追加情報
• 今回は使わない
• 4 byte
9. sh_addralign
• アドレスのアライン
• .text や .rodata などはアラインが必要
• 8 byte
10. sh_entsize
• このヘッダが示すセクションに固定サイズのエントリテーブルが格
納される場合設定する
• 今回は使わない
• 8 byte
今回使うセクションのヘッダたち
NULL セクション
• sh_name: 0x0
• sh_type: 0x0
• sh_flags: 0x0
• sh_addr: 0x0
• sh_offset: 0x0
• sh_size: 0x0
• sh_link: 0x0
• sh_info: 0x0
• sh_addralign: 0x0
• sh_entsize: 0x0
.shstrtab セクション
• sh_name: 0x1
• sh_type: 0x3
• sh_flags: 0x0
• sh_addr: 0x0
• sh_offset: 0xac
• sh_size: 0x19
• sh_link: 0x0
• sh_info: 0x0
• sh_addralign: 0x1
• sh_entsize: 0x0
.text セクション
• sh_name: 0xb
• sh_type: 0x1
• sh_flags: 0b0110
• sh_addr: 0x78
• sh_offset: 0x78
• sh_size: 0x27
• sh_link: 0x0
• sh_info: 0x0
• sh_addralign: 0x10
• sh_entsize: 0x0
.rodata セクション
• sh_name: 0x11
• sh_type: 0x1
• sh_flags: 0b0010
• sh_addr: 0x9f
• sh_offset: 0x9f
• sh_size: 0xd
• sh_link: 0x0
• sh_info: 0x0
• sh_addralign: 0x4
• sh_entsize: 0x0
これらをそのまま下につなげればおk
入力した
実行してみる
完成!
ELF の手打ちは楽しい!
コンパイラとアセンブラとローダを作るか
結局ローダはOSにロードされる
OS …
ご清聴ありがとうございました

Hello world make