2010-12-29 15 views
2
char program[3] = { 0x6a, 0x0a, 0xC3 }; //push 10; ret; 
char (*p)(void) = program; 
printf("char = %c \n", p()); 

このコードはクラッシュします。どうして?x86 asmクラッシュアプ​​リケーション

次のコードは、[OK]を動作します:

char program[3] = { 0x90, 0x90, 0xC3 }; //nop; nop; ret 
void (*p)(void) = program; 
p(); 
+1

プッシュ+ RET ==ジャンプでx86-32上で実行しているよう。 – ruslik

答えて

3

まず、アセンブリで「何かを行う」ことができ、動作することを期待することはできません。アプリケーションバイナリインターフェイスと呼ばれるものがあり、オペレーティングシステム全体のプログラムと実際にコード内のプログラムの動作方法を指定しています。

たとえば、ほとんどのx86-32プラットフォームでは、共通の規則の1つに、eaxに戻り値を含める必要があります。スタックにプッシュされる一連の値もあります(関数のスタックフレームと呼ばれます)。もう1つは、特定のレジスタを保存する必要がある一方、他のレジスタ(スクラッチレジスタと呼ばれることもあります)にはゴミが残ることがあります。

要するに、これらのルールに違反すると、状況が悪くなり、OSがあなたのために整理します。それは実際にあなたのプロセッサだけでなくオペレーティングシステムにも依存します。たとえLinuxやwindowsでもx64が異なる場合などです。

私はまた、char programを追加する必要がありますプログラムではない、それは既存のプログラムの中の機能です。

最後に、nop, nop, retは何もしません。実際にスタックフレームに何もしていないので、これらの命令を実行して戻ることができます。

+0

戻り値がどこからスタックに追加されたのか分かりません。ありがとう –

+0

入力値がスタックに追加されました。私は、あなたがノップスレッドのアイデアを探っていることを推測しています。通常、バッファオーバーフローと呼ばれるパラメータのサイズを正しくチェックしない関数を悪用する方法です... –

+0

いいえ、私はちょうどこれはCコードの問題ではなく、命令そのものであることを示しています。 –

5

あなたはそれから飛び出ることで、クリーンアップあなたのスタックに必要があるため。 ret命令で詳細を読む - スタックからの値でcs:ipをロードします!

+0

技術的に 'retf'だけがCSとEIPの両方をロードします(遠い戻り値) - ' retn'はEIPをポップします(そして最近の 'ret'のためにほとんどのアセンブラが出力します)。あなたがほとんどユーザ空間で見ることのできない 'iret 'のような命令を無視する)。 –

3

呼び出し規約について学ぶことを強くお勧めします。 32ビットx86の場合は、スタックを変更する機能は、より多くのように、この(CDECL)になります。

push ebp 
mov ebp, esp ; or ENTER instruction for the push+mov 

; Function code! 

mov esp, ebp 
pop ebp ; or LEAVE instruction for the mov+pop 
ret 

mov esp, ebpはリターンを意味し、それは関数の先頭にあった道に、あなたのスタックのリターンを保証しますアドレスがスタック上にあり、retがロードされてジャンプする。

関数内のローカル変数は、スタックに配置されます(sub esp, 0x4は、1つの32ビット変数に領域を割り当て、[esp + 0]として使用可能)。

64ビットx86(Itaniumではありません)には独自の呼び出し規約があります。その後、ファストコールなどがあります。

Wikipedia Article on x86 Calling Conventionsをご覧ください。

+0

+1実際に呼び出し規約を引用しています。 –

0

そして脇にあなたのプログラムの目的(10の出力を期待)に基づいて、スタックのクリーンアップから...

char program[3] = { 0x6a, 0x0a, 0xC3 }; //push 10; ret; 
char (*p)(void) = program; 
printf("char = %c \n", p()) 

...10を押すべきではない関数の戻り値はAX(16ビット)に格納され、EAXの場合は32ビット

渡すパラメータが意図されている場合、スタックのクリーンアップは__fastcall (またはパスカル)であれば、クリーンアップを行う必要があるのは呼び出されたルーチンです。それはCの呼び出し規約(_cdecl)であれば、それはそれは*をうまくいくスタック

1
char program[] = { 
    0x55,    /* push %ebp */ 
    0x89, 0xe5,  /* mov %esp, %ebp */ 
    0x6a, 0x0a,  /* push $0xa */ 
    0x8b, 0x04, 0x24, /* mov (%esp), %eax */ 
    0x89, 0xec,  /* mov %ebp, %esp */ 
    0x5d,    /* pop %ebp */ 
    0xc3,    /* ret */ 
}; 

をクリーンアップする必要があり、発信者です。あなたのスタックレイアウトを台無しにしているので、プログラムがクラッシュします。

*限りprogramは必ずしも真ではありませんメモリの実行可能部分、に存在し、そしてあなたは、一般的なCの呼び出し規約

+0

+1をコードとして実行することを言い表す。 –

関連する問題