私はLinuxシステムコールメカニズムを理解しようとしています。本を読んでいますが、終了関数は次のようになります(gdbを使用)。
mov $0x0,%ebx
mov $0x1,%eax
80 int $0x80
私はこれがシャットダウンシステムコールであることを知っていますが、私のDebianでは:
jmp *0x8049698
push $0x8
jmp 0x80482c0
たぶん誰かがなぜ違うのか説明できますか? 0x80482c0でdisasを実行しようとすると、gdbは次のように出力します。
指定されたアドレスを含む関数はありません。
ありがとうございます!
答え1
コード呼び出しは、実際には実行できない可能性があるexit()
Cライブラリ(libc)関数にリンクされています。exit()
int $0x80
コードexit()
の関数呼び出しは、実際には call
プログラム接続テーブル(PLT)の命令にコンパイルされます。ランタイムダイナミックリンカは、ファイルを/usr/lib/libc.so
メモリにマッピングする役割を果たします。それはCライブラリです。ランタイムダイナミックリンカはまた、PLTのエントリを修正して最終的にmapからコードを呼び出します/usr/lib/libc.so
。
私が知っている限り(私はArch Linuxを使用しています)、2番目の3つの命令はPLTエントリであり、gdb
ステップごとに入ると "exit @ plt"を呼び出します。jmp 0x80482c0
別のアドレスにジャンプし、最終的にlibc.so
コードにジャンプします。
かなり複雑な練習でこれを自分で証明することができます。まず、「exit @ plt」アドレスをgdb
示すPLTテーブルエントリのアドレスを取得しました。jmp *0x8049698
私のx86 Arch Linuxシステムでは:
(gdb) disassemble 0x8048310,+20
Dump of assembler code from 0x8048310 to 0x8048324:
0x08048310 <exit@plt+0>: jmp *0x80496e8
0x08048316 <exit@plt+6>: push $0x10
0x0804831b <exit@plt+11>: jmp 0x80482e0
それではreadelf -e _program_ > elf.headers
。elf.headers
ファイルを表示する「セクションタイトル:」というテキスト行を見つけることができ、セクションタイトルのどこかに次の内容が表示されます。
[ 9] .rel.dyn REL 08048290 000290 000008 08 A 5 0 4
[10] .rel.plt REL 08048298 000298 000020 08 AI 5 12 4
[11] .init PROGBITS 080482b8 0002b8 000023 00 AX 0 0 4
[12] .plt PROGBITS 080482e0 0002e0 000050 04 AX 0 0 16
"exit@plt"はアドレス0x8048310にあります。 「.rel.plt」セクションにあります。 「.rel.plt」はおそらく「relocator link table」を意味します。
int $0x80
今、私たちはおそらく存在しない部分に到達します。ldd _program_
ない。同様に、Arch Linux x86は次のように言います。
linux-gate.so.1 (0xb77d9000)
libc.so.6 => /usr/lib/libc.so.6 (0xb7603000)
/lib/ld-linux.so.2 (0xb77da000)
「linux-gate.so.1」は見えますか?これには、システムコールを実行する実際のコードが含まれています。それであってもint $0x80
、指示であったりsysenter
、他のものであってもよい。 Linuxカーネルは、実際のコードと共にプロセスのアドレス空間に「小さな共有ライブラリ」を置き、小さな共有ライブラリのアドレスをELF「補助ベクトル」に渡すようになっています。詳細を確認してくださいman vdso
。動的リンカーは/lib/ld-linux.so.2
ELF補助ベクトルの詳細を知り、最終的にlinx-gate.so.1
PLTのどこかにアドレスを配置するので、実際のC関数呼び出しは効率的なシステム呼び出しを生成します。
何度も電話をかけると、通話するたびにldd _program_
表示されるアドレスがlinux-gate.so.1
異なります。実際、カーネルは、独自のコードを実行するためにスタックの場所を知る必要があるマルウェアを混同するために、毎回同じアドレスにスタックの上部を配置しません。