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時間ぐらいはかかりましたかねぇ。時間がたっぷり有る時にやりましょう。