デバッガーが同じメモリに手動で書き込むことができるのに、メモリに書き込む命令を実行しようとすると、セグメンテーション違反が発生するのはなぜですか?

Why do I get a segmentation fault when attempting to execute an instruction writing to memory when the debugger can manually write to the same memory?


質問 written by AlphaCentauri @2019-01-23 10:15:28Z

: 0 : 1 : 80

これは、いくつかのスタックオーバーフローエクスプロイトの一部であるx86-64用です。

gdb出力:

=> 0x000055555555e996:  48 89 18    mov    QWORD PTR [rax],rbx

rbxをraxに格納されているメモリのアドレスに移動します(単純)。 この命令でセグメンテーション違反が発生するため、見てみましょう。

raxには何がありますか?

(gdb) i r rax
rax            0x7ffff79f4c80      140737347800192

このメモリは有効ですか?

(gdb) x/16b $rax
0x7ffff79f4c80 <_itoa_upper_digits>:    0x30    0x31    0x32    0x33    0x34    0x35    0x36    0x37
0x7ffff79f4c88 <_itoa_upper_digits+8>:  0x38    0x39    0x41    0x42    0x43    0x44    0x45    0x46

まあ、少なくとも私は読むことができます。 書いてもいいですか?

(gdb) set $rbx = 0x4141414141414141
(gdb) set {unsigned long} $rax = $rbx
(gdb) x/16b $rax
0x7ffff79f4c80 <_itoa_upper_digits>:    0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0x7ffff79f4c88 <_itoa_upper_digits+8>:  0x38    0x39    0x41    0x42    0x43    0x44    0x45    0x46

正常に動作しているようで、このセット操作は実際に実際にメモリに書き込むように見えるため、既知の無効なアドレスに対して同じ操作を試みるとエラーになります。 したがって、このメモリに対して書き込みと読み取りの両方ができるようです。

この命令を実行してみましょう。 まだ同じもの:

(gdb) x/i $pc
=> 0x55555555e996:  mov    QWORD PTR [rax],rbx
(gdb) si

Program received signal SIGSEGV, Segmentation fault.
(a=<error reading variable: Cannot access memory at address 0x376141366141355d>)
    at test.c:371
371     asm volatile("mov %rbx,(%rax);"
=> 0x000055555555e996:  48 89 18    mov    QWORD PTR [rax],rbx
   0x000055555555e999:  c3  ret    

書き込みできることを確認したばかりで失敗するのはなぜですか?

コメント 1

実行可能領域に書き込もうとしていますか?書き込み保護されている可能性が高いため、GDBはこれを認識しており、書き込み前に保護を解除します。

written by tkausl @2019-01-23 10:17:58Z

コメント 2

うーん、それは「アドレス0x376141366141355dのメモリにアクセスできません」ということで少し気が進まないかもしれませんが、そのアドレスは私にはほとんど意味がありません。

written by AlphaCentauri @2019-01-23 10:21:39Z

コメント 3

定義されたメモリ領域外の任意のメモリ位置でメモリの読み取り/書き込み/実行属性を調べることは可能ですか?定義する必要がある場合は、メモリ属性を手動で設定する必要があるようです...

written by AlphaCentauri @2019-01-23 10:25:11Z

コメント 4

pmap -x <process id>は、すべてのメモリ領域とそのアクセス属性を表示します。そのためのコマンドがGDBにあるかどうかはわかりません。

written by tkausl @2019-01-23 10:26:14Z

回答 1 written by Employed Russian @2019-01-23 15:30:14Z
3

書き込みできることを確認したばかりで失敗するのはなぜですか?

プログラムそこに書き込むことができず 、GDBのみが書き込み可能です。

書き込もうとしているアドレスは.textセクションにあり、通常はPROT_READ|PROT_EXEC使用して PROT_WRITEを使用せずに mmap .text

ただし、GDB(またはこれをptraceするプロセス)は、そのようなマッピングに書き込むことができます。 これは、GDBがブレークポイントを挿入できるようにするために必要です(多くの場合、プログラム命令を書き直す必要があります)。

コメント 1

おかげで、アドレスがitoa関数の一部としてリストされているのに気づいたはずです...しかし、このアドレス0x376141366141355dはどうなっていますか?

written by AlphaCentauri @2019-01-23 19:31:02Z

コメント 2

@AlphaCentauri: 0x376141366141355dは有効なアドレスではありません。これは非標準です(上位16ビットはビット48と同じではありません。つまり、64ビットに符号拡張された48ビットの数値ではありません)。ASCIIデータをロードし、ポインターとして使用しようとしたようです。メモリ内のポインタを何かで上書きしたのかもしれません。

written by ピーターコルド @2019-01-23 20:02:06Z