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コードリファクタリングを行いたいと思っています。先が長い…

ELF弄りメモ Part.0 ~事前調査編~

耳がとんがってる妖精でも爆発するやつでもない、ELFファイル弄りです。
軽く弄って闇鍋に突っ込もうと思ってたんですけど、軽く弄れなかったので別記事で書くことになった次第です。自分で解釈したこと、弄ってみたこと中心に書いていきます。

やりたいこと

既存のELF実行ファイルに共有ライブラリ参照を追加し、そのライブラリの関数を実行する。

まずELFって?

Executable and Linkable Format(実行可能でリンク可能なフォーマット)の略。LinuxだけでなくBSDとかにも使われてるらしい。Linuxの実行ファイルや共有オブジェクト(.so)の形式はこれ。

構造 

図1 ELFファイルの構造(簡略)
ELFファイルは図1のような構造をしています。詳しい構造はこのスライドサイトが参考になります。
プログラムヘッダはセグメント毎、セクションヘッダはセクション毎に構造体を持っていて、それの塊がテーブルです。構造体の定義はLinuxの場合/usr/include/linux/elf.hにあるはずです。
セクションはそれぞれ役割があります。 またセクションは増えたり減ったりするためセクションヘッダを見ないとだめそうです。

かせつ

 以下のように考えています。
.interp、.shstrtab以外をうまく弄ればできそう?
.dynstrに読み込みたいライブラリ名と関数の文字列を書き込んで、.hashと.dynsymをそれに合わせてうまく描き替える
PLT→GOTもうまくやれるようにして.textから呼び出す
最後にヘッダ類を修正する

その他の参考サイト

http://warabanshi.hatenablog.com/archive/category/ELF
https://web.archive.org/web/20170607001117/http://www.hyuki.com:80/yukiwiki/wiki.cgi?WhirlwindTutorialOnCreatingReallyTeensyElfExecutablesForLinux



2019年3月21日木曜日

闇鍋(個人的雑多メモ)

なにもわからん!

完全に個人用なので読みやすさ度外視してます。こんなのでも読んでくれて間違いを指摘してくれる神がいるなら泣いて喜びますのでコメントでお願いします。

移植関係

Linuxカーネル関係

デバッグ

kgdbを使うらしい(未検証、参考サイト(原文))

コードリーディング

Eclipseだと便利そう?(参考サイト)
今使ってるVSCodeだとIntelliSenceだよりっぽい(参考サイト)

ドライバ/Device Tree

Device Treeはここのサイトがわかりやすかった。

Androidポート

kernel/configs以下にandroid-*.cfgというファイルがあるが、これをデバイス固有のdefconfigに混ぜることによってできる。
kernel/configs/android-fetch-configs.sh で最新の泥defconfigの元がもらえる。
scripts/kconfig/merge_config.sh で複数のconfigを混ぜて.configにしてくれる。
参考サイト:公式ドキュメント

HAL(HIDL込)

Default HAL(*.default.soのソースコード)は/hardware/libhardware/modules以下にある。
HIDLはFrameworkとHALを繋ぐもの。Oreo以降に実装されている。動作モードは/system/manifest.xmlで記述する。
hwbinderモード:FrameworkとHALを別プロセスで動作させる。/system側に*-impl.so、/vendor側に*-service.soが必要。Treble化する(/systemと/vendorを分ける)場合はこのモード。
passthroughモード:FramoworkとHALを同じプロセスで動作させる。/systemに-impl.soが必要。泥ポートするときTreble化しないならこっちで開発を進めた方が良さそう。
参考サイト:公式ドキュメントスライド

ビルド関係

Oreo以降は/system(Trebleなら/vendorも)のearly mountが必要。Linux KernelのDevice Treeで記述する(こんな感じ)。
HIDLは上記の必要なのを記述してあげる(こんな感じ)。
BoardConfig.mkでandroidboot.hardware=<ハードウェア名> でro.hardwareを指定すると、init.<ハードウェア名>.rcが読み込めるようになる。
BOARD_<パーティション名>IMAGE_PARTITION_SIZEはbyte換算。

AOSP系のみ

AndroidProducts.mkにaosp_なんちゃら.mkを書く(aospじゃなくても可)
PRODUCT_DEVICEはdevice configurationのフォルダ名と同じくする

りばーすえんナントカ

smali弄り

ここにはるろいど氏がいい感じにまとめてくれてます、感謝。

invoke系の違いは
invoke-static:staticメソッド呼ぶ
invoke-direct:privateメソッド呼び出し、コンストラクタ呼び出し(<init>って書いてあるやつ)で使われる
invoke-virtual:publicメソッド呼ぶ
なのかなって思ってる。これ考えて1からsmali書くよりアプリ書いてばらして移植が手っ取り早いと思う。
あと引数レジスタp0はthisで、p1から引数になる。引数レジスタを通常レジスタのように使いまわすことも可能。
iget-objectのレジスタは宛先、呼び元の順

ダークELF

Part.0 ~事前調査編~
Part.1 ~各セクションの分割~