• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
ELFの動的リンク
 

ELFの動的リンク

on

  • 4,490 views

スタート低レイヤー#2で発表した資料です。

スタート低レイヤー#2で発表した資料です。
http://partake.in/events/1bed8969-5bc9-4f02-bc19-2698cb5577a3

Statistics

Views

Total Views
4,490
Views on SlideShare
2,681
Embed Views
1,809

Actions

Likes
10
Downloads
19
Comments
0

7 Embeds 1,809

http://7shi.hateblo.jp 1660
https://twitter.com 107
http://neo-himajin.mydns.jp 26
http://cloud.feedly.com 11
http://www.feedspot.com 3
http://feedly.com 1
http://translate.googleusercontent.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    ELFの動的リンク ELFの動的リンク Presentation Transcript

    • スタート低レイヤー #2 ELFの動的リンク 七誌
    • お断わり • ELFの仕様書を参考に実験したことを基にした書き下 ろしです。要約ではありません。 • 独断と偏見で書いています。 • コマンドや簡単なプログラムでELFをいじりながら、動 的リンクの仕組みを追います。 • NetBSDのソースは読みません。 • 折に触れてNetBSDには言及します。 • Pythonで書いたサンプル実装を使います。 • https://bitbucket.org/7shi/elf-dyn • 使用するコンパイラはi386-elf-gccです。 • binutilsとgccを --target=i386-elf でビルド
    • 七誌について
    • 自己紹介 • 池袋バイナリ勉強会を主催(後述) • 歴史的発展に興味があり、UNIX V1からNetBSDへの 系譜を追い掛けようとしている • V1 → V6 → V7 → 32V → 4BSD → 386BSD → NetBSD • NetBSDをOS学習の定番にしたいですね! • 落ち着いたらMulticsに進みたい • 手始めのV6で格闘中 • せっかくなので調べたことを教材にまとめたい • PDP-11や8進数が障害になっていると感じたため、8086移 植を計画中(後述)
    • 池袋バイナリ勉強会 • 本来の趣旨は、OSを作れるコンパイラを作ること • 趣味の陶芸教室(ろくろ?)みたいな感覚で、実用的な知 識を提供しようとはあまり思っていない • 色々な勉強会を試したものの淘汰された • 淘汰=継続的に人が集まらない • 現時点で続いているのは5つ • 池袋バイナリ勉強会 • BSDカーネルの設計と実装 読書会 • はじめてのOSコードリーディング 読書会 • F#談話室 • 初級ラテン語リーディング • その他、他の方の勉強会も開いたりします
    • V6移植ハッカソン • OS学習用教材作成の 一環 • 夏休み中に開催 • 全部は無理だと思う • アセンブラとコンパイラ の8086化までできれば 良いな
    • 1. ELFファイルの概要
    • ELFファイルとは • 実行可能バイナリのファイル形式 • 歴史的発展: a.out → COFF → ELF • NetBSDのネイティブサポートはCOFFを経由せずに a.out → ELF • WindowsのPEはCOFFを拡張したもの • Mac OS XではMach-Oという形式 • 読んだことがないので、詳しく知りません • NetBSDではCOFFもPEもMach-Oもバイナリエミュレー ションとして読み込むことはできる • 参照するライブラリが揃わないと実行できないけど
    • a.outファイルの構造 • UNIX V6のものを示す • a.outファイルは独自拡張された 亜種が多い • セクションは.textと.data固定 • .bssはすべてゼロのため、ヘッダ にサイズだけ定義されている • 後ろに再配置情報やシンボル 情報が付けられるが、実行に は必須ではないため省略 • stripコマンドによってそれらを 削った状態が右図 a.outヘッダ .text .data
    • COFFファイルの構造 • セクションヘッダにより、セクショ ンが自由に定義できる • 機能拡張は独自のセクションを 定義する形で行える • PEファイルでの拡張点 • DOSスタブやオプショナルヘッダ などCOFFにない要素がある • オンデマンドページングでシーク しやすいように、セクションごとの アラインメントが強制 COFFヘッダ セクションヘッダ セクション セクション セクション セクション
    • ELFファイルの構造 • セクションとセグメントという 二重構造(後述) • セクションは用途別に細かく 別れている • 実行時にはそこまで細かい 分類は必要ないので、メモリ 属性に基づいてセクションを まとめたものがセグメント • セクションヘッダは取り外しの 便のため後ろに配置 ELFヘッダ プログラムヘッダ (セグメント管理) セクション セクション セクション セクション セクションヘッダ
    • セクションとセグメント (1) • セグメントという用語は色々な用法があるため混乱を 招きやすい • x86のセグメントはCPUの機能を指しているため、ELFのセ グメントと同じ概念ではない • 前者(x86の機能)で後者(ELFのセグメント)を実装できる ため、まったく別物というわけではない • しかし現在そのような実装は主流ではない • 64bitではセグメント自体が廃止された • セグメントのことはあまり深く考えないで、セクション 中心に見ていけば良いと思う • ここから先はその方針で進める
    • セクションとセグメント (2) • UNIX V6にもセグメントの概念は 存在した • メモリを用途別にMMUで分割し た単位がセグメント 1. .text 2. .data + .bss + ヒープ + スタック • ヒープとスタックの間の空きは割り 当てないで物理メモリを節約 • これが発展して抽象化されたの がELFのセグメント • と考えれば良いのではないか メモリ配置 .text .data .bss ヒープ (空き) スタック
    • 2. セクション
    • 静的リンク • まずは静的リンクから $ i386-elf-gcc –nostdlib -s ret.s $ i386-elf-readelf -S a.out There are 3 section headers, ... Section Headers: [Nr] Name ... [ 0] ... [ 1] .text ... [ 2] .shstrtab ... ret.s .globl _start _start: ret ELFヘッダ プログラムヘッダ .text .shstrtab セクションヘッダ
    • セクションの説明 • 実質的には.textが1つ ret.s .globl _start _start: ret ELFヘッダ プログラムヘッダ .text .shstrtab セクションヘッダ セクション名 役割 .text 機械語コードが 格納されている .shstrtab セクション名の文 字列が格納され ている
    • 共有ライブラリの作成 • 動的リンク対象として準備 • 今回は呼び出し側を追うため、共 有ライブラリ側は追わない • セクションとか凄いことになっている $ i386-elf-gcc -c test.s $ i386-elf-ld -shared -s -o libtest.so test.o $ file libtest.so libtest.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped test.s .globl test test: ret
    • 動的リンク • 共有ライブラリの中のコードは PLT(Procedure Linkage Table)経 由で呼び出す(後述) • いきなりセクションが爆発! ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ start.s .globl _start _start: call test@PLT $ i386-elf-gcc -nostdlib -s start.s libtest.so
    • セクション概要 セクション 説明 .interp 動的リンクを処理するインタプリタ .hash シンボル名のハッシュテーブル .dynsym シンボルテーブル .dynstr シンボル名の文字列テーブル .rel.plt 動的リンクのために書き替えが必要なアド レスのリスト .plt 動的リンクされた関数などを.got.pltからア ドレスを取得して呼び出すコード .text C言語などで書いたプログラムをコンパイ ルしたコード .dynamic 動的リンクに必要な情報を集めたテーブル .got.plt 動的リンクされた関数などのアドレステー ブル .shstrtab セクション名の文字列テーブル ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ
    • ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ ELFヘッダ • 32bit/64bit、エンディアン、CPU • 後続のヘッダへのポインタ $ i386-elf-readelf -h a.out ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x80481b4 Start of program headers: 52 (bytes into file) Start of section headers: 672 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 5 Size of section headers: 40 (bytes) Number of section headers: 11 Section header string table index: 10
    • プログラムヘッダ • ローダのための情報 • セグメント、動的リンク関係 $ i386-elf-readelf -l a.out Elf file type is EXEC (Executable file) Entry point 0x80481b4 There are 5 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x000a0 0x000a0 R E 0x4 INTERP 0x0000d4 0x080480d4 0x080480d4 0x00013 0x00013 R 0x1 [Requesting program interpreter: /usr/lib/libc.so.1] LOAD 0x000000 0x08048000 0x08048000 0x001b9 0x001b9 R E 0x1000 LOAD 0x0001bc 0x080491bc 0x080491bc 0x00098 0x00098 RW 0x1000 DYNAMIC 0x0001bc 0x080491bc 0x080491bc 0x00088 0x00088 RW 0x4 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .hash .dynsym .dynstr .rel.plt .plt .text 03 .dynamic .got.plt 04 .dynamic ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ
    • .interp • 文字列が格納されている • 動的リンクを行う外部プログラム • インタプリタと呼ばれる • NetBSDではld.elf_so • シーケンス 1. execveシステムコール 2. カーネルのローダが指定された ELFファイルを読み込み 3. インタプリタが動的リンクを処理 ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -p .interp a.out String dump of section '.interp': [ 0] /usr/lib/libc.so.1
    • .hash • シンボル名の検索を高速化するため のハッシュテーブル • 共有ライブラリ内で未定義のシンボル があれば実行ファイルから検索してリ ンクされるため、実行ファイル側にも 持っておく必要がある • 以前のNetBSDではlibcにenvironと __prognameが未定義で、実行ファイル 側に定義させていた ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -x .hash a.out Hex dump of section '.hash': 0x080480e8 03000000 05000000 03000000 04000000 ................ 0x080480f8 02000000 00000000 00000000 00000000 ................ 0x08048108 00000000 01000000 ........
    • .dynsym • シンボルのテーブル • .hashで名前がハッシュ化 • 共有ライブラリの関数への参照 • Valueがゼロ、NdxがUND • 自身が持つシンボル • Valueにアドレス入っている ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -s a.out Symbol table '.dynsym' contains 5 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 NOTYPE GLOBAL DEFAULT UND test 2: 08049254 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 3: 08049254 0 NOTYPE GLOBAL DEFAULT ABS _edata 4: 08049254 0 NOTYPE GLOBAL DEFAULT ABS _end
    • .dynstr • 文字列が格納されている • シンボル名 • .dynsymから参照 • 共有ライブラリのファイル名 • .dynamicから参照 ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -p .dynstr a.out String dump of section '.dynstr': [ 1] libtest.so [ c] test [ 11] _edata [ 18] __bss_start [ 24] _end
    • .rel.plt • 動的リンクのために書き替えが 必要なアドレスのリスト • アドレスとシンボルのペア • インタプリタによって共有ライブラ リ内のアドレスが書き込まれる • 動的リンクの根幹メカニズム • 下の例では08049250にtestのアド レスを書き込むことで動的リンクが 実現される ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -r a.out Relocation section '.rel.plt' at offset 0x18c contains 1 entries: Offset Info Type Sym.Value Sym. Name 08049250 00000107 R_386_JUMP_SLOT 00000000 test
    • .plt • リンカが自動生成する定型コード • 動的リンクされた関数を.got.pltから アドレスを取得して飛ぶラッパー • それ以外のコードは遅延リンク(後述) • サンプルコードのtest@PLTがこれ ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-objdump -d -M intel -j .plt a.out Disassembly of section .plt: 08048194 <test@plt-0x10>: 8048194: ff 35 48 92 04 08 push DWORD PTR ds:0x8049248 804819a: ff 25 4c 92 04 08 jmp DWORD PTR ds:0x804924c 80481a0: 00 00 add BYTE PTR [eax],al 080481a4 <test@plt>: 80481a4: ff 25 50 92 04 08 jmp DWORD PTR ds:0x8049250 80481aa: 68 00 00 00 00 push 0x0 80481af: e9 e0 ff ff ff jmp 8048194 <test@plt-0x10> start.s .globl _start _start: call test@PLT
    • .text • 機械語コードが格納されている • いわゆるバイナリはここがメイン ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-objdump -d -M intel -j .text a.out Disassembly of section .text: 080481b4 <.text>: 80481b4: e8 eb ff ff ff call 80481a4 <test@plt> start.s .globl _start _start: call test@PLT
    • .dynamic (1) • 動的リンクに必要な情報を集め たテーブル ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -d a.out Dynamic section at offset 0x1bc contains 12 entries: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libtest.so] 0x00000004 (HASH) 0x80480e8 0x00000005 (STRTAB) 0x8048160 0x00000006 (SYMTAB) 0x8048110 0x0000000a (STRSZ) 41 (bytes) 0x0000000b (SYMENT) 6 (bytes) 0x00000015 (DEBUG) 0x0 0x00000003 (PLTGOT) 0x8049244 0x00000002 (PLTRELSZ) 8 (bytes) 0x00000014 (PLTREL) REL 0x00000017 (JMPREL) 0x804818c 0x00000000 (NULL) 0x0
    • .dynamic (2) ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ セクション Type 説明 .hash HASH セクションのアドレス .dynsym SYMTAB セクションのアドレス SYMENT セクションに含まれるシンボル 情報の1エントリのサイズ .dynstr STRTAB セクションのアドレス STRSZ セクションのサイズ .rel.plt JMPREL セクションのアドレス PLTRELSZ セクションのサイズ PLTREL セクション内に含まれる再配 置情報の種類 (REL/RELA) .got.plt PLTGOT セクションのアドレス
    • 08048194 <test@plt-0x10>: 8048194: ff 35 48 92 04 08 push DWORD PTR ds:0x8049248 804819a: ff 25 4c 92 04 08 jmp DWORD PTR ds:0x804924c 80481a0: 00 00 add BYTE PTR [eax],al 080481a4 <test@plt>: 80481a4: ff 25 50 92 04 08 jmp DWORD PTR ds:0x8049250 80481aa: 68 00 00 00 00 push 0x0 80481af: e9 e0 ff ff ff jmp 8048194 <test@plt-0x10> .got.plt • 動的リンクされた関数などのアド レステーブル • ここをインタプリタが書き替える ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -x .got.plt a.out Hex dump of section '.got.plt': 0x08049244 bc910408 00000000 00000000 aa810408 .dynamic 遅延リンク関係 書替対象 .pltが参照しているのはココ!
    • .shstrtab • セクション名の文字列が格納さ れている • 名前を任意長で取るため • COFF/PEではセクションヘッダに名 前が固定長8文字で格納 ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -p .shstrtab a.out String dump of section '.shstrtab': [ 1] .shstrtab [ b] .interp [ 13] .hash [ 19] .dynsym [ 21] .dynstr [ 29] .rel.plt [ 32] .text [ 38] .dynamic [ 41] .got.plt 文字列の途中から 参照することで兼ね ている (NULL終端による)
    • セクションヘッダ • セクションの情報 • 位置やセクション数はELFヘッダ で定義されている ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ $ i386-elf-readelf -S a.out There are 11 section headers, starting at offset 0x2a0: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 080480d4 0000d4 000013 00 A 0 0 1 [ 2] .hash HASH 080480e8 0000e8 000028 04 A 3 0 4 [ 3] .dynsym DYNSYM 08048110 000110 000050 10 A 4 1 4 [ 4] .dynstr STRTAB 08048160 000160 000029 00 A 0 0 1 [ 5] .rel.plt REL 0804818c 00018c 000008 08 A 3 6 4 [ 6] .plt PROGBITS 08048194 000194 000020 04 AX 0 0 4 [ 7] .text PROGBITS 080481b4 0001b4 000005 00 AX 0 0 4 [ 8] .dynamic DYNAMIC 080491bc 0001bc 000088 08 WA 4 0 4 [ 9] .got.plt PROGBITS 08049244 000244 000010 04 WA 0 0 4 [10] .shstrtab STRTAB 00000000 000254 00004a 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
    • ローダとセクション • ローダはセクションヘッダを見ないということ にはなっているけど、NetBSDではLinuxバイ ナリかどうかの判定で見ている • NetBSD 5.1でバイナリをいじっていたとき、 縮めるためセクションヘッダと.shstrtabを削 除したのにELFヘッダの修正を忘れて不正な 参照が残りカーネルパニックになったことが ある(NetBSD 6で修正済み) • その問題を追った時に作ったパッチ ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ --- sys/compat/linux/common/linux_exec_elf32.c.orig +++ sys/compat/linux/common/linux_exec_elf32.c @@ -111,7 +111,7 @@ * Now let's find the string table. If it does not exists, give up. */ strndx = (int)(eh->e_shstrndx); - if (strndx == SHN_UNDEF) { + if (strndx == SHN_UNDEF || strndx >= eh->e_shnum) { error = ENOEXEC; goto out; }
    • PEとの比較 • PEでは.dynsymと.dynstrと.got.plt が1つになった.idataというセク ションで動的リンクが処理される • カーネルが直接処理して、インタプ リタのようなものはない • アドレスが書き込まれる箇所 (.got.plt相当)はサンクと呼ぶ ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ PEヘッダ セクションヘッダ .idata .text
    • 3. 動的リンク
    • Pythonで動的リンク • ここからはPythonで簡単なローダ を作って処理を追ってみます • 完全なインタプリタを実装するわ けではありません • 実行ファイルに対するリンク処理の みを行います • 共有ライブラリを読み込む処理は実 装しません • WindowsでELFを読み込むという 一種のネタです • Pythonを選んだのはF#を見せても 読めないと言われそうだからです ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ
    • ファイルの読み込み • 指定されたファイルを読み込む • 省略した場合は a.out • 単純化のため逐次読み込みしな いで、全体を一度に読み込む ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ from sys import argv aout = "a.out" if len(argv) < 2 else argv[1] with open(aout, "rb") as f: elf = f.read()
    • ELFヘッダ (1) • ELFファイルかどうかをチェック • ファイルサイズ • ファイル全体がヘッダより小さいという ことはあり得ない • ゴルフと言ってヘッダの中にセクション を入れる遊びはあるけど・・・ • シグネチャ • アーキテクチャのチェック • 32bit • リトルエンディアン ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ assert len(elf) >= 52, "not found: ELF32 header" assert elf[0:4] == "¥x7fELF", "not found: ELF signature" assert ord(elf[4]) == 1, "not 32bit" assert ord(elf[5]) == 1, "not little endian"
    • ELFヘッダ (2) • ヘッダ本体の読み込み ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ (e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx) = unpack( "<HHLLLLLHHHHHH", elf[16:52])
    • ELFヘッダ (3) • 位置独立コードで、i386を対象と しているかをチェック ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ assert e_type == 3, "not PIE" assert e_machine == 3, "not i386" • 今回のローダはWindows用 • アドレスを指定しても既に使用さ れていれば確保できないため、ど こに読み込んでも良いように位置 独立を要求 • セクションのサンプルで使ったバ イナリはPIEではないため対象外
    • プログラムヘッダ (1) • プログラムヘッダは複数ある • 1つずつインスタンスとして読み込 むためのクラスを定義 class Elf32_Phdr: def __init__(self, data, pos): (self.p_type, self.p_offset, self.p_vaddr, self.p_paddr, self.p_filesz, self.p_memsz, self.p_flags, self.p_align) = unpack( "<LLLLLLLL", data[pos : pos + 32]) ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ
    • プログラムヘッダ (2) • ELFヘッダの情報を基に、プログラ ムヘッダを読み込む phs = [Elf32_Phdr(elf, e_phoff + i * e_phentsize) for i in range(e_phnum)] ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ • 内包表記でリストを作成 項目 説明 e_phoff プログラムヘッダのファイル中の位置 e_phentsize プログラムヘッダ1個のサイズ e_phnum プログラムヘッダの個数
    • セグメントのロード • メモリサイズの算出 memlen = max([ph.p_vaddr + ph.p_memsz for ph in phs]) ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ • 実行可能ページを確保 mem = VirtualAlloc(0, memlen, MEM_COMMIT, PAGE_EXECUTE_READWRITE) • プログラムヘッダを見てロード pelf = cast(elf, c_void_p).value for ph in phs: addr = mem + ph.p_vaddr if ph.p_type == 1: # PT_LOAD o, sz = ph.p_offset, ph.p_memsz memmove(addr, pelf + o, sz) print "LOAD: %08x-%08x => %08x-%08x" % ( o, o + sz - 1, addr, addr + sz - 1)
    • .dynamic • プログラムヘッダに.dynamicが指 定されていれば読み込む • 最低限必要なものだけ • type = 0 は終端 elif ph.p_type == 2: # PT_DYNAMIC while True: type = read32(addr) val = read32(addr + 4) if type == 0: break elif type == 5: strtab = mem + val elif type == 6: symtab = mem + val elif type == 11: syment = val elif type == 23: jmprel = mem + val elif type == 2: pltrelsz = val addr += 8 ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ
    • .rel.plt • アドレスとシンボルを読み込み • .dynsymと.dynstrをたどることで、 シンボルを解釈 if jmprel != None: print print ".rel.plt(DT_JMPREL):" for reladdr in range(jmprel, jmprel + pltrelsz, 8): offset = read32(reladdr) info = read32(reladdr + 4) stroff = read32(symtab + (info >> 8) * syment) name = string_at(strtab + stroff) print "[%08x]offset: %08x, info: %08x; %s" % ( reladdr, offset, info, name) ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ
    • リンク • シンボル名に合致する関数を探し てアドレスを書き込む • 単純化のため共有ライブラリは読 み込まないでPythonで定義した関 数へのサンクを書き込んでいる assert libc.has_key(name), "undefined reference: " + name addr = mem + offset faddr = cast(libc[name], c_void_p).value print "linking: %s -> [%08x]%08x" % (name, addr, faddr) write32(addr, faddr) ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ Hex dump of section '.got.plt': 0x08049244 bc910408 00000000 00000000 aa810408 書替対象 080481a4 <test@plt>: 80481a4: ff 25 50 92 04 08 jmp DWORD PTR ds:0x8049250 参 照
    • 動的リンク完了 • .got.pltを書き替えて完成 • .pltのコードが.got.pltを参照してリン ク先に飛べる ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ Hex dump of section '.got.plt': 0x08049244 bc910408 00000000 00000000 aa810408 080481a4 <test@plt>: 80481a4: ff 25 50 92 04 08 jmp DWORD PTR ds:0x8049250 参 照 080481b4 <.text>: 80481b4: e8 eb ff ff ff call 80481a4 <test@plt> • ここでは起動時にアドレス解決を 行ったが、最初に呼び出すまで遅 延させる仕組みが入っている
    • 4. 遅延リンク
    • 遅延リンク 1. 最初に呼び出されたときにインタ プリタで受け取る 2. .got.pltを書き替えて対象に飛ぶ 3. 次回からはそのまま飛べる ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ 08048194 <test@plt-0x10>: 8048194: ff 35 48 92 04 08 push DWORD PTR ds:0x8049248 804819a: ff 25 4c 92 04 08 jmp DWORD PTR ds:0x804924c 80481a0: 00 00 add BYTE PTR [eax],al 080481a4 <test@plt>: 80481a4: ff 25 50 92 04 08 jmp DWORD PTR ds:0x8049250 80481aa: 68 00 00 00 00 push 0x0 80481af: e9 e0 ff ff ff jmp 8048194 <test@plt-0x10> Hex dump of section '.got.plt': 0x08049244 bc910408 00000000 00000000 aa810408 8049248 8049250804924c jmp [0x08049250] → jmp 0x080481aa .plt.text .text インタプリタ 3 1 2 実行ファイル 共有ライブラリ
    • Pythonで遅延リンク • Python内のインタプリタを呼ぶた めのサンクを.got.pltに登録 def interp(id, offset): print "delayed link: id=%08x, offset=%08x" % (id, offset) return link(jmprel + offset) thunk_interp = CFUNCTYPE(c_void_p, c_void_p, c_uint32)(interp) call_interp = JIT([ 0xff, 0x14, 0x24, # call [esp] 0x83, 0xc4, 8, # add esp, 8 0x85, 0xc0, # test eax, eax 0x74, 2, # jz 0f 0xff, 0xe0, # jmp eax 0xc3 ]) # 0: ret if pltgot != None: writeptr(pltgot + 4, thunk_interp) writeptr(pltgot + 8, call_interp) ELFヘッダ プログラムヘッダ .interp .hash .dynsym .dynstr .rel.plt .plt .text .dynamic .got.plt .shstrtab セクションヘッダ Hex dump of section '.got.plt': 0x08049244 bc910408 00000000 00000000 aa810408 08048194: push thunk_interp 0804819a: jmp call_interp test@plt: 080481a4: jmp [0x08049250] 080481aa: push 0x0 080481af: jmp 0x08048194
    • ご清聴ありがとうございました