Ubuntu 20.10 で NASM バージョン 2.15.04 を使用して、64 ビット アセンブリで単純なスタック カナリアをコーディングしようとしています。以下のコードを実行すると、コマンドでアセンブルおよびリンクするときにセグメンテーション違反が発生しnasm -felf64 canary.asm && ld canary.o
ます。
global _start
section .text
_start: endbr64
push rbp ; Save base pointer
mov rbp, rsp ; Set the stack pointer
call _func ; Call _func
mov rdi, rax ; Save return value of _func in RDI
mov rax, 0x3c ; Specify exit syscall
syscall ; Exit
_func: endbr64
push rbp ; Save the base pointer
mov rbp, rsp ; Set the stack pointer
sub rsp, 0x8 ; Adjust the stack pointer
mov rax, qword fs:[0x28] ; Get stack canary
mov qword [rbp - 0x8], rax ; Save stack canary on the stack
xor eax, eax ; Clear RAX
mov rax, 0x1 ; Specify write syscall
mov rdi, 0x1 ; Specify stdout
mov rsi, msg ; Char* buffer to print
mov rdx, 0xd ; Length of the buffer
syscall ; Write msg
mov rax, qword [rbp - 0x8] ; Retrieve the stack canary
xor rax, qword fs:[0x28] ; Compare to original value
je _return ; Jump to _return if canary matched original
xor eax, eax ; Clear RAX
mov rax, 0x1 ; Specify write syscall
mov rdi, 0x1 ; Specify stdout
mov rsi, stack_fail ; Char* buffer to print
mov rdx, 0x18 ; Length of the buffer
syscall ; Write stack_fail
mov rax, 0x3c ; Specify exit syscall
mov rax, 0x1 ; Specify error code 1
syscall ; Exit
_return: xor eax, eax ; Set return value to 0
add rsp, 0x8 ; Reset stack pointer
pop rbp ; Get original base pointer
ret ; Return
section .data
msg: db "Hello, World", 0xa, 0x0
stack_fail db "Stack smashing detected", 0xa, 0x0
GDB を使用してデバッグすると、16 行目でセグメンテーション違反が発生していることがわかりますmov rax, qword fs:[0x28]
。
─────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x40101b <_func+4> push rbp
0x40101c <_func+5> mov rbp, rsp
0x40101f <_func+8> sub rsp, 0x8
→ 0x401023 <_func+12> mov rax, QWORD PTR fs:0x28
0x40102c <_func+21> mov QWORD PTR [rbp-0x8], rax
0x401030 <_func+25> xor eax, eax
0x401032 <_func+27> mov eax, 0x1
0x401037 <_func+32> mov edi, 0x1
0x40103c <_func+37> movabs rsi, 0x402000
─────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "a.out", stopped 0x401023 in _func (), reason: SIGSEGV
ただし、アセンブルし、libc を介して動的にリンクするとnasm -felf64 canary.asm && ld canary.o -lc -dynamic-linker /usr/lib64/ld-linux-x86-64.so.2
、実行が成功し、セグメンテーション エラーが発生しなくなりました。
Radare2 を使用して最終的なバイナリを比較すると、両方のバージョンが問題の命令を次のように同じようにアセンブルしたことがわかります。
0x00401023 64488b042528. mov rax, qword fs:[0x28]
どちらの場合も GDB は、その命令の実行時に FS レジスタが 0x0000 であることも示しています。
したがって、バイナリが libc とリンクされているかどうかに関係なく、命令バイトと FS レジスタは同一であり、コードは libc からの外部シンボルを使用していません。libc をリンクすると実行が成功するのに、libc をリンクしないとセグメンテーション違反が発生するのはなぜですか? それは可能ですか、そして/またはlibcをリンクせずにこれをどのように実装しますか?
注: この例でのスタック カナリアの関連性または必要性は、質問の焦点ではありません。