TUI Based ELF Analyzer in Rust

私は 2020年4月に サイボウズ・ラボユース という人材育成プロジェクトに採択されてから,
複数アーキテクチャを対象にするコンパイラドライバのスクラッチ開発 に挑戦しています.

その過程で,Rustで便利に使えるELF周辺のユーティリティがほしいなと思って,
このようなライブラリを作り始めました.

ELFの読み込みやその他操作が簡単に,Rustのコア機能を使って行えるもので,
イメージとしては /usr/include/elf.h のRust実装+αみたいな感じです.

このような 純粋なライブラリ を作っていると,
やはり ライブラリを使うツール を作る必要が出てきます.
実装しているライブラリのバグや完成度,使いやすさを自分の手で確かめられるからです.

そのために作り始めたのが,
TUIベースのELF解析ツール , elfpeach です.

  • 既存のTUIのELF解析ツールが見受けられなかった
  • GNU readelfの出力に不満があった(後述)

という点から開発を決意しました.
結果として,良いものを作れているのではないかと考えています.

本記事では,elfpeachの解説と今後の展望をベースに,
ELF解析ツールについての考えを取り上げたいと思います.

elfpeachの主な機能

ELF解析の標準ツールである GNU readelfで -a オプションを渡した時に表示される情報と,
現在のelfpeachで実装されている機能を比較してみます..
x が実装済み, - が未実装を表します.

機能 readelf elfpeach
64bit ELF x x
32bit ELF x - ( elf-utilities の不備)
x86_64以外のアーキテクチャ x - ( elf-utilities の不備)
ELFヘッダ x x
セクションヘッダテーブル x x
sh_link/sh_info のより詳細な情報 - x
プログラムヘッダテーブル x x
セクションとセグメントのマッピング表示 x -
動的情報 x x
d_un のより詳細な情報 - x
再配置テーブル x -
セクショングループ x -
バージョン x -
ヒストグラム x -
note x -

こうしてみると,まだまだ実装すべき部分が残っていることがわかりますが,
一番のコア機能であるヘッダ/セクション/セグメント/シンボルが解析できるので,
ここで一区切りとして,記事にすることにしました.

それぞれの概要と,特徴的な機能について触れます.

ELFヘッダについて

実際に解析したスクショを載せます.

scshot1

殆どの出力はGNU readelfに似ていますが,
GNU readelf よりこだわった部分の紹介をします.

どのような種類のELFであれ,ELFヘッダは必ず存在します.
ELFヘッダから得られる情報はかなり多いですが,
多くの人が見るのは Ehdr.e_entry の情報でしょう.
プログラムのエントリポイントとなるアドレスが格納されているフィールドですが,
/bin/ls のようにシンボルテーブルがstripされていなければ,
Sym.st_value の値から,エントリポイントとなるシンボルが見つかるはずです.

そこでelfpeachでは,
Ehdr.e_entry の値でシンボルテーブルを探索し,
該当するシンボルがあれば,そのシンボル名を表示するようにしています.
(上記例では _start が表示されていますね. )

一般的に用いられるコンパイラドライバを使っている場合,
大体固定のシンボルが指定されていますし,気にする必要はありません.
しかし,組み込みやベアメタル,その他特殊な状況ではこのエントリポイントを気にする必要が出てきます.

実際私はコンパイラドライバを自作しており,スタートアップルーチンも 独自のもの を使用しているので,
この機能がELF解析ツールにあると非常に助かります.
自作アセンブラやリンカのデバッグに役立つからですね.

また, Ehdr.e_shstrndx というメンバには,
セクションヘッダテーブルが用いる セクション名テーブル のインデックスを指定しますが,
これも同様にセクション名を表示するようにしました.
ほぼすべての場合で .shstrtab が指定されていると思いますが.

セクションヘッダテーブルについて

scshot2

これも殆どreadelfに即したものになっています.

elfpeachのこだわった点として,
readelfよりリッチな情報を提供する というものがあります.

セクションヘッダには, sh_link/sh_info というフィールドが存在します.
これらは Shdr.sh_type によって異なる意味を持ち,
その意味はフィールドの数値だけを見てもすぐにわかりません.
しかしreadelfでは単にその値が出力されているだけです.

