Binary Hacks拾い読み
寒くなってきた。朝の最低気温が5度ぐらいになる事がある。吐く息が 真っ白。
家では室内用の草履を履いていたんだけど、つま先が寒くなってきたので靴下を履きだした。 あと10日もすると、あぐらをかいてソファーに座るようになるのかな。それとも室内用の防寒 ブーツか。そして炬燵の出番だ。
散歩の時はそろそろ手袋が必要だな。外に飛び出すまでに決断が必要なのよ。早朝散歩はもう止めるかな。 陽が登って暖かくなってからでもいいわいな。暖かくなったら動き出すって、おいら変温動物だな。
昼間は暖かくて、ついつい昼寝なんかをしちゃう。寝ぼけ眼で本棚をみたら、昔読んだ本の 背表紙が飛び込んできた。
Binary Hacksって本。オライリーから出てたやつ。翻訳本じゃなくて、日本人のその手の人達に よる書き下ろし。
拾い読みを始めた。購入した時に通読したはずなんだけど、内容は頭の中から蒸発してた。 改めて見ると、この所いろいろやってたのと、 良く重なる部分がある。面白そうな所を我流で応用してみよう。
catchsegv
以前たまたま見つけた、エラーが発生した時、状況を報告してくれるった言うLinux限定機能が しっかりHackとして載ってた。
#include <stdio.h> foo(a){return a / 0;} main(){ printf("%d\n", foo(5)); }
こんな、泣く子も黙るゼロでの割り算。
sakae@slck:~/t$ ./a.out Floating point exception
勿論エラーである。けど、詳しい事は分からない。そこで、おまじないを先にしておくと
sakae@slck:~/t$ export LD_PRELOAD=/lib/libSegFault.so sakae@slck:~/t$ export SEGFAULT_SIGNALS=all
sakae@slck:~/t$ ./a.out *** Floating point exception Register dump: EAX: 00000005 EBX: b7727ff4 ECX: bfc44184 EDX: 00000000 ESI: 00000000 EDI: 00000000 EBP: bfc440c8 ESP: bfc440c4 EIP: 0804842d EFLAGS: 00010282 CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b Trap: 00000000 Error: 00000000 OldMask: 00000000 ESP/signal: bfc440c4 CR2: 00000000 Backtrace: ./a.out[0x804842d] ./a.out[0x8048447] /lib/libc.so.6(__libc_start_main+0xf5)[0xb75c05a5] ./a.out[0x8048311] Memory map: 08048000-08049000 r-xp 00000000 08:01 163674 /home/sakae/t/a.out :
Windowsのブルースクリーンより、詳細な説明が出てくる。この一点でWindowsよりは親切設計って 認めてあげましょう。
いつもSEGVでげろしてる人達向けに特効薬も用意されてるよ。 上記のおまじないをスクリプト化したやつがあるんだ。catchsegv ./a.outって やると、その時だけ親切にしてくれます。これ、便利だよー。(って、そんなに何時もゲロしてんの?)
pidof
こんな英単語もどきがあると、次に続くのは何か分かっちゃうよね。そう、後ろには、探したい プログラム名を指定するんだ。そうすると、pidが帰ってくる。
sakae@slck:~$ /sbin/pidof bash 2260 2152 2091 2076 sakae@slck:~$ /sbin/pidof screen 2089
bashが4つも起動してて何やってんだか? それはそうと、pidofってrootさんの道具なの? (Slackwareの場合だけど) 訝しげに調べてみると、pidofってのは仮の名前で、本名は、killall5ってやつ。
NAME killall5 -- send a signal to all processes.
なんで、こんな怖いコマンドと一緒くたに(シンボリックリンクだけど)するんかね? Linuxな 人達には憑いていけんな。
libtool
以前、libtoolが使われていた、vmware-toolbox-cmdをgdbから追おうとして手こずった。
[sakae@arch toolbox]$ gdb -q vmware-toolbox-cmd "/home/sakae/open-vm-tools-9.2.0-799703/toolbox/vmware-toolbox-cmd": not in executable format: File format not recognized
ラッパーが被っていてgdbに怒られたのだ。そんな時は、libtoolの配下から起動すればよい。
[sakae@arch toolbox]$ libtool --mode=execute gdb -q vmware-toolbox-cmd Reading symbols from /home/sakae/open-vm-tools-9.2.0-799703/toolbox/.libs/lt-vmware-toolbox-cmd...done. (gdb) set args help disk (gdb) run Starting program: /home/sakae/open-vm-tools-9.2.0-799703/toolbox/.libs/lt-vmware-toolbox-cmd help disk warning: Could not load shared library symbols for linux-gate.so.1. Do you need "set solib-search-path" or "set sysroot"? [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Program received signal SIGILL, Illegal instruction. 0xb7f97455 in Hostinfo_TouchXen () at hostinfoHV.c:135 135 __asm__ __volatile__( (gdb) c Continuing. disk: perform disk shrink operations Usage: /home/sakae/open-vm-tools-9.2.0-799703/toolbox/.libs/lt-vmware-toolbox-cmd disk <subcommand> [args] : [Inferior 1 (process 9934) exited normally]
これは、libtoolが(まだインストールしていない)ライブラリィーの位置をRPATHに自動的に セットした状態で、gdbを起動してくれるのだ。
BPをプログラムの中で指定する
普通BPをセットするには、gdbを起動した後に行う。(.gdbinitrcで設定するって方法も考えられなく はないけど)しかし、場合によっては、ターゲットプログラムの中へBPを埋め込んでおきたい 事もある。BPを埋め込むには、i386限定なら一命令で出来る。
} else { __asm__ __volatile__("int3"); retval = cmd->func(argv, argc, gQuiet); }
汎用的にやるなら
#include <signal.h> raise(SIGTRAP);
gdbのBP機能は、sigtrapが使われているからだ。
vmware-toolbox-cmd.cのコマンドディスパッチの所にBP埋めておいて動かすと、 トラップがかかり、gdbに制御が渡ってくる。
: (gdb) c Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. main (argc=4, argv=0x8051250) at toolbox-cmd.c:504 504 retval = cmd->func(argv, argc, gQuiet); (gdb) l 499 } else if (cmd->requireArguments && ++optind >= argc) { 500 ToolsCmd_MissingEntityError(argv[0], SU_(arg.subcommand, "subcommand")); 501 retval = EX_USAGE; 502 } else { 503 __asm__ __volatile__("int3"); 504 retval = cmd->func(argv, argc, gQuiet); 505 } 506
上記は勿論、gdbからアプリを起動した場合だ。よって、それを知らない普通の人を煙に巻くことは 簡単だ。
[root@arch .libs]# ./lt-vmware-toolbox-cmd help disk Trace/breakpoint trap [root@arch .libs]#
あのー、何か分からないんですが、プログラムが旨く実行されないんですけどーーー。 strace取って、報告するよって人には、どんな風に見えるかな?
--- SIGTRAP {si_signo=SIGTRAP, si_code=SI_KERNEL} --- +++ killed by SIGTRAP +++ Trace/breakpoint trap
ついでに、ltraceした時のも
: strcmp("device", "help") = -1 strcmp("help", "help") = 0 unexpected breakpoint at 0x8049bcc strcmp("timesync", "disk") = 1 strcmp("script", "disk") = 1 strcmp("disk", "disk") = 0 VMTools_GetString(0x804cdf1, 0x804d308, 0x87e9498, 0xb751b220, 0x804c22b) = 0x804d31a g_print(0x804d31a, 0x804c23b, 0x87e9478, 0x804c23b, 0x804c22bdisk: perform disk shrink operations Usage: ./lt-vmware-toolbox-cmd disk <subcommand> [args] Subcommands: list: list available locations shrink <location>: wipes and shrinks a file system at the given location shrinkonly: shrinks all disks wipe <location>: wipes a file system at the given location ) = 321 g_key_file_free(0x87ea800, 3, 0, 0x8050120, 0xbfbdc628) = 0 +++ exited (status 0) +++
さらっと、そんなBPは期待してねーよと言いつつ、実行されちゃってますなあ。これが、裏で動いて いるptraceの真の力?です。
無視したBPのアドレスが出てたので、gdbでtrapした時のアドレス周辺を眺めてみました。
0x08049bbd <+839>: call 0x804953e <ToolsCmd_MissingEntityError> 0x08049bc2 <+844>: movl $0x40,0x34(%esp) 0x08049bca <+852>: jmp 0x8049bf1 <main+891> 0x08049bcc <+854>: int3 => 0x08049bcd <+855>: mov 0x38(%esp),%eax 0x08049bd1 <+859>: mov 0x4(%eax),%eax 0x08049bd4 <+862>: mov 0x805018c,%ecx 0x08049bda <+868>: mov 0xc(%ebp),%edx
相変わらず、分からん石だ事。
Programing Env Tips
Binary Hacksとは直接関係ないけど、たまたま見てた所で C言語のLinux環境関連tips が手際よくまとまっていたので、メモしとく。
昔は、この手の本を何冊か持ってたけど、引越しの時に全部整理しちゃったから、全体を 俯瞰するにはうってつけだったりします。
freebsd-update
もー幾つ寝るとFreeBSDが新しくなるの? 10月末らしいですけど。 ちょいとフライングして、RC版をバイナリー・アップデートしてみる。実はこれやるの 初めてだったりします。(今までは、クリーンインストールのみ)
お約束で、Home-dirをtarで固めて、Slackwareへ退避しておきます。duで見たら2Gあったけど 、固めたら500Mになった。ここでも、4倍則が成立してるな。何もなければ戻す事も 無いんで、まあ保険だな。
secd# freebsd-update upgrade -r 9.1-RC2 Looking up update.FreeBSD.org mirrors... 3 mirrors found. Fetching metadata signature for 9.0-RELEASE from update4.FreeBSD.org... done. Fetching metadata index... done. Fetching 1 metadata files... done. Inspecting system... done. The following components of FreeBSD seem to be installed: kernel/generic src/src world/base world/doc world/games The following components of FreeBSD do not seem to be installed: Does this look reasonable (y/n)? y Fetching metadata signature for 9.1-RC2 from update4.FreeBSD.org... done. Fetching metadata index... done. Fetching 1 metadata patches. done. Applying metadata patches... done. Fetching 1 metadata files... done. Inspecting system... done. Fetching files from 9.0-RELEASE for merging... done. Preparing to download files... Fetching 37470 patches.....10 : ..35790....35800....35810....35820....35830....35840.. done. Applying patches... done. Fetching 3022 files... The following files will be updated as part of updating to : ;;; show many file's by more /var/named/etc/namedb/named.root /var/yp/Makefile.dist To install the downloaded upgrades, run "/usr/sbin/freebsd-update install".
ここまでが長いんだ。何せ、そっくりそのまま入れ替える雰囲気だもの。
Netからpatchを落としている間、freebsd-updateがどういう風になってるか、しげしげと 眺めてしまいましたよ。それは、長い長いshellのスクリプトでした。3246行もあるよ。
# Provide some default parameters default_params () { # Save any parameters already configured, and clear the slate saveconfig nullconfig # Default configurations config_WorkDir /var/db/freebsd-update : }
こんなのが見つかったので、ネットから落としてきたやつが何処に保存されてるか判明。 サイズを調べてみたら、750Mも有った。何これ、CD1枚じゃ効かないじゃん。クリーン インストールの方が楽か。と、思ったけど、この溜め込んだデータを使ってロールバック (無かった事にする)が出来るのね。これって、Macで言うタイムマシン? まあ、RC2からRELEASEへの変更は軽微なものである事を願うのみ。
# Generate a random seed for use in picking mirrors. If HTTP_PROXY # is set, this will be used to generate the seed; otherwise, the seed # will be random. if [ -n "${HTTP_PROXY}${http_proxy}" ]; then RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" | tr -d 'a-f' | cut -c 1-9` else RANDVALUE=`jot -r 1 0 999999999` fi }
こんな面白いコードを見つけた。人様のコードは読んでみるものだ。とか言ってる間にDLは終了したんで、 いよいよインストール(と言うかアップデート)。一回目はカーネル。
secd# freebsd-update install Installing updates... Kernel updates have been installed. Please reboot and run "/usr/sbin/freebsd-update install" again to finish installing updates. secd# shutdown -r
新しいカーネルになったら、今度は、ユーザーランドのアップデート
secd# freebsd-update install Installing updates...
難だかんだで、2.5時間ぐらいはかかりましたかねぇ。時間がたっぷり有る時にやりましょう。