カナリーワード は、バッファーオーバーフローを検出して対応する方法として、プログラム内のバッファー(スタックなど)と制御データの間の境界に配置される一連のビットです。
Linuxでは、これらのカナリアは通常何ビット長ですか?
やってみよう!これは非常に単純なサンプルプログラムです。
int test(int a)
{
return a;
}
GCCでコンパイルし、アセンブリ段階でコンパイルをインターセプトします。 (-S
フラグがこれを行います。)アセンブリファイルの名前を変更して(上書きされないように)、再度コンパイルします。今回は-fstack-protector-all
フラグと-mstack-protector-guard=global
フラグも追加します。最初のフラグはすべての関数のスタックカナリアを有効にし、2番目のフラグはスレッドローカルのカナリアではなくグローバルカナリアを選択します。 (スレッドローカルのデフォルトは、実際にはおそらくより有用ですが、グローバルバージョンのアセンブリは理解しやすいです。)
生成された2つのアセンブリファイルを比較すると、次の追加が見つかります(コメントは私のものです)。
movl %edi, -20(%rbp) ; save function parameter onto stack (unrelated to canary)
movq __stack_chk_guard(%rip), %rax ; load magic value into RAX register
movq %rax, -8(%rbp) ; save RAX register onto stack (place the canary)
movl -20(%rbp), %eax ; load function parameter into EAX register for return (unrelated to canary)
movq -8(%rbp), %rcx ; load canary value into RCX register
movq __stack_chk_guard(%rip), %rdx ; load magic value into RDX register
cmpq %rdx, %rcx ; compare canary value to expected value
je .L3 ; if they are the same, jump to label .L3 (continue)
call __stack_chk_fail ; otherwise (stack corruption detected), call the handler
.L3:
leave
カナリアはすべて64ビット幅のRAX、RCX、RDXレジスタで処理されることがわかります。 (それらの32ビット版はEAX、EBX、EDXという名前になります。16ビットバージョンはAX、BX、CXと呼ばれます。8ビットバリアントのAL、BL、CLです。)別の手がかりは、カナリア(MOVQおよびCMPQ)には、64ビット命令を識別する「Q」サフィックスがあります。 (32ビット命令には「L」、16ビット命令には「W」、8ビットバージョンには「B」というサフィックスが付いています)。
したがって、カナリアは64ビット値であり、64ビットアーキテクチャ(私の場合はx86_64 GNU/Linux)で意味があると結論付けます。私にとって最も理にかなっているので、ネイティブのWordサイズが常に使用されることを期待しています。マシンで同じ実験を試して、何が得られるかを確認できます。
私がこのページで読むことができるように: Stack Smashing Protector
スタックカナリアはネイティブのWordサイズであり、ランダムに選択した場合、攻撃者は2 ^ 32または2 ^ 64の組み合わせの中から適切な値を推測する必要があります
使用されるビット数は、プロセッサのワードサイズと等しくなければなりません。したがって、32ビットプロセッサを使用している場合、そのワードサイズは32なので、カナリアワードは32ビット長です。