6

コード内の一部の関数を実行可能ファイルの特定のセクションに移動することはできますか? もしそうなら、どのように?

gcc でコンパイルされたアプリケーションの場合、Xc を含むより多くのソース ファイルがあります。各オブジェクトは関連するソースからコンパイルされ (Xo は Xc から取得されます)、リンカーは大きな実行可能ファイルを生成します。

Xc の 2 つの関数を実行可能ファイルの特定のセクション (.magic_section など) に配置する必要があります。これが必要な理由は、セクションが残りのセクションとは別のメモリ領域にロードされるためです。

私の問題は、ソース Xc を変更できないことです。それ以外の場合は__attribute__ ((section ("magic_section")))、関数などに特定のフラグを使用していたでしょう。

リンカーのドキュメントを読み、リンカーのカスタム スクリプトを作成しましたが、特定のシンボルを配置する必要があるセクションを指定できませんでした。なんとかセクション全体を移動することができました。

4

2 に答える 2

1

残念ながら、バイナリ オブジェクト、ダイナミック リンカ、またはダイナミック ローダーを変更しないと、これを達成することはできません。とにかく、これは非常に難しい作業です。

オプション 1 - ELF 操作

各 ELF 実行可能ファイルは、実際のコード/データ/シンボル文字列/... を含むセクションと、ローダーがコードをメモリ内のどこにロードするか、この ELF が公開するシンボル、必要なシンボルなどを決定するのに役立つセグメントから作成されます。他の場所、特定のコード/データをロードする場所など

次のように入力して、バイナリのセグメントを観察できます。

readelf -l [あなたのバイナリ]

出力は次のようになります (バイナリとして ls を選択しました)。

[ishaypeled@ishay-dev bin]$ readelf -l --wide ./ls

Elf file type is EXEC (Executable file)
Entry point 0x4048bf
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R E 0x8
  INTERP         0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x01b694 0x01b694 R E 0x200000
  LOAD           0x01bdf0 0x000000000061bdf0 0x000000000061bdf0 0x000864 0x0016d0 RW  0x200000
  DYNAMIC        0x01be08 0x000000000061be08 0x000000000061be08 0x0001f0 0x0001f0 RW  0x8
  NOTE           0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x01895c 0x000000000041895c 0x000000000041895c 0x00071c 0x00071c R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x01bdf0 0x000000000061bdf0 0x000000000061bdf0 0x000210 0x000210 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got 

次に、この出力を調べてみましょう。

最初の表 (プログラム ヘッダー):
[タイプ] - セグメント タイプ、このセクションの目的
[オフセット] - このセグメントが始まるファイル内のオフセット
[VirtAddr] - プロセス アドレス空間でこのセクションをロードする場所 (このセグメントをロードする必要がある場合は、すべてがロードされるわけではありません)
[PhysAddr] - 私が遭遇した最新のすべての OS の VirtAddr と同じ
[FileSiz] - ファイル上のこのセクションの大きさ。これはセクションへのリンクです - 現在のセグメントは Offset から Offset+FileSiz
[MemSiz] の範囲のすべてのセクションで構成されています - 仮想メモリ内のこのセクションの大きさ (これはファイルのサイズと同じである必要はありません!ファイルのサイズを超える場合、超過分は 0 に設定されます)
[Flg] - 許可フラグ、R 読み取り E 実行 W 書き込み。[Align] - メモリ内で必要なメモリ アラインメント。

ここでは、タイプ LOAD (PT_LOAD) のセグメントに焦点を当てています。これらのセグメントは、セクションからデータをグループ化し、ローダーにそれらをプロセス アドレス空間のどこに配置するかを指示し、それらのパーミッションの指定を決定します。

セクションからセグメントへのマッピング表で、便利なセクションからセグメントへのマッピングを確認できます。

2 つの LOAD セグメント 2 と 3
を観察してみましょう。セグメント 2 には読み取り権限と実行権限があり、(とりわけ) .text セクションと .rodata セクションにまたがっていることがわかります。

したがって、ELF 操作を使用して目的を達成するには、次のようにします。

  1. ファイル内で関数を作成するバイナリ データを見つけます (readelf ユーティリティが役に立ちます)。
  2. ELF ヘッダーを変更することにより (これを自動的に行うツールを知りません。おそらく独自に作成する必要があります)、.text セクションを含むセグメントを 2 つの連続した LOAD セグメントに分割し、関数コードを除外します。
  3. ELF ヘッダーを変更して、2 つの関数のみを含む新しい LOAD セグメントを作成します。
  4. 古い関数の場所へのすべての参照 (存在する場合) を新しいものに更新します。

ここまで読んですべてを理解したなら、これが非常に退屈で、実際のケースではほぼ不可能な作業であることを理解する必要があります。

オプション 2 - 動的リンカー操作 上記の例のINTERP セグメント タイプに注意してください。これは、使用する動的リンカーを指定する ASCII 文字列です。
動的リンカーの役割は、セグメントを解析し、実行時のシンボルの解決、.so ファイルからのセグメントの読み込みなど、すべての動的操作を実行することです。

ここで可能な操作は、ダイナミック リンカ コードを変更して (注: これはシステム全体の変更です!)、関数のバイナリ データをプロセス アドレス空間の特定のメモリ アドレスにロードすることです。このアプローチにはいくつかの欠点があることに注意してください。

  1. 動的リンカーへの変更が必要です
  2. ELF ファイル内の関数へのすべての参照を更新する必要があります。

オプション 3 - 動的ローダー操作 オプション 2 とほぼ同じですが、動的リンカの代わりに ld ライブラリ機能を変更します。

結論 あなたがまさにやりたいことは非常に難しく、実に退屈な作業です。私は現在、既存の共有オブジェクト ファイルに任意の関数を挿入できるツールの開発に取り組んでおり、少なくとも数週間はうまく機能することを保証します。
あなたが望むものを達成する別の方法はありませんか?これら 2 つの関数が別のアドレスに必要なのはなぜですか? おそらくもっと簡単な解決策があります...

于 2015-03-24T12:56:04.550 に答える