elfpeachでは数値をそのまま出力するのに加えて,
sh_type ごとに異なる出力 をするようにしました.

例えば上記画像であれば,
Shdr.sh_type == SHT_RELA より,

  • sh_link … 対応するシンボルテーブルのセクションインデックス
  • sh_info … 再配置対象となるセクションのインデックス

というような情報が格納されているはずです.
これらをより見やすくするための工夫を施しています.
実装をお見せします.

pub fn section_information<'a>(
    elf_file: &'a file::ELF64,
    sct: &'a section::Section64,
) -> Paragraph<'a> {
    let sct_info = match sct.header.get_type() {
        section::Type::Dynamic => dynamic_info(elf_file, sct),
        section::Type::Hash | section::Type::SymTabShNdx => hash_info(elf_file, sct),
        section::Type::SymTab | section::Type::DynSym => symtab_info(elf_file, sct),
        section::Type::Group => group_info(elf_file, sct),
        section::Type::Rel | section::Type::Rela => relocation_info(elf_file, sct),
        _ => common_section_info(sct),
    };

    Paragraph::new(sct_info).block(Block::default().borders(Borders::ALL).title("Sections"))
}

このようにして描画する情報を区別しています.

プログラムヘッダテーブルについて

scshot3

現状はGNU readelfと同等の機能しか提供できていませんが,
これも INTERP ならファイルパスを表示する等の機能を作成してもいいかもしれません.

シンボルテーブルについて

scshot4

基本情報の出力は問題なく実装できています.
お気づきの通り 重大な欠陥 が存在するのですが,
それは 今後の展望 でお話したいと思います.

動的情報について

これも基本的には同様ですが,
Dyn.d_un の情報をreadelfよりリッチに提供しています.
非常に長いので,詳細はこちらをご覧ください.

今後実装しようと思っている機能

テーブル構造のフィルタリング機能

ELFには多くの テーブル形式 が存在します.
ヘッダテーブルやシンボルテーブル,再配置情報に動的情報などがその例です.
これらは大きなバイナリであれば非常に多くのエントリ数を持つでしょう.

readelfだと grep <address> とかで見たいシンボルのみ見れるというのに対して,
現状 ひたすら を入力するしかない という残念な仕様になっています.
よって,このままでは使い物になりません.

これをTUIツールっぽく解決するために,
フィルタリング機能 を導入しようと考えています.
イメージとしては次のような感じ.

  • あるキーの入力でウィンドウがポップアップ(modal windowというんだったかな)
  • 構造体のメンバと検索したい値を指定する
  • 合致するエントリのみ表示される

これにより多くの問題は解決すると思います.

32bit ELFの読み込み

これはelf-utilitiesの問題になりますが,
現状32bit版のELFに関わるAPIが全く存在しない,という欠点が存在します.
readelfでは問題なく解析可能なので,これは早急に取り組む必要がありそうです.

elf-utilitiesに沢山のトレイトを実装すれば,キレイな感じで実装できそうな感じはします.
のでやるだけなんですが,やっていないので,やらなければいけませんね.

hexdump

セクションの部 を見て頂ければわかりますが,
右下に余らせているスペースがありますよね.
このスペースを使用して,セクション内部のhexdumpを表示したら便利なんじゃないかと思っています.

例えば .interp など, sh_typeSHT_PROGBITS であるが,
内容はファイルパスのascii文字列であり,hexdumpによって簡単に解析可能である場合,
それが表示されていると結構便利です.
あとは .comment とかもそうですね.

なんかRustでいい感じのツールあるんでしょうか.
教えて頂ければ助かります.

まとめ

まだまだ発展途上のelfpeachですが,
ELF解析ツールの選択肢の一つに入れるようなものを目指して,これからもほそぼそと続けていきます.

一応サイボウズ・ラボユースの成果物に含まれているので基本的には一人で開発していきますが,
OSS活動の範囲内であれば支援していただけると大変助かります.

是非使って,あら探しして,Issueに上げてください.