コードは次のように逆アセンブルされます。
00000000 31C0 xor eax,eax
00000002 50 push eax
00000003 682F2F7368 push dword 0x68732f2f
00000008 682F62696E push dword 0x6e69622f
0000000D 89E3 mov ebx,esp
0000000F 50 push eax
00000010 53 push ebx
00000011 89E1 mov ecx,esp
00000013 99 cdq
00000014 B00B mov al,0xb
00000016 CD80 int 0x80
の厚意によるndisasm。これらの手順を順を追って説明し、途中でスタック フレームを分析してみましょう。
xor eax,eaxeaxオペランドとそれ自体の XOR 演算の結果は常にゼロになるため、レジスタをゼロにします。push eax次に、値をスタックにプッシュします。したがって、スタックは現在、多かれ少なかれ次のようになっています (espコードの先頭にあるの値に関連して示されているオフセットは、現在指しているespスタック セルを意味します)。esp
+----------+
0 | 00000000 |
esp -4 | xxxxxxxx |
+----------+
次に、push dwordいくつかの即時値をスタックにプッシュする 2 つの命令があります。これらを実行すると、次のようになります。
+----------+
0 | 00000000 |
-4 | 68732f2f |
-8 | 6e69622f |
esp -12| xxxxxxxx |
+----------+
esp現在、スタックにプッシュされた 2 番目の即値の最後のバイトを指しています。プッシュされた値を ASCII として解釈してみましょう。現在の の値から順番に開始した場合に、スタックから読み取られる順序になりますesp。のバイト シーケンスを取得します。2f62696e2f2f7368これは ASCII では に等しくなり/bin//shます。さらに、シーケンスは 0 で終わるため、有効な C 文字列です。
espこれが、 の現在の値がレジスタに保存される主な理由ebxです。実行される実行可能ファイルへのパスが含まれています。POSIX は単純に複数のスラッシュを無視し、それらを 1 つのスラッシュとして扱うため、二重スラッシュは OS にとって問題ではありません。
次に、 と の現在の値をスタックeaxにebxプッシュします。eaxにはゼロが含まebxれており、C-string へのポインターが含まれていることがわかっています"/bin//sh"。現在、スタックは次のようになっています。
+----------+
0 | 00000000 |
-4 | 68732f2f |
-8 | 6e69622f |
ebx -12| 00000000 |
-16| (ebxVal) |
ecx esp -20| xxxxxxxx |
+----------+
レジスタの値をスタックにプッシュした後、現在のポインタespが に保存されecxます。
cdqこの場合、非常に巧妙なトリックを実行する命令です。現在の値をレジスタペアに符号拡張しeaxますedx:eax。したがって、この場合、ゼロedxの符号拡張はゼロであるため、 の値をゼロにします。もちろん、 で値をクリアすることもできますedxがxor edx, edx、その命令は 2 バイトでエンコードされており、1 バイトcdqしか使用しません。
次の命令は、値0xb(11) を の下位バイトレジスタに入れeaxます。前のケースと同様に、 を実行することもできますがmov eax, 0xb、即値は完全な 32 ビット値としてエンコードする必要があるため、5 バイトの命令になります。
int 0x80Linux でシステム コール インボーカーを呼び出します。システムコールの数eax(現在は に等しい0xbため、sys_execve関数が呼び出されます) と、 、 、 、 、および の追加ebxのecx引数edxがesi必要ediですebp。
それでは、そのシステム コールのプロトタイプを見てみましょう。
int execve(const char *filename, char *const argv[], char *const envp[]);
したがって、filename引数は に配置されますebx- それは を指し/bin//shます。argvに配置される はecx、実行される実行可能ファイルの引数の配列であり、NULL値で終了する必要があります。Intel アーキテクチャでNULLは、 は に等しく0、それをecx指します: へのポインタ/bin//sh、そしてNULL値です。envp、つまりNULL、環境値の配列を指します。これchar*は、形式の値として表現する必要がありますkey=value。
実行が成功execveすると、現在のプロセス イメージが、指定された引数で実行された、指定された実行可能ファイルのイメージに置き換えられます。この場合、/bin/shは (存在する場合) の引数で実行されます/bin//sh。
これが機能しない理由については、Michael がおそらく正しかったでしょう。最近の Linux カーネルはデータ ページを実行不可としてマークしており、それらを実行しようとするとセグメンテーション フォールトが発生します。