私はHacking、The Art of Exploitationを読み始め、メモリチェックに関するいくつかの点について混乱しました。
メインを分解すると、個別の組み立て説明書があるメモリ全体の出力が出ますよね?各命令は必ずしもメモリアドレスを1ずつ増やす必要はないので、最後に<main + 1>と<main + 3>を追加できます。だからコマンドはこのスクリーンショットプログラムの最初の20命令を表示します。各メッセージには何バイトの情報が含まれていますか?
今混乱しています。 ir eipを使用するとき、このレジスタの位置は0x8048384です(それでmainの最初の命令として保存されますか?)。スクリーンショット。隣の値が保存する値ですがここで知りたいのですが0x00fc45c7を保存しますが、上記の出力にはこの命令はありませんか?私はそれがプログラムの次のコマンドを含むメモリを指すべきだと思います。
今最大の混乱が来ます。 $ eipが保存されているメモリを観察し、同時に複数のセルを観察できます。このスクリーンショット。しかし、x / 2xを使用したスクリーンショットを見ると、メモリに2つの値が格納されていますが、どちらもサイズが4バイトであることがわかります。その後、x / 12を使用すると、突然0x8048384に4つの単語があり、他の4つの単語0x8048394がありますか?
使用する単位によっては、メモリアドレスに格納されている値がどのように異なるのか理解できないようです。また、各メモリアドレスには1バイトの情報のみを含めるべきだと考えました。
質問の説明が必要な場合は投稿してください。英語は私の母国語ではなく、私の説明が正しいかどうかわかりません。
事前に助けてくれてありがとう。
答え1
各メッセージには何バイトの情報が含まれていますか?
1バイトから複数バイトまでの命令push %ebp
には固定長がありません。たとえば、コマンドの出力は
objdump -d a.out
コマンドの長さをより明確にします(コマンドが何であるかを示すため、便利です)。
08048400 <main>:
8048400: 55 push %ebp
8048401: 89 e5 mov %esp,%ebp
8048403: 83 ec 08 sub $0x8,%esp
8048406: 90 nop
8048407: c7 45 fc 00 00 00 00 movl $0x0,0xfffffffc(%ebp)
804840e: 89 f6 mov %esi,%esi
8048410: 83 7d fc 09 cmpl $0x9,0xfffffffc(%ebp)
今混乱しています。 ir eipを使用すると、このレジスタの位置を知ることができます。
(gdb) disassemble main
Dump of assembler code for function main:
0x8048400 <main>: push %ebp
0x8048401 <main+1>: mov %esp,%ebp
0x8048403 <main+3>: sub $0x8,%esp
0x8048406 <main+6>: nop
0x8048407 <main+7>: movl $0x0,0xfffffffc(%ebp)
0x804840e <main+14>: mov %esi,%esi
0x8048410 <main+16>: cmpl $0x9,0xfffffffc(%ebp)
...
(gdb) i r eip
eip 0x8048406 0x8048406
私のバージョンでは、プログラムがeip
プログラムを停止するコマンドを指していますnop
。b main
gdb
$eip
今最大の混乱が来ます。メモリがどこに保存されているかを確認できます。
(gdb) x/10b $eip
0x8048406 <main+6>: 0x90 0xc7 0x45 0xfc 0x00 0x00 0x00 0x00
0x804840e <main+14>: 0x89 0xf6
上記の出力を覚えてくださいobjdump
。
8048406: 90 nop
8048407: c7 45 fc 00 00 00 00 movl $0x0,0xfffffffc(%ebp)
804840e: 89 f6 mov %esi,%esi
10バイトダンプには、$eip
90、c7 45 fc 00 00 00 00、89 f6など、次の3つの命令が表示されます。
使用する単位によっては、メモリアドレスに格納されている値がどのように異なるのか理解できないようです。また、各メモリアドレスには1バイトの情報のみを含めるべきだと考えました。
gdb
さまざまな幅または論理単位で確認できます。x/3i
たとえば、次の3つのコマンドを表示するとします。
(gdb) x/3i $eip
0x8048406 <main+6>: nop
0x8048407 <main+7>: movl $0x0,0xfffffffc(%ebp)
0x804840e <main+14>: mov %esi,%esi
使用する幅の最良の選択は状況によって異なります。 8ビットシステムではバイトを確認する必要があります。 64ビットシステムのメモリアドレスがg
ジャンボか8バイトかを確認する必要があります。選択範囲の幅を変更すると、表示されるgdb
数字が変わる可能性があります。幅が異なるとビットパターンが変わり、数字も変わることがあるからです。さらに、CPUのエンディアンは状況を複雑にすることができます。
考慮する:
#include <stdio.h>
char *pointer = "test";
int main(void) {
printf("%s\n", pointer);
}
チェックするときに異なる幅を選択すると、異なるpointer
ビットパターンから異なる数字が得られます。
(gdb) p pointer
$1 = 0x8048488 "test"
(gdb) x/4c pointer
0x8048488 <_IO_stdin_used+4>: 116 't' 101 'e' 115 's' 116 't'
(gdb) x/4t pointer
0x8048488 <_IO_stdin_used+4>: 01110100 01100101 01110011 01110100
(gdb) x/t (int *)pointer
0x8048488 <_IO_stdin_used+4>: 01110100011100110110010101110100
もう1つの問題は、Intelシステムがリトルエンディアンであることです(Intelではこれを問題と見なすことはできません)。したがって、詳細に見ると、これらの複雑さによってビットパターンがx/4t
わずかに異なります。ビッグエンディアンモードに入るようにx/t (int *)
指示できます。gdb
(gdb) set endian big
The target is assumed to be big endian
(gdb) x/t (int *)pointer
0x88840408: Cannot access memory at address 0x88840408
しかし、今メモリアドレスを逆に入力する必要があります!このhtonl
関数呼び出しは、32ビットのリトルエンディアン値をビッグエンディアン値に変換します。
$ cfu 'printf("%d\n", htonl(0x88840408))'
134513800
その後、gdb
そのアドレスを試すことができます。
(gdb) x/t (int *)134513800
0x8048488 <_IO_stdin_used+4>: 01110100011001010111001101110100
4桁のビットパターンがビッグエンディアン形式と一致します。これらの結果を表に表示すると便利です。
--> big endian reads this way
t e s t
x/4t (char) 01110100 01100101 01110011 01110100
big endian 01110100 01100101 01110011 01110100
<-- yaw siht sdaer naidne elttil dna
t s e t
little endian 01110100 01110011 01100101 01110100
test
文字列のビットパターンを表示する3つの方法(いくつかの方法の1つ)があります
gdb
。入力を分割するために指定したビット数gdb
とシステムのバイト順序に応じて、同じビットパターンに異なる数字が表示されます。