コンパイル
C言語ソース
#include <stdio.h>
void foo() {
printf("hello from foo\n");
}
int main() {
foo();
printf("back in main\n");
return 0;
}
アセンブリ言語へコンパイル
C言語コードをアセンブリ言語にコンパイルするために、gccコマンドを使用します。
gcc -S -masm=intel test.c -o test.s
アセンブリ言語ソース
コンパイルされたアセンブリは次のようになっています:
.file "test.c"
.intel_syntax noprefix
.text
.section .rdata,"dr"
.LC0:
.ascii "hello from foo\0"
.text
.globl foo
.def foo; .scl 2; .type 32; .endef
.seh_proc foo
foo:
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
sub rsp, 32
.seh_stackalloc 32
.seh_endprologue
lea rax, .LC0[rip]
mov rcx, rax
call puts
nop
add rsp, 32
pop rbp
ret
.seh_endproc
.section .rdata,"dr"
.LC1:
.ascii "back in main\0"
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
sub rsp, 32
.seh_stackalloc 32
.seh_endprologue
call __main
call foo
lea rax, .LC1[rip]
mov rcx, rax
call puts
mov eax, 0
add rsp, 32
pop rbp
ret
.seh_endproc
.def __main; .scl 2; .type 32; .endef
.ident "GCC: (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.2.0"
.def puts; .scl 2; .type 32; .endef
動作結果
C:\Users\nanashi\test>gcc test.s -o test.exe
C:\Users\nanashi\test>test
hello from foo
back in main
C:\Users\nanashi\test>
jmpへ書き換える
次にfoo()
関数の呼び出しをjmp命令
を使って行う方法を示します。変更前のアセンブリでは、foo()関数はcall命令
で呼ばれていますが、これをjmp命令
に変更します。
書き換え箇所
call foo
lea rax, [rip + back_here]
push rax
jmp foo;
back_here:
動作結果
変更後も、以前と同じ出力が得られます。
C:\Users\nanashi\test>gcc test.s -o test.exe
C:\Users\nanashi\test>test
hello from foo
back in main
C:\Users\nanashi\test>
call模倣部分の説明
lea rax, [rip + back_here]
jmp 命令
で foo関数
に飛んだ後、foo関数
内の処理が終わったら、back_here ラベルに戻ります。
※調べたところ、back_here
は絶対番地ではなくrip
からの差分(オフセット)であり、rip+back_here
の結果はback_here
の場所になるらしいのですが、頭がこんがらがり、自分でも理解が怪しいです。
詳しい方がおられましたらご教示下さい。1時間ほど考え込みましたが、理解したようなしてないような感じです。
push rax
次に、計算した戻り先アドレス(back_hereの位置)をスタックにプッシュします。このアドレスは、foo関数
から戻った後に実行されるべき命令の場所になります。
jmp foo
最後に、foo関数
へ跳びます。ここで、jmp命令
は単純にfoo関数
に移るだけです。call命令
のように戻り先アドレスを自動でスタックに積まないので、戻り先を手動でスタックへ入れる必要があるのです。
(今日は熱を出して会社を休んだのに自分は一体何をしているのでしょう。。。)