1

私は、Ubuntu Linux 12.04、Intel x86_64 アーキテクチャ用のカスタム PE ハンドラーに取り組んできましたbinfmt(これがおなじみの場合は、このプロジェクトに関連するいくつかの質問を既に投稿しています)。私が提供している情報の量が過剰である場合は、事前にお詫び申し上げます。

binfmtハンドラーはかなり標準的です。PE ヘッダーとセクションを読み込み、セクション テーブルで指定されたアドレスのユーザー空間メモリにそれらのセクションを書き込みます。そして、すべての準備が整ったら、私は電話します

start_thread(regs, entry_addr, current->mm->start_stack);

組み込みの Linux ハンドラとまったく同じです。私の場合、regs = 0xcf7dffb4entry_addr = 0x401000、およびstart_stack = 0xbffff59b

Intel x86 アセンブリに次のコードがあります。

push ebp
mov ebp, esp

mov eax, 4
add eax, 5

pop ebp
ret

このプログラムをfasmで PE 形式の実行可能ファイル (math1.exe)にコンパイルbinfmtし、insmod. このプログラムを でデバッグするとgdb、次のように表示されます。

(gdb) set disassembly-flavor intel
(gdb) x/6i 0x401000
    0x401000:   push   ebp
    0x401001:   mov    ebp,esp
    0x401003:   mov    eax,0x4
    0x401008:   add    eax,0x5
    0x40100b:   pop    ebp
    0x40100c:   ret

コードが正しいアドレスにロードされていることがわかります。それで:

(gdb) run
Starting program: /media/sf_Sandbox/math1.exe 

Program received signal SIGSEGV, Segmentation fault.
0x0040100c in ?? ()

レジスタ ダンプを実行すると、次のようになります。

(gdb) info registers
eax            0x9  9
ecx            0x81394e8    135501032
edx            0x8137808    135493640
ebx            0x8139548    135501128
esp            0xbfffe59b   0xbfffe59b
ebp            0x0  0x0
esi            0x81394e8    135501032
edi            0x2f7ff4 3112948
eip            0x40100c 0x40100c
...other registers...

コードが実行されたことがわかります。なぜならeax = 0x9、そうあるべきだからです。ただし、表面的には、これがステートメントでセグメンテーション違反になる理由は見つかりませんret。調べdmesgて、見つけた

math1.exe[1864] general protection ip:40100c sp:bffff5bd error:0

しかし、これを引き起こしている可能性のあるものに関するドキュメントはほとんど見つかりませんでした。同じアセンブラで ELF 形式にコンパイルされた同じコードが問題なく実行されるため、問題はコード自体ではないことはわかっています。

この問題に関する私の現在の理論は次のとおりです。

  1. ハンドラーでスタック ポインターをいじることはあまりありません。組み込みの Linux ハンドラ (ELF、a.out、およびフラット形式の 3 つを挙げます) には、、、および引数を処理する関数がありcreate_*_tables()ます。テスト プログラムは入力を受け取らないため、最初はこの関数を含めませんでしたが、関数を (フラット ハンドラーから) 実装しても、これまでのところ問題は解決しません。(盲目的に貼り付けて他のモジュールから関数を呼び出すのは悪い考えであることはわかっていますが、その関数の a.out バージョンとフラット バージョンは本質的に同一であるため、実行形式にはあまり依存していないようです。試してみます。)argcargvenvpcreate_flat_tables()
  2. の実行前後に発生する一連の関数呼び出しに関するこの記事を見つけましたmain()。math1.exeのobjdumpには、上記のアセンブリ コードのみが含まれていますが、 ELF 形式へのアセンブリ (ファイルを生成する) およびリンク(ELF バイナリを取得する)objdump後の同じプログラムの には、記事に記載されている他の関数が含まれています ( 、など)。おそらく、これらの機能は、私が以前考えていたよりも、Linux プラットフォームで必須です。*.ogcc_start()__libc_start_main()

説明/提案/追加のトラブルシューティング手順を探しています。前もって感謝します!

4

1 に答える 1

3

_startandシーケンスの少なくとも 1 つの側面を実装する必要があり__libc_start_mainます: _exit syscall を呼び出します。execve によって作成されたフレームから「ret」を実行するだけで、プロセスが正常に終了することを期待することはできません。プロセスを終了するためにメインから戻ることは C の機能であり、プログラムは C ではありません。

私の記憶はsyscallインターフェースで少し曖昧ですが、次のようになると思います:

  1. %eax をシステムコール番号に設定します ( _NR_exit)
  2. %ebx を最初の引数 (終了コード) に設定します
  3. 整数 $0x80

%ecx、%edx への追加の引数 ... 順序がわかりません。しかし、_exit() は引数を 1 つしかとらず、%ebx に含まれていると確信しています。

于 2012-08-20T22:25:38.500 に答える