coLinux上でプログラムを実行してみる。
# ./pointer 8059: binding file ./pointer to /lib/tls/i686/cmov/libc.so.6: normal symbol `printf' [GLIBC_2.0] 5
なぜかローダーのメッセージが出ているけど気にしない事にする。それよりも、coredumpせずに実行できてしまった事が驚き。仕方ないので宣言部分を適当にいじる。
int *n = (int *)0xAAAAAAAA;
# ulimit -c unlimited # ./pointer Segmentation fault (core dumped)
coredumpしたので中身をみていく。
# gdb pointer core GNU gdb 6.4-debian Copyright 2005 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". Failed to read a valid object file image from memory. Core was generated by `./pointer'. Program terminated with signal 11, Segmentation fault. warning: Can't read pathname for load map: Input/output error. Reading symbols from /lib/tls/i686/cmov/libc.so.6...done. Loaded symbols for /lib/tls/i686/cmov/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x08048386 in main ()
スタックトレースはbt。
(gdb) bt #0 0x08048386 in main ()
main関数で落ちてるからな・・・。
レジスターはinfo registersで見るらしい。
(gdb) info registers eax 0xaaaaaaaa -1431655766 ecx 0xbfa177cc -1079937076 edx 0x1 1 ebx 0xb7f04adc -1208988964 esp 0xbfa17710 0xbfa17710 ebp 0xbfa17738 0xbfa17738 esi 0xbfa177c4 -1079937084 edi 0xbfa17750 -1079937200 eip 0x8048386 0x8048386 <main+38> eflags 0x10282 66178 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
なるほど。eaxというレジスターにsegmentation faultの原因になったアドレスが入っている。
(gdb) x/i $eip 0x8048386 <main+38>: movl $0x5,(%eax)
%eaxのアドレスへ0x5をコピー(move long-word??)してるところで落ちてる。
(gdb) x/20i main 0x8048360 <main>: push %ebp 0x8048361 <main+1>: mov %esp,%ebp 0x8048363 <main+3>: sub $0x18,%esp 0x8048366 <main+6>: and $0xfffffff0,%esp 0x8048369 <main+9>: mov $0x0,%eax 0x804836e <main+14>: add $0xf,%eax 0x8048371 <main+17>: add $0xf,%eax 0x8048374 <main+20>: shr $0x4,%eax 0x8048377 <main+23>: shl $0x4,%eax 0x804837a <main+26>: sub %eax,%esp 0x804837c <main+28>: movl $0xaaaaaaaa,0xfffffffc(%ebp) <-- %ebp - 4(最初のローカル変数) に 0xaaaaaaaa を入れる 0x8048383 <main+35>: mov 0xfffffffc(%ebp),%eax <-- %eax = 0xaaaaaaaa 0x8048386 <main+38>: movl $0x5,(%eax) 0x804838c <main+44>: mov 0xfffffffc(%ebp),%eax <-- 再び %ebp - 4 を %eaxに入れて 0x804838f <main+47>: mov (%eax),%eax <-- dereference? 0x8048391 <main+49>: mov %eax,0x4(%esp) 0x8048395 <main+53>: movl $0x8048498,(%esp) 0x804839c <main+60>: call 0x80482b0 <printf@plt> 0x80483a1 <main+65>: mov $0x0,%eax 0x80483a6 <main+70>: leave
命令長がバラバラで気持ち悪い・・・(←RISC系の人)。命令もよく分からないし・・・何なんだ。
で、スタックはどうやってみるのかな。
http://www.unixwiz.net/techtips/win32-callconv-asm.html によると
%ESPはStack Pointer。
%EBPはBase Pointer(Frame Pointer)らしい。関数の引数とローカル変数を参照するときに使うらしい。へえ。
最初のローカル変数は、%EBP - 4にあるらしい。
(gdb) x/32x 0xbfa17710 0xbfa17710: 0xb7f04adc 0xbfa177cc 0xbfa17738 0x080483c3 ^^^^^^^^^^ esp 0xbfa17720: 0x00000001 0xb7f04adc 0x080494a0 0xb7f04adc 0xbfa17730: 0x00000000 0xaaaaaaaa 0xbfa17798 0xb7deeea2 ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^- local var1 ebp old EIP 0xbfa17740: 0x00000001 0xbfa177c4 0xbfa177cc 0xb7f1bbf6 ^^^^^^^^^^ ^^^^^^^^^^ param #1 param #2 0xbfa17750: 0xb7f04adc 0x00000000 0xbfa17750 0xbfa17798 0xbfa17760: 0xbfa17740 0xb7deee64 0x00000000 0x00000000 0xbfa17770: 0x00000000 0xb7f26778 0x00000001 0x080482c0 0xbfa17780: 0x00000000 0xb7f1bb30 0xb7f1c691 0xb7f26778 (gdb)
確かにそんな感じだ。ついでにパラメーターも見てみる。第一引数はargcだから1*1、第二引数は**argvだから、最初の要素は実行ファイル名になってるはず。
(gdb) x/x 0xbfa177c4 0xbfa177c4: 0xbfa17e98 (gdb) x/s 0xbfa17e98 0xbfa17e98: "./pointer"
なるほど。
ただ、上のURLでは__cdecl conventionsのときのスタックって言ってるから、conventionによってフォーマットが違うのかも。
あと、%ESPは0xbfa17710なんだけど、ここにはレジスターの値が保存されてるらしい。この例だとebx。じゃあスタックをたどるのはどうやってやるんだろう。この辺も調べねば。
あー
わすれてた。nを未初期化に戻して、何が入ってるか確認してみる。
# gdb ./pointer [GNU gdb 6.4-debian Copyright 2005 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) x/20i main 0x8048360 <main>: push %ebp 0x8048361 <main+1>: mov %esp,%ebp 0x8048363 <main+3>: sub $0x18,%esp 0x8048366 <main+6>: and $0xfffffff0,%esp 0x8048369 <main+9>: mov $0x0,%eax 0x804836e <main+14>: add $0xf,%eax 0x8048371 <main+17>: add $0xf,%eax 0x8048374 <main+20>: shr $0x4,%eax 0x8048377 <main+23>: shl $0x4,%eax 0x804837a <main+26>: sub %eax,%esp 0x804837c <main+28>: mov 0xfffffffc(%ebp),%eax 0x804837f <main+31>: movl $0x5,(%eax) <--- ココで止める 0x8048385 <main+37>: mov 0xfffffffc(%ebp),%eax 0x8048388 <main+40>: mov (%eax),%eax 0x804838a <main+42>: mov %eax,0x4(%esp) 0x804838e <main+46>: movl $0x8048494,(%esp) 0x8048395 <main+53>: call 0x80482b0 <printf@plt> 0x804839a <main+58>: mov $0x0,%eax 0x804839f <main+63>: leave 0x80483a0 <main+64>: ret
前回Segmentation Faultが起こったmovl $0x5,(%eax)のとこで止めたいので、今回のケースでは0x804837f
(gdb) break *0x804837f Breakpoint 1 at 0x804837f (gdb) run Starting program: /root/work/pointer Failed to read a valid object file image from memory. Breakpoint 1, 0x0804837f in main ()
止まりました
(gdb) info register eax 0xb7f44be0 -1208726560 ecx 0xbf8e167c -1081207172 edx 0x1 1 ebx 0xb7f22adc -1208866084 esp 0xbf8e15c0 0xbf8e15c0 ebp 0xbf8e15e8 0xbf8e15e8 esi 0xbf8e1674 -1081207180 edi 0xbf8e1600 -1081207296 eip 0x804837f 0x804837f <main+31> eflags 0x286 646 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) x/32x 0xbf8e15c0 0xbf8e15c0: 0xb7f22adc 0xbf8e167c 0xbf8e15e8 0x080483bf 0xbf8e15d0: 0x00000001 0xb7f22adc 0x0804949c 0xb7f22adc 0xbf8e15e0: 0x00000000 0xb7f44be0 0xbf8e1648 0xb7e0cea2 ^^^^^^^^^^ 0xbf8e15f0: 0x00000001 0xbf8e1674 0xbf8e167c 0xb7f39bf6 0xbf8e1600: 0xb7f22adc 0x00000000 0xbf8e1600 0xbf8e1648 0xbf8e1610: 0xbf8e15f0 0xb7e0ce64 0x00000000 0x00000000 0xbf8e1620: 0x00000000 0xb7f44778 0x00000001 0x080482c0 0xbf8e1630: 0x00000000 0xb7f39b30 0xb7f3a691 0xb7f44778 (gdb) x/x 0xb7f44be0 0xb7f44be0 <_rtld_global_ro>: 0x00000000
_rtld_global_roってのはぐぐってもよく分からなかった。
*1:voidなので