linux 64ビットアセンブリと32ビットアセンブリの違いと互換性
6309 ワード
32ビットアセンブリ「Linuxアセンブリ入門-32ビット」を参照
一、違うシステム呼び出し番号が異なります.例えばx 86のsys_writeは4,sys_exitは1です.x 86_64のsys_writeは1、sys_exitは60です.linuxシステム呼び出し番号は、実際には/usr/include/asm/unistd_に定義されています.32.hおよび/usr/include/asm/unistd_64.h中. システム呼び出しで使用されるレジスタが異なり、x 86_64ではeaxに対応するraxを使用してシステム呼び出し番号を渡すが、x 86_64では、x 86のebx/ecx/edxではなく、rdi/rsi/rdxを使用して最初の3つのパラメータが伝達される. 32ビットプログラムの場合、int$0 x 80を呼び出してシステム呼び出しに入り、システム呼び出し番号をeaxに入力し、各パラメータはebx、ecx、edxの順序でレジスタに渡され、システム呼び出し戻り値はeaxレジスタに格納される. 64ビットプログラムの場合、syscallを呼び出してシステム呼び出しに入り、システム呼び出し番号をraxに入力し、各パラメータはrdi、rsi、rdxの順序でレジスタに渡され、システム呼び出し戻り値はraxレジスタに格納される. システムコール「int 80」 ではなく「syscall」を使用 32ビットと64ビットプログラムのアドレス空間範囲は異なる.
例:
32ビット
64ビット
二、互換性
ハードウェア命令の互換性のため、32ビットのプログラムはユーザー状態で何の影響も受けずに実行され、カーネルは0 x 80番割り込みを32ビットプログラムのシステム呼び出しサービスとして保持しているため、32ビットプログラムは安全に0 x 80番割り込みをトリガーしてシステム呼び出しを使用することができ、カーネルは0 x 80割り込みのために別の新しいシステム呼び出しテーブルを配置しているため、したがって、データ型を一貫した64ビットタイプに安全に変換することができ、アプリケーションレベルに加えて2つのcライブラリが提供され、64ビットと32ビットプログラムを異なるライブラリにリンクすることができる.
三、インラインアセンブリ
1.プログラムコードと問題
まず、以下の簡単なCプログラム(test.c)を見ます.
このプログラムはwrite関数を呼び出し、インタフェースは次のとおりです.
fdが0であるとコンソールに出力されることを示す.したがって、上記のプログラムの実行結果は、6の長さの文字列「Hello」をコンソールに出力することである.
コンソールでgcc testを呼び出す.c、正しく出力できます.
アセンブリコードの下でのシステム呼び出しプロセスをよりよく理解するために、上記のコードをインラインアセンブリのフォーマットに書き換えることができる(『asmインラインアセンブリでシステム呼び出しを実現する』)
ここで、4はwrite関数のシステム呼び出し番号であり、ebx/ecx/edxはシステム呼び出しの最初の3つのパラメータである.
しかしながら、gcc test_を実行するasm_A.cコンパイル後、プログラムを実行すると、プログラムに出力がないことがわかります.奇妙な問題は、次のようなtestを採用するとasm_B.cの書き方は、プログラムが正常に出力できる.
出力は次のとおりです.
2つのコードの唯一の違いはtestです.asm_A.cのstrはスタック空間に格納され、test_asm_B.cのstrはスタック空間に格納される.
では、なぜ格納場所の違いが全く異なる結果をもたらすのでしょうか.
2.原因分析
上記コードを32ビットでコンパイルする、すなわちgcc test_asm_A.c-m 32とgcc test_asm_B.c-m 32では、2つのコードが正しく出力されていることがわかります.これは,上記のコードを32ビットでコンパイルすることで,正しい結果が得られることを示している.
-m 32フラグがない場合、gccはデフォルトで64ビットでコンパイルされます.32ビットと64ビットのプログラムは、コンパイル時に次のような違いがあります(上記). 32ビットと64ビットプログラムのアドレス空間範囲は異なる. 32ビットと64ビットプログラムのシステム呼び出し番号は異なり、本例のwriteのように32ビットシステムでは呼び出し番号4、64ビットシステムでは1である. 32ビットプログラムの場合、int$0 x 80を呼び出してシステム呼び出しに入り、システム呼び出し番号をeaxに入力し、各パラメータはebx、ecx、edxの順序でレジスタに渡され、システム呼び出し戻り値はeaxレジスタに格納される. 64ビットプログラムの場合、syscallを呼び出してシステム呼び出しに入り、システム呼び出し番号をraxに入力し、各パラメータはrdi、rsi、rdxの順序でレジスタに渡され、システム呼び出し戻り値はraxレジスタに格納される.
上の2つのコードを見ると、int$0 x 80を呼び出してシステム呼び出しに入りますが、64ビットでコンパイルすると、次のような異常が発生します.プログラムのアドレス空間は64ビットアドレス空間である. 0 x 80番割り込みは32ビットシステム呼び出し関数であるため、システム呼び出しは32ビット方式で解釈される.すなわち、すべてのレジスタは32ビット低い値のみを考慮する.
プログラムに入力された各パラメータを見ると、システム呼び出し番号(4)、1番目と3番目のパラメータ(0と6)は32ビット以内であるが、strのアドレスは64ビットアドレスであり、0 x 80システム呼び出しでは32ビット低いのみが考慮される.
これでtest_asm_A.cは正しく実行できませんがtest_asm_B.cが正しく実行できる理由は明らかです. test_asm_A.cではstrはスタック空間に格納されているが,スタック空間はシステムの高位から32ビット低いアドレスのみをとり,エラーアドレスが得られる. test_asm_B.cでは、strはスタック空間に格納され、スタック空間はシステムの下位で開始される.このような小さなプログラムでは、strアドレスの上位32ビットは0であり、下位32ビットのみがゼロ値ではないため、遮断エラーは発生しない.
メモリスタックの場合は、Computer_を表示できます.Architecture/x 86/命令システム/下内容
表示、test_asm_B.cが正しく実行されるのは偽物にすぎない.スタック空間は低位から始まるため,開拓空間が多すぎてスタック空間も高位に入ると,このコードも同様にエラーになる可能性がある.
3.64ビットシステムのシステム呼び出しコード
64ビットシステムで正しく出力できるasmシステム呼び出しコードを与えます.
一、違う
例:
32ビット
global _start
_start:
jmp short ender
starter:
xor eax, eax ;clean up the registers
xor ebx, ebx
xor edx, edx
xor ecx, ecx
mov al, 4 ;syscall write
mov bl, 1 ;stdout is 1
pop ecx ;get the address of the string from the stack
mov dl, 5 ;length of the string
int 0x80
xor eax, eax
mov al, 1 ;exit the shellcode
xor ebx,ebx
int 0x80
ender:
call starter ;put the address of the string on the stack
db 'hello',0x0a
64ビット
global _start ; global entry point export for ld
_start:
jump short string ; get message addr
code:
; sys_write(stdout, message, length)
pop rsi ; message address
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rdx, 13 ; message string length + 0x0a
syscall
; sys_exit(return_code)
mov rax, 60 ; sys_exit
mov rdi, 0 ; return 0 (success)
syscall
string:
call code
db 'Hello!',0x0a ; message and newline
二、互換性
ハードウェア命令の互換性のため、32ビットのプログラムはユーザー状態で何の影響も受けずに実行され、カーネルは0 x 80番割り込みを32ビットプログラムのシステム呼び出しサービスとして保持しているため、32ビットプログラムは安全に0 x 80番割り込みをトリガーしてシステム呼び出しを使用することができ、カーネルは0 x 80割り込みのために別の新しいシステム呼び出しテーブルを配置しているため、したがって、データ型を一貫した64ビットタイプに安全に変換することができ、アプリケーションレベルに加えて2つのcライブラリが提供され、64ビットと32ビットプログラムを異なるライブラリにリンクすることができる.
三、インラインアセンブリ
1.プログラムコードと問題
まず、以下の簡単なCプログラム(test.c)を見ます.
#include
int main(){
char str[] = "Hello
";
write(0, str, 6);
return 0;
}
このプログラムはwrite関数を呼び出し、インタフェースは次のとおりです.
int write(int fd /* */, const char* src /* */, int len /* */)
fdが0であるとコンソールに出力されることを示す.したがって、上記のプログラムの実行結果は、6の長さの文字列「Hello」をコンソールに出力することである.
コンソールでgcc testを呼び出す.c、正しく出力できます.
アセンブリコードの下でのシステム呼び出しプロセスをよりよく理解するために、上記のコードをインラインアセンブリのフォーマットに書き換えることができる(『asmインラインアセンブリでシステム呼び出しを実現する』)
test_asm_A.c
int main(){
char str[] = "Hello
";
asm volatile(
"int $0x80
\t"
:
:"a"(4), "b"(0), "c"(str), "d"(6)
);
return 0;
}
ここで、4はwrite関数のシステム呼び出し番号であり、ebx/ecx/edxはシステム呼び出しの最初の3つのパラメータである.
しかしながら、gcc test_を実行するasm_A.cコンパイル後、プログラムを実行すると、プログラムに出力がないことがわかります.奇妙な問題は、次のようなtestを採用するとasm_B.cの書き方は、プログラムが正常に出力できる.
test_asm_B.c
#include
int main(){
char *str = (char*)malloc(7 * sizeof(char));
strcpy(str, "Hello
");
asm volatile(
"int $0x80
\t"
:
:"a"(4), "b"(0), "c"(str), "d"(6)
);
free(str);
return 0;
}
出力は次のとおりです.
[root@tsinghua-pcm C]# gcc test_asm_B.c
test_asm_B.c: ‘main’ :
test_asm_B.c:4:2: : ‘strcpy’ [ ]
strcpy(str, "Hello
");
^
[root@tsinghua-pcm C]# ./a.out
Hello
[root@tsinghua-pcm C]# cp inline_assembly.c test_asm_A.c
[root@tsinghua-pcm C]# gcc test_asm_A.c
[root@tsinghua-pcm C]# ./a.out
[root@tsinghua-pcm C]#
2つのコードの唯一の違いはtestです.asm_A.cのstrはスタック空間に格納され、test_asm_B.cのstrはスタック空間に格納される.
では、なぜ格納場所の違いが全く異なる結果をもたらすのでしょうか.
2.原因分析
上記コードを32ビットでコンパイルする、すなわちgcc test_asm_A.c-m 32とgcc test_asm_B.c-m 32では、2つのコードが正しく出力されていることがわかります.これは,上記のコードを32ビットでコンパイルすることで,正しい結果が得られることを示している.
[root@tsinghua-pcm C]# gcc test_asm_A.c -m32
[root@tsinghua-pcm C]# ./a.out
Hello
[root@tsinghua-pcm C]# gcc test_asm_B.c -m32
test_asm_B.c: ‘main’ :
test_asm_B.c:4:2: : ‘strcpy’ [ ]
strcpy(str, "Hello
");
^
[root@tsinghua-pcm C]# ./a.out
Hello
-m 32フラグがない場合、gccはデフォルトで64ビットでコンパイルされます.32ビットと64ビットのプログラムは、コンパイル時に次のような違いがあります(上記).
上の2つのコードを見ると、int$0 x 80を呼び出してシステム呼び出しに入りますが、64ビットでコンパイルすると、次のような異常が発生します.
プログラムに入力された各パラメータを見ると、システム呼び出し番号(4)、1番目と3番目のパラメータ(0と6)は32ビット以内であるが、strのアドレスは64ビットアドレスであり、0 x 80システム呼び出しでは32ビット低いのみが考慮される.
これでtest_asm_A.cは正しく実行できませんがtest_asm_B.cが正しく実行できる理由は明らかです.
メモリスタックの場合は、Computer_を表示できます.Architecture/x 86/命令システム/下内容
表示、test_asm_B.cが正しく実行されるのは偽物にすぎない.スタック空間は低位から始まるため,開拓空間が多すぎてスタック空間も高位に入ると,このコードも同様にエラーになる可能性がある.
3.64ビットシステムのシステム呼び出しコード
64ビットシステムで正しく出力できるasmシステム呼び出しコードを与えます.
//test_asm_C.c
int main(){
char str[] = "Hello
";
// :64 ,write 1
asm volatile(
"mov %2, %%rsi
\t"
"syscall"
:
:"a"(1), "D"(0), "b"(str), "d"(6)
);
return 0;
}